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(); } |
如果您将
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 typeBeanFactoryPostProcessor orBeanPostProcessor , since such beans will get initialized early in the container lifecycle and should avoid triggering other parts of the configuration at that point.