关于 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)

相关讨论

  • 您需要显示完整的堆栈跟踪 - MessagingGatewaySupport.send 似乎您在初始化应用程序上下文之前正在发送消息。
  • 日志在调试模式下有很多行。上传到谷歌驱动器 -drive.google.com/open?id=0ByEWhJ9QdUASNGJmNTVaZjlGd1k
  • >15:01:48.848 [main] INFO com.ftp.example.FTPOutService - Starting FTP out process... 你开始这个过程太早了 - 应用程序上下文还没有建立。您需要展示该课程。在完全构建应用程序上下文之前,您不得启动任何消息传递。
  • 我在代码中添加了 FTPOutService 类。谢谢
  • 你从哪里调用 ftpToBbg()
  • 添加了具有 main 方法的 FtpExample。谢谢
  • 您使用什么版本的 Spring Integration?如果显式声明 toSftpChannelMessageChannel bean,它是如何工作的?
  • 请看我的回答。

看看你有什么:

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.


以上是关于 spring:SFTP : BeanPostProcessor 干扰 @ServiceActivator 和 @MessagingGateway的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>