I am trying to migrate a project from a mixed XML/Java configuration to pure Java Config (not yet Java DSL, but annotated @Bean methods). So far, I managed to convert channels, inbound channel adapters, transformers and service activators), but I'm stuck with the conversion of a filter.
The integration.xml
file define the following filter (the Message carries a Java.io.File payload)
<int:filter input-channel="channelA" output-channel="channelB"
ref="integrationConfiguration" method="selector"/>
The selector is defined in the IntegrationConfiguration class (that also holds all other SI-related @Bean methods:
@Configuration
public class IntegrationConfiguration {
// channels
@Bean
public MessageChannel channelA() { return new DirectChannel(); }
@Bean
public MessageChannel channelB() { return new DirectChannel(); }
// some other required channels
// ...
// inbound channel adapters
@Bean
@InboundChannelAdapter(channel = "channelA")
public MessageSource<File> fileReadingMessageSource() {
var source = new FileReadingMessageSource();
// source configuration (not relevant here)
return source;
}
// ...
// filter on Message<File>
public boolean selector(@Header("file_name") String name,
@Header("file_relativePath") String relativePath) {
// do stuff with name and relativePath and return true or false
return true;
}
// transformers
@Bean
@Transformer(inputChannel = "channelB", outputChannel = "channelC")
public HeaderEnricher enrichHeaders() {
var expression = new SpelExpressionParser().parseExpression("...");
var headers = Map.of("additional_header",
new ExpressionEvaluatingHeaderValueMessageProcessor<>(expression, String.class));
return new HeaderEnricher(headers);
}
// ...
// service activators
@Bean
@ServiceActivator(inputChannel = "channelC")
public FileWritingMessageHandler fileWritingMessageHandler() {
var handler = new FileWritingMessageHandler(
new SpelExpressionParser().parseExpression("headers.additional_header")
);
// handler configuration (not relevant here)
return handler;
}
// ...
}
I tried to replace the XML-defined bean with:
@Bean
@Filter(inputChannel = "channelA", outputChannel = "channelB")
public boolean filter() {
// get the "file_name" and "file_relativePath" headers
var expression1 = new SpelExpressionParser().parseExpression("headers.file_name");
var name = expression1.getValue(String.class);
var expression2 = new SpelExpressionParser().parseExpression("headers.file_relativePath");
String relativePath = expression2.getValue(String.class);
// do stuff with name and relativePath and return true or false
return true;
}
When I run the code, it gives me a BeanCreationException
:
Error creating bean with name 'filter' defined in class path resource [.../IntegrationConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [boolean]: Factory method 'filter' threw exception; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'headers' cannot be found on null
What did I do wrong?
UPDATE after Artem's answer and insightful comments:
Using @Bean isn't necessary for a POJO method, keep it for
out-of-the-box type: MessageHandler, Transformer, MessageSelector etc
In this case, one can use a (not an out-of-the-box) @Bean MessageSelector, but it is actually more lines of code for the same result:
@Bean
@Filter(inputChannel = "channelA", outputChannel = "channelB")
public MessageSelector messageSelector() {
return new MessageSelector(){
@Override
public boolean accept(Message<?>message){
var headers = message.getHeaders();
var name = headers.get("file_name", String.class);
var relativePath = headers.get("file_relativePath", String.class);
return selector(name, relativePath);
}
};
}