java - bean xml中的Spring配置方法引用
问题描述
我的配置类中有一个类似于下面代码的地图(尽管我的实际问题涉及一组不同的类):
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>
解决方案
简短的回答是肯定的,但我需要一个方法引用工厂类来帮助我生成这些 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|instance>::<method>
作为字符串,拆分它并将方法引用作为函数返回。但是我想要一个可以处理静态和非静态方法引用(例如java.class.Integer::parseInt
和fileValidator::validate
)的通用工厂类,所以这是我想出的工厂类。我希望你发现这个类也很有用:
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 类和 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。
推荐阅读
- sql - 是否有一个 SQL 函数可以在 Date 列中填充缺失的日期并用以前的值填充 B 列中的相同记录?(PostgresSQL)
- html - 如何以角度更改列表框中第一个元素的颜色?
- c# - 为什么在 xaml 中绑定 ItemsSource 时 ui 更新但在代码中分配它时不更新?
- facebook - facebook 服务器发送实时视频评论事件,仅提供视频所有者评论
- java - ConstraintValidation 不适用于 Rest Api 请求
- javascript - 在回发时我不想改变表单的视觉效果?
- ios - 为 Pushkit 分配 PKPushRegistryDelegate 时如何消除类型错误?
- php - 如何更改php代码以在每个输入框下显示错误
- javascript - 在 JEST 测试中隐藏控制台日志
- angularjs - 如何在 twilio-video 中添加额外的轨道?