将过滤器从XML迁移到纯Java配置

发布于 2025-02-11 17:46:43 字数 3729 浏览 1 评论 0 原文

我正在尝试将一个项目从混合的XML/Java配置迁移到纯Java Config(尚未Java DSL,而是注释@BEAN方法)。 到目前为止,我设法转换了通道,入站通道适配器,变压器和服务激活器),但我坚持使用过滤器的转换。

Integration.xml 文件定义以下过滤器(该消息带有Java.io.file有效载荷)

<int:filter input-channel="channelA" output-channel="channelB"
            ref="integrationConfiguration" method="selector"/>

选择器在IntegrationConfiguration类中定义(还持有所有其他与SI相关的@BEAN方法:

@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试图用以下方式替换XML定义的bean:

@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;
}

当我运行代码时,它会给我一个 bean> bean creationexception

错误创建class路径资源中定义的名称'filter'的bean [.../integrationconfiguration.class.class.class.class ]:通过工厂方法实例化;嵌套的异常是org.springframework.beans.beaninstantiationException:无法实例化[Boolean]:Factory方法“过滤器' null上找不到

我做错了什么?

属性或字段“标题” Pojo方法不是必需的,请保留

开箱即用类型:MessageHandler,Transformer,MessagsElector等

在这种情况下,可以使用(不是现成的)@Bean MessageElector,但实际上它是相同结果的更多代码行:

@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);
        }
    };
}

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);
        }
    };
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

不回头走下去 2025-02-18 17:46:43

有足够的事情可以这样做:

@Filter(inputChannel = "channelA", outputChannel = "channelB")
public boolean selector(@Header("file_name") String name,
                        @Header("file_relativePath") String relativePath) {

请参阅该 @filter

 * Indicates that a method is capable of playing the role of a Message Filter.
 * <p>
 * A method annotated with @Filter may accept a parameter of type
 * {@link org.springframework.messaging.Message} or of the expected
 * Message payload's type. Any type conversion supported by default or any
 * Converters registered with the "integrationConversionService" bean will be
 * applied to the Message payload if necessary. Header values can also be passed
 * as Message parameters by using the
 * {@link org.springframework.messaging.handler.annotation.Header @Header} parameter annotation.
 * <p>
 * The return type of the annotated method must be a boolean (or Boolean).

@filter @serviceactivator 相似@transformer :您将方法标记并指向频道。该框架创建了一个端点,并将该方法用作处理程序以从频道消耗消息。方法调用的结果分别为端点目的处理程序。如果过滤器,则请求消息将发送到输出频道(或从标头回复频道),如果结果为 true 。否则该消息将被丢弃。

在文档中查看更多信息:

There is just enough to do like this:

@Filter(inputChannel = "channelA", outputChannel = "channelB")
public boolean selector(@Header("file_name") String name,
                        @Header("file_relativePath") String relativePath) {

See docs for that @Filter:

 * Indicates that a method is capable of playing the role of a Message Filter.
 * <p>
 * A method annotated with @Filter may accept a parameter of type
 * {@link org.springframework.messaging.Message} or of the expected
 * Message payload's type. Any type conversion supported by default or any
 * Converters registered with the "integrationConversionService" bean will be
 * applied to the Message payload if necessary. Header values can also be passed
 * as Message parameters by using the
 * {@link org.springframework.messaging.handler.annotation.Header @Header} parameter annotation.
 * <p>
 * The return type of the annotated method must be a boolean (or Boolean).

The @Filter is similar to the @ServiceActivator or @Transformer: you mark the method and point to the channels. The framework creates an endpoint and use that method as a handler to consume messages from the channel. The result of the method call is handler respectively to the endpoint purpose. In case of filter the request message is sent to the output channel (or reply channel from header) if result is true. Otherwise the message is discarded.

See more info in docs: https://docs.spring.io/spring-integration/docs/current/reference/html/configuration.html#annotations

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文