[英]how to configure spring beans with null return in context xml file
[英]Spring configure method references in beans xml
我的配置 class 中有一个 map,它看起来像下面的代码(尽管我的实际问题涉及一组不同的类):
private Map<Class, Function<String, ?>> someParser = ImmutableMap.of(
Short.class, Short::parseShort, Integer.class, Integer::parseInt,
Double.class, Double::parseDouble);
有没有办法在 XML 文件中配置它? 就像在 XML 文件中将方法引用视为 bean 一样? 由于下面的代码显然不起作用:
<util:map id="someParser" key-type="java.lang.Class">
<entry key="java.lang.Short" value-ref="Short::parseShort" />
<entry key="java.lang.Integer" value-ref="Integer::parseInteger" />
<entry key="java.lang.Double" value-ref="Double::parseDouble" />
</util:map>
简短的回答是肯定的,但我需要一个方法参考工厂 class 来帮助我生成这些 bean。 这绝对是太冗长了,但它的工作原理。 我最终得到了这样的结果:
<bean id="methodFactory" class="com.foo.bar.util.MethodRefFactory" />
<util:map id="someParser" key-type="java.lang.Class">
<entry key="java.lang.Integer" value-ref="parseInteger" />
<entry key="java.lang.Double" value-ref="parseDouble" />
</util:map>
<bean id="parseInteger" factory-bean="methodFactory" factory-method="getMethodRef">
<constructor-arg value="java.lang.Integer::parseInt" />
<constructor-arg value="java.util.function.Function" type="java.lang.Class" />
<constructor-arg><null /></constructor-arg>
</bean>
<bean id="parseDouble" factory-bean="methodFactory" factory-method="getMethodRef">
<constructor-arg value="java.lang.Double::parseDouble" />
<constructor-arg value="java.util.function.Function" type="java.lang.Class" />
<constructor-arg><null /></constructor-arg>
</bean>
工厂 class 应该只接受方法引用语法<class|instance>::<method>
作为字符串,拆分它并将方法引用作为 Function 返回。 But I wanted a generic factory class that can handle both static and non-static method references (eg. java.class.Integer::parseInt
and fileValidator::validate
), so here's the factory class that I came up with. 我希望你发现这个 class 也很有用:
public class MethodRefFactory {
private ApplicationContext context;
public MethodRefFactory(ApplicationContext context) {
this.context = context;
}
/** Use this method to create non-static method references like fileValidator::validate */
public <T> T getMethodSpring(String signature, Class<T> lambdaFuncType) throws Throwable {
String[] sigTokens = signature.split("::");
String sigClass = sigTokens[0];
Object objToInvoke = null;
try {
objToInvoke = context.getBean(sigClass);
} catch(BeansException ex) {
ex.printStackTrace();
}
return getMethodRef(signature, lambdaFuncType, objToInvoke);
}
/** Use this method to create static method references like java.lang.Integer::parseInt */
public <T> T getMethodRef(String signature, Class<T> lambdaFuncType, Object objToInvoke) throws Throwable {
String[] sigTokens = signature.split("::");
String sigClass = sigTokens[0];
String sigMethod = sigTokens[1];
boolean isStaticMethod = Objects.isNull(objToInvoke);
Class realType = isStaticMethod ? Class.forName(sigClass) : objToInvoke.getClass();
Method realMethod = getRealMethod(realType, sigMethod);
MethodHandles.Lookup caller = MethodHandles.lookup();
MethodHandle realMethodHandle = caller.unreflect(realMethod);
MethodType realMethodHandleType = realMethodHandle.type();
MethodType lambdaFuncMethodType = isStaticMethod ? realMethodHandleType.generic() :
generateLambdaFuncMethodType(lambdaFuncType);
MethodType targetMethodType = isStaticMethod ? realMethodHandleType :
extractMatchingMethodTypeForRealMethod(realMethodHandleType);
String lambdaFuncMethodName = lambdaFuncType.getMethods()[0].getName();
MethodType lambdaFuncAndRealType = isStaticMethod ? MethodType.methodType(lambdaFuncType) :
MethodType.methodType(lambdaFuncType, realType);
CallSite site = LambdaMetafactory.metafactory(caller, lambdaFuncMethodName, lambdaFuncAndRealType,
lambdaFuncMethodType, realMethodHandle, targetMethodType);
MethodHandle factory = site.getTarget();
if (!isStaticMethod) {
factory = factory.bindTo(objToInvoke);
}
return (T) factory.invoke();
}
private Method getRealMethod(Class type, String methodName) {
return Arrays.stream(type.getMethods()).filter(m -> m.getName().equals(methodName))
.sorted(this::compareMethods).findFirst().get();
}
private MethodType extractMatchingMethodTypeForRealMethod(MethodType target) {
return MethodType.methodType(target.returnType(), Arrays.copyOfRange(target.parameterArray(),1, target.parameterCount()));
}
private <T> MethodType generateLambdaFuncMethodType(Class<T> funcType) {
Method method = funcType.getMethods()[0];
if (method.getParameterCount() == 0) {
return MethodType.methodType(Object.class);
}
Class[] params = Arrays.copyOfRange(method.getParameterTypes(), 1, method.getParameterCount());
return MethodType.methodType(method.getReturnType(), method.getParameterTypes()[0], params);
}
private int compareMethods(Method m1, Method m2) {
return m1.getName().equals(m2.getName()) ? Integer.compare(m1.getParameterCount(), m2.getParameterCount()) :
m1.getName().compareTo(m2.getName());
}
}
这是 SimpleBean class 和 MyInterface 接口:
interface MyInterface<R> {
R process(Object in);
}
class SimpleBean {
public String simpleFunction(String in) {
return "java.util.function.Function test: " + in;
}
public String simpleBiFunction(Double in, Integer y) {
return "java.util.function.BiFunction test: " + (in + y);
}
public String simpleIntFunction(Integer y) {
return "java.util.function.IntFunction test: " + y;
}
public String simpleSupplier() {
return "java.util.function.Supplier test ";
}
public void simpleConsumer(String param) {
System.out.println("java.util.function.Consumer test: " + param);
}
}
这是我的测试用例。 请注意,它支持Function
、 BiFunction
、 IntFunction
、 Supplier
、 Consumer
,甚至是自定义功能接口:
public static void main(String[] args) throws Throwable {
MethodRefFactory factory = new MethodRefFactory(null);
Function f = factory.getMethodRef("java.lang.Short::parseShort", Function.class, null);
System.out.println(f.apply("2343"));
SimpleBean bean = new SimpleBean();
Function f2 = factory.getMethodRef("com.foo.bar.util.SimpleBean::simpleFunction", Function.class, bean);
System.out.println(f2.apply("foo"));
BiFunction f3 = factory.getMethodRef("com.foo.bar.util.SimpleBean::simpleBiFunction", BiFunction.class, bean);
System.out.println(f3.apply(25.0, 4));
Supplier f4 = factory.getMethodRef("com.foo.bar.util.SimpleBean::simpleSupplier", Supplier.class, bean);
System.out.println(f4.get());
Consumer f5 = factory.getMethodRef("com.foo.bar.util.SimpleBean::simpleConsumer", Consumer.class, bean);
f5.accept("bar");
IntFunction f6 = factory.getMethodRef("com.foo.bar.util.SimpleBean::simpleIntFunction", IntFunction.class, bean);
System.out.println(f6.apply(1234));
MyInterface f7 = factory.getMethodRef("com.foo.bar.util.SimpleBean::simpleFunction", MyInterface.class, bean);
System.out.println(f7.process("myInterface param"));
}
这是用于非静态方法参考的最后一个示例 XML:
<util:map id="executorMap" key-type="java.lang.Class">
<entry key="com.foo.bar.action.Read" value-ref="readerReadMsg" />
<entry key="com.foo.bar.action.Validate" value-ref="validatorValidateMsg" />
<entry key="com.foo.bar.action.Transform" value-ref="transformerTransformMsg" />
<entry key="com.foo.bar.action.Persist" value-ref="persisterPersistMsg" />
</util:map>
<bean id="readerReadMsg" factory-bean="methodFactory" factory-method="getMethodSpring">
<constructor-arg value="reader::readMsg" />
<constructor-arg value="java.util.function.BiFunction" type="java.lang.Class" />
</bean>
<bean id="validatorValidateMsg" factory-bean="methodFactory" factory-method="getMethodSpring">
<constructor-arg value="validator::validateMsg" />
<constructor-arg value="java.util.function.BiFunction" type="java.lang.Class" />
</bean>
<bean id="transformerTransformMsg" factory-bean="methodFactory" factory-method="getMethodSpring">
<constructor-arg value="transformer::transformMsg" />
<constructor-arg value="java.util.function.BiFunction" type="java.lang.Class" />
</bean>
<bean id="persisterPersistMsg" factory-bean="methodFactory" factory-method="getMethodSpring">
<constructor-arg value="persister::persistMsg" />
<constructor-arg value="java.util.function.BiFunction" type="java.lang.Class" />
</bean>
以下链接极大地帮助我理解了LambdaMetafactory.metafactory
工作原理以及它如何使我能够实现我想要的: this 、 this和this 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.