Spring Boot 2.0 - Quartz 和 Spring 集成 - 不工作

Spring Boot 2.0 - Quartz & Spring Integration - Not working

我在 spring 引导应用程序中使用 Quartz 和 Spring 集成模块。在 pom 中添加 Spring Integration 后,出现以下异常。在 pom.xml 中添加 Spring 集成模块之前,它一直在工作。看起来 Quartz 和 Spring Integration 之间有些冲突。请帮忙。

异常

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'quartzScheduler' defined in class path resource [org/springframework/boot/autoconfigure/quartz/QuartzAutoConfiguration.class]: Invocation of init method failed; nested exception is org.quartz.SchedulerException: ThreadPool class 'org.springframework.scheduling.quartz.LocalTaskExecutorThreadPool' props could not be configured. [See nested exception: java.lang.NoSuchMethodException: No setter for property 'threadCount']
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1710) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:583) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:502) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=12=](AbstractBeanFactory.java:312) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:310) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:741) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:868) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:388) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:327) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
    at com.xxx.t3.starter.Application.main(Application.java:31) [classes/:na]
Caused by: org.quartz.SchedulerException: ThreadPool class 'org.springframework.scheduling.quartz.LocalTaskExecutorThreadPool' props could not be configured.
    at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:849) ~[quartz-2.3.0.jar:na]
    at org.quartz.impl.StdSchedulerFactory.getScheduler(StdSchedulerFactory.java:1559) ~[quartz-2.3.0.jar:na]
    at org.springframework.scheduling.quartz.SchedulerFactoryBean.createScheduler(SchedulerFactoryBean.java:636) ~[spring-context-support-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.scheduling.quartz.SchedulerFactoryBean.afterPropertiesSet(SchedulerFactoryBean.java:510) ~[spring-context-support-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1769) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1706) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    ... 15 common frames omitted
Caused by: java.lang.NoSuchMethodException: No setter for property 'threadCount'
    at org.quartz.impl.StdSchedulerFactory.setBeanProps(StdSchedulerFactory.java:1447) ~[quartz-2.3.0.jar:na]
    at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:847) ~[quartz-2.3.0.jar:na]
    ... 20 common frames omitted

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.xxx</groupId>
    <artifactId>etl</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-activemq</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.activemq</groupId>
                    <artifactId>activemq-broker</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-integration</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-sftp</artifactId>
        </dependency>
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>wlthint3client</artifactId>
            <version>12.2.1.2</version>
            <scope>system</scope>
            <systemPath>C:/Oracle/products/mw_home/wlserver/server/lib/wlthint3client.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc</artifactId>
            <version>7</version>
            <scope>system</scope>
            <systemPath>C:/Oracle/products/mw_home/oracle_common/modules/oracle.jdbc/ojdbc7.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>com.sandp</groupId>
            <artifactId>rsadecryptor</artifactId>
            <version>1.0</version>
            <scope>system</scope>
            <systemPath>C:/DB/rsadecryptor.jar</systemPath>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

application.yml

spring:
  mail:
    host: XXXXXXXXXX
    port: 25
    protocol: smtp
    defaultEncoding: UTF-8
  quartz:
    jdbc:
      initialize-schema: never
    properties:
      org:
        quartz:
          scheduler:
            instanceId: AUTO
            #instanceName: ETL
            batchTriggerAcquisitionMaxCount: 25
            skipUpdateCheck: true
          threadPool:
            threadCount: 50
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
            tablePrefix: QRTZ_ 
            useProperties: true
            acquireTriggersWithinLock: true

email:
  from: test@xxxx.com

scheduler:
  datasource:
    url: jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=XXXXXXXXXX)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=XXXXXX)))
    username: scheduler
    password: XXXXXX
    driver-class-name: oracle.jdbc.OracleDriver

t3:
  datasource:
    url: jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=XXXXXXXXXXXXX)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=XXXXXXXXXXXX)))
    username: app
    password: XXXXXXXXX
    driver-class-name: oracle.jdbc.OracleDriver

Application.java

@ComponentScan("com.xxx.t3")
@IntegrationComponentScan
@EnableIntegration
@SpringBootApplication(exclude = { ActiveMQAutoConfiguration.class, JmxAutoConfiguration.class })
public class Application
{
    public static void main(String[] args) 
    {
        ApplicationContext context = SpringApplication.run(Application.class, args);        
    }
}

SchedulerConfig.java

@Configuration
public class SchedulerConfig
{
    private DataSource dataSource;

    private PlatformTransactionManager transactionManager;

    @Autowired
    public SchedulerConfig(@Qualifier("schedulerDataSource") DataSource dataSource, @Qualifier("schedulerTransactionManager") PlatformTransactionManager transactionManager) 
    {
        this.dataSource = dataSource;
        this.transactionManager = transactionManager;
    }

    @Bean
    public SchedulerFactoryBeanCustomizer schedulerFactoryBeanCustomizer() 
    {
        return bean -> 
        {
            bean.setDataSource(dataSource);
            bean.setTransactionManager(transactionManager);
            bean.setSchedulerName("ETL");
        };
    }
}

SftpConfig.java

@ConfigurationProperties("sftp")
@Configuration
public class SftpConfig 
{
    private String host;
    private int port;
    private String user;
    private String password;
    private String remoteDirectory;
    private String localDirectory;

    @Bean
    public SessionFactory<LsEntry> sftpSessionFactory()
    {
        DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
        factory.setHost(host);
        factory.setPort(port);
        factory.setUser(user);
        factory.setPassword(password);
        factory.setAllowUnknownKeys(true);

        return new CachingSessionFactory<LsEntry>(factory);
    }

    @Bean
    public SftpInboundFileSynchronizer sftpInboundFileSynchronizer() 
    {
        SftpInboundFileSynchronizer fileSynchronizer = new SftpInboundFileSynchronizer(sftpSessionFactory());
        fileSynchronizer.setDeleteRemoteFiles(true);
        fileSynchronizer.setRemoteDirectory(remoteDirectory);

        return fileSynchronizer;
    }

    @Bean
    @InboundChannelAdapter(channel = "fromSftpChannel", poller = @Poller(cron = "0/5 * * * * *"))
    public MessageSource<File> sftpMessageSource() 
    {
        SftpInboundFileSynchronizingMessageSource source = new SftpInboundFileSynchronizingMessageSource(sftpInboundFileSynchronizer());
        source.setAutoCreateLocalDirectory(true);
        source.setLocalDirectory(new File(localDirectory));

        return source;
    }

    @Bean
    @ServiceActivator(inputChannel = "fromSftpChannel")
    public MessageHandler resultFileHandler()
    {
        return new MessageHandler() 
        {
            @Override
            public void handleMessage(Message<?> message) throws MessagingException 
            {
                System.out.println("File added - " + message.getPayload());
            }
        };
    }           
}

Spring集成auto-configures一个TaskScheduler,也就是一个TaskExecutor.

我刚刚查看了启动的自动配置,如果它在应用程序上下文中找到了一个任务执行器,它会将它连接到石英工厂 bean 中:

public QuartzAutoConfiguration(QuartzProperties properties,
        ObjectProvider<List<SchedulerFactoryBeanCustomizer>> customizers,
        ObjectProvider<Executor> taskExecutor, ObjectProvider<JobDetail[]> jobDetails,
        ObjectProvider<Map<String, Calendar>> calendars,
        ObjectProvider<Trigger[]> triggers, ApplicationContext applicationContext) {
    this.properties = properties;
    this.customizers = customizers.getIfAvailable();
    this.taskExecutor = taskExecutor.getIfUnique();
    this.jobDetails = jobDetails.getIfAvailable();
    this.calendars = calendars.getIfAvailable();
    this.triggers = triggers.getIfAvailable();
    this.applicationContext = applicationContext;
}

if (this.taskExecutor != null) {
    schedulerFactoryBean.setTaskExecutor(this.taskExecutor);
}

然后,石英工厂 bean 配置一个 LocalTaskExecutorThreadPool,它没有线程数 属性。

您可以添加第二个任务执行器,这样 getIfUnique() 调用将 return 为空。

我建议你针对 spring-boot 提出一个 github 问题,看看他们是否能以某种方式避免这个问题。

编辑

您还应该能够将 bean.setTaskExecutor(null); 添加到您的 SchedulerFactoryBeanCustomizer 以恢复之前的行为。