Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a Java8 Lambda Function inside spring XML

Let's say I have this class:

public class FooToBarTransformer {
    public Bar transform(Foo foo) {
        // do some cool stuff
    }
}

And I want to consume it as a Function in some other class:

public class Thing {
    public Thing(Function<Foo, Bar> f) {
        this.converter = f;
    }
}

Now, if I'm instantiating a Thing through Java, I'd do it with Java8 Lambdas like this:

FooToBarTransformer transformer = new FooToBarTransformer();
new Thing((foo) -> transformer.transform(foo));
// or new Thing(transformer::transform);

My problem, is that I need to create a Thing though spring.

<bean id="fooToBarTransformer" class="com.mypackage.FooToBarTransformer"/>
<bean id="theThing" class="com.mypackage.Thing">
    <constructor-arg index="0" ????????? />
</bean>

Now, there are a few possible workarounds I've considered to make this easier:

  1. if FooToBarTransformer implemented Function, then it would just be a simple ref="fooToBarTransformer"
  2. I could create a different interface that FooToBarTransformer implements and change Thing to take an instance of that interface instead of Function. For the purpose of this question, neither of those are options.

Based on some other ways I've seen of doing executions in the spring xml, I've tried value="#{(foo) -> fooToBarTransformer.transform(foo)}" and value="#{fooToBarTransformer::transform}" but spring choked on this.

The best option I've come up with so far is to provide a translation function in code:

public Function<Foo, Bar> toFunction() {
    return transformer::transform;
}

and reference it in spring with value="#{fooToBarTransformer.toFunction()}", but this seems rather hokey.

Is there a better way to do this?

like image 291
Krease Avatar asked Jun 26 '15 21:06

Krease


1 Answers

I think what you need to do is to switch to Java Config (http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-java) In the meantime, if you can't switch, you can still pull it off by combining methods. In you're XML configuration file add:

<bean class="com.myapp.config.FooBarConfiguration"/>

Then, you create class com.myapp.config.FooBarConfiguration like this:

@Configuration
public class FooBarConfiguration {
    @Bean
    public FooToBarTransformer fooTransformer() {
        return new FooToBarTransformer();
    }

    @Bean
    public Thing theThing(FooToBarTransformer fooTransformer) {
        return new Thing(fooTransformer::transform);
    }
}

Just make sure that ConfigurationClassPostProcessor is a registered post-processor in your Spring context by either having:

<context:annotation-config>

Or

<context:component-scan>

Or by manually adding the post processor as a bean:

<bean class="org.springframework.context.annotation.ConfigurationClassPostProcessor"/>

This should work from Spring 3.0 and above according with http://docs.spring.io/spring/docs/current/javadoc-api/index.html?org/springframework/context/annotation/ConfigurationClassPostProcessor.html. Not sure what version you're using.

like image 86
Ulises Avatar answered Oct 05 '22 23:10

Ulises