关于 spring:SFTP : BeanPostProcessor 干扰 @ServiceActivator 和 @MessagingGateway

SFTP : BeanPostProcessor interfere with @ServiceActivator and @MessagingGateway

似乎 BeanPostProcessor 接口实现对@ServiceActivator 产生了影响。将 BeanPostProcessor 与 @ServiceActivator 一起使用的方式应该是什么。谢谢。

这里有完整的日志logs

以下是用于 SFTP 的 Java Config -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package com.ftp.example;

import java.io.File;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.integration.annotation.Gateway;
import org.springframework.integration.annotation.IntegrationComponentScan;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.file.FileNameGenerator;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.sftp.outbound.SftpMessageHandler;
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

import com.jcraft.jsch.ChannelSftp.LsEntry;

@Configuration
@EnableScheduling
@EnableAspectJAutoProxy
@EnableAsync
@IntegrationComponentScan
@EnableIntegration
@EnableBatchProcessing
@PropertySource("file:C:\\\\DEV\\\\workspace_oxygen\\\\ftp-example\\\\ftp-example.properties")
public class DependencySpringConfiguration {

    private Logger LOG = LoggerFactory.getLogger(DependencySpringConfiguration.class);

    @Value("${project.name}")
    private String applicationName;


    @Value("${${project.name}.ftp.server}")
    private String server;

    @Value("${${project.name}.ftp.port}")
    int port;

    @Value("${${project.name}.ftp.username}")
    private String username;

    @Value("${${project.name}.ftp.password}")
    private String password;

    @Value("${${project.name}.ftp.remote.directory}")
    private String remoteDirectory;

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean
    public ProcessStarter processStarter() {
        return new ProcessStarter();
    }

/*  @Bean
    public LogInjector logInjector() {
        return new LogInjector();
    }*/


    @Bean
    public FTPOutService fTPOutService() {
        return new FTPOutService();
    }


    @Bean
    public SessionFactory<LsEntry> sftpSessionFactory() {
        DefaultSftpSessionFactory sf = new DefaultSftpSessionFactory();
        sf.setHost(server);
        sf.setPort(port);
        sf.setUser(username);
        sf.setPassword(password);
        sf.setAllowUnknownKeys(true);
        return new CachingSessionFactory<LsEntry>(sf);
    }

    @Bean
    @ServiceActivator(inputChannel ="toSftpChannel")
    public MessageHandler handler() {
        SftpMessageHandler handler = new SftpMessageHandler(sftpSessionFactory());
        handler.setRemoteDirectoryExpression(new LiteralExpression(remoteDirectory));
        handler.setFileNameGenerator(new FileNameGenerator() {

            @Override
            public String generateFileName(Message< ? > message) {
                return"fileNameToBeFtp.txt";
            }

        });
        return handler;
    }

    @MessagingGateway
    public interface MyGateway {
        @Gateway(requestChannel ="toSftpChannel")
        void sendToSftp(File file);
    }


}

我们在做 SFTP 时像这样调用网关对象

主类

1
2
3
4
5
6
7
8
9
10
11
12
13
public class FtpExample {

    public static String[] ARGS;
    private static final Logger LOG = LoggerFactory.getLogger(FtpExample.class);

    public static void main(String[] args) throws Exception {
        ARGS = args;
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(DependencySpringConfiguration.class);

        ProcessStarter processStarter = ctx.getBean(ProcessStarter.class);
        processStarter.startService();
    }
}

其他类 -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class ProcessStarter {
    @Inject
    private FTPOutService ftpOutService;

    public void startService() {
        ftpOutService.ftpToBbg();
    }
}



public class FTPOutService {
    private static Logger log = LoggerFactory.getLogger(FTPOutService.class);

    @Inject
    private ApplicationContext appContext;

    public void ftpToBbg() {

        log.info("Starting FTP out process...");
        File file = null;
        try {
            file = new File("C:\\\\Temp\\\\log\\\\debug\\\\ftp\\\\priceindex\\\\for-upload\\\\ftp-example.txt.REQ");
            MyGateway gateway = appContext.getBean(MyGateway.class);
            gateway.sendToSftp(file);
            log.info("File {} written successfully on remote server", file);
        } catch (Exception e) {
            log.error("Error while uploading file {}", file, e);
        }
    }

}

除非我没有在上面定义的 Java Config -

中添加以下 bean 声明,否则上面的代码工作正常

1
2
3
public LogInjector logInjector() {
            return new LogInjector();
}

上面的 bean 定义有以下实现 -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class LogInjector implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
        ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                // make the field accessible if defined private
                ReflectionUtils.makeAccessible(field);
                if (field.getAnnotation(Log.class) != null) {
                    if (org.slf4j.Logger.class == field.getType()) {
                        org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(bean.getClass());
                        field.set(bean, log);
                    }  else if (java.util.logging.Logger.class == field.getType()) {
                        java.util.logging.Logger log = java.util.logging.Logger.getLogger(bean.getClass().toString());
                        field.set(bean, log);
                    }
                }
            }
        });
        return bean;
    }
}


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface Log {
}

一旦在 Java Config 中添加任何 BeanPostProcessor 实现,它就会产生问题并且应用程序无法看到 toSftpChannel -

org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'toSftpChannel' available at
org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:685)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1199)
at
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at
org.springframework.integration.support.channel.BeanFactoryChannelResolver.resolveDestination(BeanFactoryChannelResolver.java:88)
at
org.springframework.integration.support.channel.BeanFactoryChannelResolver.resolveDestination(BeanFactoryChannelResolver.java:45)
at
org.springframework.integration.gateway.MessagingGatewaySupport.getRequestChannel(MessagingGatewaySupport.java:327)
at
org.springframework.integration.gateway.MessagingGatewaySupport.send(MessagingGatewaySupport.java:368)
at
org.springframework.integration.gateway.GatewayProxyFactoryBean.invokeGatewayMethod(GatewayProxyFactoryBean.java:477)
at
org.springframework.integration.gateway.GatewayProxyFactoryBean.doInvoke(GatewayProxyFactoryBean.java:429)
at
org.springframework.integration.gateway.GatewayProxyFactoryBean.invoke(GatewayProxyFactoryBean.java:420)
at
org.springframework.integration.gateway.GatewayCompletableFutureProxyFactoryBean.invoke(GatewayCompletableFutureProxyFactoryBean.java:65)
at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy57.sendToSftp(Unknown Source)


看看你有什么:

1
2
3
4
@Bean
public LogInjector logInjector() {
    return new LogInjector();
}

如果您将 BeanPostProcessors 声明为 @Bean,则必须使用 static 修饰符指定它们:https://docs.spring.io/spring/docs/5.0.0.RELEASE/spring-framework-参考/core.html#beans-factorybeans-annotations

You may declare @Bean methods as static, allowing for them to be called without creating their containing configuration class as an instance. This makes particular sense when defining post-processor beans, e.g. of type BeanFactoryPostProcessor or BeanPostProcessor, since such beans will get initialized early in the container lifecycle and should avoid triggering other parts of the configuration at that point.