SFTP: BeanPostProcessor мешает работе @ServiceActivator и @MessagingGateway
Кажется, реализация интерфейса BeanPostProcessor влияет на @ServiceActivator. Каким должен быть способ использовать BeanPostProcessor с @ServiceActivator. Благодарю.
Полные журналы доступны здесь журналы
Ниже приводится Java Config, используемый для SFTP -
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
Основной класс
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();
}
}
Другие классы -
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);
}
}
}
Приведенный выше код работает нормально, если я не добавляю следующее объявление bean-компонента в определенный выше Java Config -
public LogInjector logInjector() {
return new LogInjector();
}
Выше определение бина имеет следующую реализацию -
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 {
}
Как только любая реализация BeanPostProcessor добавлена в Java Config, это создает проблему, и приложение не может видеть toSftpChannel -
org.springframework.beans.factory.NoSuchBeanDefinitionException: нет объекта bean с именем 'toSftpChannel', доступного в org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBort..getMergedLocalBeanDefinition(AbstractBeanFactory.java:1199) в org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284) в org.springframework.bean.Bean.Feans.bee.Fe) в org.springframework.integration.support.channel.BeanFactoryChannelResolver.resolveDestination(BeanFactoryChannelResolver.java:88) в org.springframework.integration.support.channel.gateway.MessagingGatewaySupport.getRequestChannel(MessagingGatewaySupport.java:327) в org.springframework.integration.gateway.MessagingGatewaySupport.send(MessagingGatewaySupport.java:368) по адресу org.springframework.integration.gateway.GatewayProxyFactoryBean.invokeGatewayMethod(GatewayProxyFactoryBean.java:477) в org.springFact.springframework.integration.gateway.GatewayProxyFactoryBean.invoke(GatewayProxyFactoryBean.java:420) при org.springframework.integration.gateway.GatewayCompletableFutureProxyFactoryBean.invoke(GatewayCompletableFutureProxyFactoryBean.java:65) в org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) в org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) в com.sun.proxy.$Proxy57.sendToSftp(Неизвестный источник)
1 ответ
Смотрит, что у тебя есть:
@Bean
public LogInjector logInjector() {
return new LogInjector();
}
Если вы объявите BeanPostProcessor
как @Bean
Вы должны указать их с static
модификатор: https://docs.spring.io/spring/docs/5.0.0.RELEASE/spring-framework-reference/core.html
Вы можете объявить
@Bean
методы как статические, позволяющие вызывать их без создания в качестве экземпляра содержащего их класса конфигурации. Это имеет особый смысл при определении бинов постпроцессора, например, типаBeanFactoryPostProcessor
или жеBeanPostProcessor
, поскольку такие bean-компоненты будут инициализироваться на ранних этапах жизненного цикла контейнера и должны избегать запуска других частей конфигурации в этой точке.