繁体   English   中英

Spring 配置 bean 中的方法引用 xml

[英]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);
    }
}

这是我的测试用例。 请注意,它支持FunctionBiFunctionIntFunctionSupplierConsumer ,甚至是自定义功能接口:

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工作原理以及它如何使我能够实现我想要的: thisthisthis

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM