【问题标题】:Spring Boot 2.0 Quartz - Use non-primary datasourceSpring Boot 2.0 Quartz - 使用非主数据源
【发布时间】:2018-04-03 19:09:13
【问题描述】:

我在我的应用程序中使用 Quartz 作为调度程序。尝试使用 Spring Boot 2.0 功能。我在配置中有 2 个不同的数据源。一个用于应用程序,另一个用于调度程序。如何使用非主数据源(在本例中为 schedulerDataSource)作为 Quartz 的数据源?请帮忙。

pom.xml

    <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-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</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>       
    </dependencies>

application.yml

spring:
  quartz:
    job-store-type: jdbc
    jdbc:
      initialize-schema: never
    properties:
      org:
        quartz:
          scheduler:
            instanceName: ETL
          threadPool:
            threadCount: 50
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
            tablePrefix: QRTZ_ 
            useProperties: true

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

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

AppDataSource.java

@Configuration
public class AppDataSource 
{
    @Bean
    @Primary
    @ConfigurationProperties("t3.datasource")
    public DataSourceProperties t3DataSourceProperties() 
    {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    @ConfigurationProperties("t3.datasource")
    public HikariDataSource t3DataSource() 
    {
        return t3DataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

    @Bean
    @ConfigurationProperties("scheduler.datasource")
    public DataSourceProperties schedulerDataSourceProperties() 
    {
        return new DataSourceProperties();
    }

    @Bean
    @ConfigurationProperties("scheduler.datasource")
    public HikariDataSource schedulerDataSource() 
    {
        return schedulerDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

    @Bean
    public PlatformTransactionManager schedulerTransactionManager()
    {
        final DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(schedulerDataSource());

        return transactionManager;
    }
}

Application.java

package com.aaa.t3.starter;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;

import com.spdji.aaa.jms.JmsService;

@ComponentScan("com.aaa.t3")
@SpringBootApplication(exclude = { ActiveMQAutoConfiguration.class, JmxAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class })
public class Application
{
    public static void main(String[] args) 
    {
        ApplicationContext context = SpringApplication.run(Application.class, args);
        JmsService jmsService = (JmsService) context.getBean("jmsService");
        jmsService.sendMessage();

        /*String[] beans = context.getBeanDefinitionNames();
        Arrays.stream(beans).sorted().forEach(System.out::println);*/
    }
}

尝试使用 SchedulerFactoryBeanCustomizer 修改数据源,但仍然引用主数据源。

@Configuration
public class SchedulerConfig 
{
    private DataSource dataSource;

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

    @Bean
    public SchedulerFactoryBeanCustomizer schedulerFactoryBeanCustomizer() 
    {
        return bean -> bean.setDataSource(dataSource);
    }
}

使用调试器验证。 SchedulerConfig 中的 Autowired 数据源是调度程序数据源,但之后它已被覆盖。在 QuartzAutoConfiguration.quartzDataSourceInitializer 中添加断点并检查数据源。这不是调度程序数据源。这已被主数据源覆盖。 Quartz 自动配置覆盖 SchedulerFactoryBeanCustomizer 自定义。我已经打开github.com/spring-projects/spring-boot/issues/12780 来修复它。

这是 spring-boot 中的一个错误。作为一种解决方法,我删除了 spring.quartz.job-store-type 属性,然后在定制器中配置了 DataSource 和 PlatformTransactionManager。请参阅下面的更新代码:

@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);
        };
    }
}

【问题讨论】:

标签: java spring-boot quartz-scheduler


【解决方案1】:

这是 spring-boot 中的一个错误。作为一种解决方法,我删除了 spring.quartz.job-store-type 属性,然后在定制器中配置了 DataSource 和 PlatformTransactionManager。请参阅下面的更新代码:

@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);
        };
    }
}

【讨论】:

    【解决方案2】:

    您可以通过自己构建SchedulerFactoryBean 来自定义数据源:

    @Bean
    public SchedulerFactoryBean schedulerFactory() {
        SchedulerFactoryBean bean = new SchedulerFactoryBean();
        bean.setDataSource(schedulerDataSource());
        return bean;
    }
    

    或者,在 Spring Boot 2 中,您可以使用 SchedulerFactoryBeanCustomizer。这将允许您自定义由自动配置器实例化的 bean,这可能会减少工作量。

    @Configuration
    public class SchedulerConfig {
        DataSource dataSource;
    
        @Autowired
        public SchedulerConfig(@Qualifier("scheduler.datasource") DataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        @Bean
        public SchedulerFactoryBeanCustomizer schedulerFactoryBeanCustomizer()
        {
            return bean -> bean.setDataSource(dataSource);
        }
    }
    

    【讨论】:

    • 是否可以在配置中执行此操作而不是构建 SchedulerFactoryBean?
    • 可能。 SchedulerFactoryBean.setDataSource() 上的 javadoc 暗示它可以在 quartz.properties 中设置,但这是“Spring 方式”。
    • 能否使用 SchedulerFactoryBeanCustomizer 分配非主数据源?我不想构建新的 SchedulerFactoryBean,因为我必须编写所有样板代码。
    • teppic - 我添加了带有 SchedulerFactoryBeanCustomizer 的示例代码。你能看看有什么问题吗?
    • @user1020455:定制器看起来是随 spring boot 2 一起出现的(我一直在使用 1.5)。我试过了,它“对我有用”。您不必通过ApplicationContext 来获取数据源,只需自动装配并直接引用它。
    【解决方案3】:

    我通过排除 DataSourceAutoConfiguration.class 来解决这个问题

    @SpringBootApplication(exclude = DataSourceAutoConfiguration.class,
    

    并自定义调度程序实例如下:

    @Bean("qzDataSource")
    @Qualifier("qzDataSource")
    public HikariDataSource qzDataSource() {
        return qzDataSourceProperties().initializeDataSourceBuilder()
                .type(HikariDataSource.class).build();
    }
    
    @Bean
    public PlatformTransactionManager quartzDataSourceTransactionManager(@Qualifier("qzDataSource") DataSource qzDataSource) {
        final DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(qzDataSource);
        return transactionManager;
    }
    
    @Bean
    public SchedulerFactoryBean schedulerFactory(@Qualifier("qzDataSource") DataSource qzDataSource) {
        SchedulerFactoryBean bean = new SchedulerFactoryBean();
        bean.setDataSource(qzDataSource);
        bean.setTransactionManager(quartzDataSourceTransactionManager(qzDataSource));
        return bean;
    }
    
    @Bean
    public Scheduler scheduler() {
        return schedulerFactory(qzDataSource()).getScheduler();
    }
    

    我的完整代码是https://github.com/guzhangyu/learn-spring-cloud/tree/master/springboot-quartz

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-14
      • 1970-01-01
      • 2019-11-14
      • 2016-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多