봄 / 봄 부팅이 제대로 석영하고 해결 실패를 통합 @Autowired

전과 금요일을 확인 Spring boot통합 Quartz탐구하는 소스 코드 방식 읽고, 프로젝트 및 발견 구성 오류 때문에 Spring올바른 통합 Quartz방법.

문제 발견

에있는 프로젝트 코드의 마지막 해 확인 QuartzJobBean존재가 부당 깨달았다.

(1) 프로젝트 종속성 :

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
        </dependency>
    </dependencies>

(2) 코드의 문제 :

@Component
public class UnprocessedTaskJob extends QuartzJobBean {

    private TaskMapper taskMapper;

    @Autowired
    public UnprocessedTaskJob(TaskMapper taskMapper){
        this.taskMapper = taskMapper;
    }
}

private JobDetail generateUnprocessedJobDetail(Task task) {
    JobDataMap jobDataMap = new JobDataMap();
    jobDataMap.put(UnprocessedTaskJob.TASK_ID, task.getId());
    return JobBuilder.newJob(UnprocessedTaskJob.class)
            .withIdentity(UnprocessedTaskJob.UNPROCESSED_TASK_KEY_PREFIX + task.getId(), UnprocessedTaskJob.UNPROCESSED_TASK_JOB_GROUP)
            .usingJobData(jobDataMap)
            .storeDurably()
            .build();
    }

(3) 정제 질문 :

코드보다 오류의 원인 UnprocessedTaskJob추가 @Component나타내는, 주석 Spring IOC컨테이너 单例클래스.
그러나, Quartz생성 된 Job각에서 Quartz Job Beanclass반사하는 적절한 만들 Job. 즉, 매번 새로 생성 Job시간, 해당 산출한다 Job예가. 이에 , 이는 UnprocessedTaskJob입니다 单例충돌.
코드보기 때문에 추가하지 않는 시간에 기록을 제출 @Component주석, 당신은을 통해 할 수 @Autowireda의 도입 Spring IOC호스팅 taskMapper, 즉 얻을 수없는 인스턴스를 依赖注入.

그러나 놀라운 내가뿐만 아니라 개발 환경에 갈 때이다 다음과 같은주의 사항, 프로그램을 실행 한 후 발견 인스턴스가 여전히 주입 할 수 프로그램 실행 ...UnprocessedTaskJob@ComponentTaskMapperJob

Spring 관리 석영

코드 분석

온라인 검색 Spring호스팅 Quartz기사, 대부분의 Spring MVC프로젝트는 해결하는 방법에 초점을 Job관통 구현 클래스를 @Autowired실현 Spring依赖注入.
대부분 달성하기 위해 인터넷에 의존 SpringBeanJobFactory달성하기 위해 통합.SpringQuartz

/**
 * Subclass of {@link AdaptableJobFactory} that also supports Spring-style
 * dependency injection on bean properties. This is essentially the direct
 * equivalent of Spring's {@link QuartzJobBean} in the shape of a Quartz
 * {@link org.quartz.spi.JobFactory}.
 *
 * <p>Applies scheduler context, job data map and trigger data map entries
 * as bean property values. If no matching bean property is found, the entry
 * is by default simply ignored. This is analogous to QuartzJobBean's behavior.
 *
 * <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1.
 *
 * @author Juergen Hoeller
 * @since 2.0
 * @see SchedulerFactoryBean#setJobFactory
 * @see QuartzJobBean
 */
public class SpringBeanJobFactory extends AdaptableJobFactory
        implements ApplicationContextAware, SchedulerContextAware {
}

/**
 * {@link JobFactory} implementation that supports {@link java.lang.Runnable}
 * objects as well as standard Quartz {@link org.quartz.Job} instances.
 *
 * <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1.
 *
 * @author Juergen Hoeller
 * @since 2.0
 * @see DelegatingJob
 * @see #adaptJob(Object)
 */
public class AdaptableJobFactory implements JobFactory {
}

그것은 위의 코드 및 코멘트를 찾을 수 있습니다 :

(1) AdaptableJobFactory를 구현 JobFactory인터페이스, 당신은 표준 생성하기 위해 수행 할 수있는 Quartz경우 (단 Quartz위 2.1.4 등);

(2) SpringBeanJobFactory유전 AdaptableJobFactory목적을 달성하기 위해 Quartz특성 의존적 패키지 인스턴스 주입.

(3) SpringBeanJobFactory구현 ApplicationContextAwareSchedulerContextAware인터페이스 ( Quartz태스크 스케줄링 상황), 이것은 생성 될 수있는 Job Bean분사 시간 ApplicationContexSchedulerContext.

팁 :
위의 코드를 기반으로 Spring5.1.8 버전
에서 Spring 4.1.0버전, SpringBeanJobFactory다음 코드와 같이 구현을 :

public class SpringBeanJobFactory extends AdaptableJobFactory
    implements SchedulerContextAware{

    // 具体代码省略
}

따라서, 초기에 Spring프로젝트의 필요성은 패키지 SpringBeanJobFactory및 구현 ApplicationContextAware인터페이스 (놀랄 서프라이즈?).

봄 이전 버전의 솔루션

이전 버전을 기반 Spring으로하는 솔루션 제공 Spring통합 Quartz솔루션을.
로부터 솔루션 제 39 장 : SpringBoot 및 석영은 단일 노드 지속성이 배포 작업의 완료의 타이밍을 바탕으로 (일련의 품질 위대한 하나님은 위대) 제공.

@Configuration
public class QuartzConfiguration
{
    /**
     * 继承org.springframework.scheduling.quartz.SpringBeanJobFactory
     * 实现任务实例化方式
     */
    public static class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements
            ApplicationContextAware {

        private transient AutowireCapableBeanFactory beanFactory;

        @Override
        public void setApplicationContext(final ApplicationContext context) {
            beanFactory = context.getAutowireCapableBeanFactory();
        }

        /**
         * 将job实例交给spring ioc托管
         * 我们在job实例实现类内可以直接使用spring注入的调用被spring ioc管理的实例
         * @param bundle
         * @return
         * @throws Exception
         */
        @Override
        protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
            final Object job = super.createJobInstance(bundle);
            /**
             * 将job实例交付给spring ioc
             */
            beanFactory.autowireBean(job);
            return job;
        }
    }

    /**
     * 配置任务工厂实例
     * @param applicationContext spring上下文实例
     * @return
     */
    @Bean
    public JobFactory jobFactory(ApplicationContext applicationContext)
    {
        /**
         * 采用自定义任务工厂 整合spring实例来完成构建任务
         * see {@link AutowiringSpringBeanJobFactory}
         */
        AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
        jobFactory.setApplicationContext(applicationContext);
        return jobFactory;
    }

    /**
     * 配置任务调度器
     * 使用项目数据源作为quartz数据源
     * @param jobFactory 自定义配置任务工厂(其实就是AutowiringSpringBeanJobFactory)
     * @param dataSource 数据源实例
     * @return
     * @throws Exception
     */
    @Bean(destroyMethod = "destroy",autowire = Autowire.NO)
    public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory, DataSource dataSource) throws Exception
    {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        //将spring管理job自定义工厂交由调度器维护
        schedulerFactoryBean.setJobFactory(jobFactory);
        //设置覆盖已存在的任务
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        //项目启动完成后,等待2秒后开始执行调度器初始化
        schedulerFactoryBean.setStartupDelay(2);
        //设置调度器自动运行
        schedulerFactoryBean.setAutoStartup(true);
        //设置数据源,使用与项目统一数据源
        schedulerFactoryBean.setDataSource(dataSource);
        //设置上下文spring bean name
        schedulerFactoryBean.setApplicationContextSchedulerContextKey("applicationContext");
        //设置配置文件位置
        schedulerFactoryBean.setConfigLocation(new ClassPathResource("/quartz.properties"));
        return schedulerFactoryBean;
    }
}

위의 코드에서,이를 달성된다 생성 인스턴스, 생성 된 전달하여 인스턴스 호스트는. 으로 설정된다 (실제로는 , 패키지의 내측 ) 및 (설정되어 있지 않은 경우, 데이터 소스는 디폴트 ).SpringBeanJobFactorycreateJobInstanceJobJobAutowireCapableBeanFactory
schedulerFactoryBeanJobFactoryAutowiringSpringBeanJobFactoryapplicationContextDataSourceQuartzRamJobStore

RamJobStore장점은 단점이 스케줄링 작업을 지속적으로 저장할 수 없다는 것입니다, 빠릅니다.

따라서, 우리는 정기적으로 작업 내에서 사용할 수 Spring IOC@Autowired같은 음 依赖注入.

봄 솔루션의 새로운 버전

(1) 설명

당신이 사용하는 경우 Spring boot, 그리고 버전이 좋은보다 큰 2.0, 그것은 권장합니다 spring-boot-starter-quartz.

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-quartz</artifactId>
    </dependency>

자동 구성 지원은 이제 Quartz 스케쥴러에 대한 (가) 있습니다. 우리는 또한 새로운 스프링 부팅 스타터 - 석영 스타터 POM을 추가했습니다.
당신은 메모리 JobStores, 또는 전체 JDBC 기반 저장소를 사용할 수 있습니다. 당신의 Spring 애플리케이션 컨텍스트의 모든 JobDetail에, 일정 및 트리거 콩은 자동 스케줄러에 등록됩니다.
자세한 내용은 참조 문서의 새로운 "Quartz 스케쥴러"절을 참조하십시오.

이들은입니다 spring-boot-starter-quartz프레젠테이션을 기반으로 도입, 당신은 해제하지 않을 경우 보여줍니다 Quartz자동 구성을, SpringBoot당신이 완료하는 데 도움이 Scheduler같은 자동화 된 구성, JobDetail/ Calendar/ Trigger, 등을하는 것은 Bean자동으로 등록됩니다 Shceduler에 있습니다. 당신은 할 수 있습니다 QuartzJobBean무료 사용 @Autowired및 기타 依赖注入주석을.

사실, 소개하지 않습니다 spring-boot-starter-quartz,하지만 수입 org.quartz-scheduler, Quartz자동화 된 구성이 여전히 적용됩니다 (다음은이 문제의 첫 번째 분석이다 제거 @Bean의견을, 프로그램이 여전히 이유를 실행, 비극 운).

(2) 분석 코드

/**
 * {@link EnableAutoConfiguration Auto-configuration} for Quartz Scheduler.
 *
 * @author Vedran Pavic
 * @author Stephane Nicoll
 * @since 2.0.0
 */
@Configuration
@ConditionalOnClass({ Scheduler.class, SchedulerFactoryBean.class, PlatformTransactionManager.class })
@EnableConfigurationProperties(QuartzProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
public class QuartzAutoConfiguration{

    // 此处省略部分代码

    @Bean
    @ConditionalOnMissingBean
    public SchedulerFactoryBean quartzScheduler() {
        // 因为新版本SchedulerFactoryBean已经实现ApplicationContextAware接口
        // 因此相对于老版本Spring解决方案中的AutowiringSpringBeanJobFactory进行封装
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        SpringBeanJobFactory jobFactory = new SpringBeanJobFactory();
        // SpringBeanJobFactory中注入applicationContext,为依赖注入创造条件
        jobFactory.setApplicationContext(this.applicationContext);
        // schedulerFactoryBean中注入setJobFactory(注意此处没有配置DataSource,DataSource详见`JdbcStoreTypeConfiguration`)
        // 以上这几个步骤,与老版本的Spring解决方案类似
        schedulerFactoryBean.setJobFactory(jobFactory);

        // 后续都是Quartz的配置属性设置,不再叙述
        if (this.properties.getSchedulerName() != null) {
            schedulerFactoryBean.setSchedulerName(this.properties.getSchedulerName());
        }
        schedulerFactoryBean.setAutoStartup(this.properties.isAutoStartup());schedulerFactoryBean.setStartupDelay((int) this.properties.getStartupDelay().getSeconds());
        schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(this.properties.isWaitForJobsToCompleteOnShutdown());
        schedulerFactoryBean.setOverwriteExistingJobs(this.properties.isOverwriteExistingJobs());
        if (!this.properties.getProperties().isEmpty()) {
            schedulerFactoryBean.setQuartzProperties(asProperties(this.properties.getProperties()));
        }
        if (this.jobDetails != null && this.jobDetails.length > 0) {
            schedulerFactoryBean.setJobDetails(this.jobDetails);
        }
        if (this.calendars != null && !this.calendars.isEmpty()) {
            schedulerFactoryBean.setCalendars(this.calendars);
        }
        if (this.triggers != null && this.triggers.length > 0) {
            schedulerFactoryBean.setTriggers(this.triggers);
        }
        customize(schedulerFactoryBean);
        return schedulerFactoryBean;
    }

    @Configuration
    @ConditionalOnSingleCandidate(DataSource.class)
    protected static class JdbcStoreTypeConfiguration {

        // 为Quartz的持久化配置DataSource,具体代码可以翻阅Spring源码得到
    }
}

이어서, SpringBeanJobFactory생성 분석, Job예를 수행하고 依赖注入키 조작 형.

/**
 * Subclass of {@link AdaptableJobFactory} that also supports Spring-style
 * dependency injection on bean properties. This is essentially the direct
 * equivalent of Spring's {@link QuartzJobBean} in the shape of a Quartz
 * {@link org.quartz.spi.JobFactory}.
 *
 * <p>Applies scheduler context, job data map and trigger data map entries
 * as bean property values. If no matching bean property is found, the entry
 * is by default simply ignored. This is analogous to QuartzJobBean's behavior.
 *
 * <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1.
 *
 * @author Juergen Hoeller
 * @since 2.0
 * @see SchedulerFactoryBean#setJobFactory
 * @see QuartzJobBean
 */
public class SpringBeanJobFactory extends AdaptableJobFactory
        implements ApplicationContextAware, SchedulerContextAware {

    @Nullable
    private String[] ignoredUnknownProperties;

    @Nullable
    private ApplicationContext applicationContext;

    @Nullable
    private SchedulerContext schedulerContext;

    /**
     * Specify the unknown properties (not found in the bean) that should be ignored.
     * <p>Default is {@code null}, indicating that all unknown properties
     * should be ignored. Specify an empty array to throw an exception in case
     * of any unknown properties, or a list of property names that should be
     * ignored if there is no corresponding property found on the particular
     * job class (all other unknown properties will still trigger an exception).
     */
    public void setIgnoredUnknownProperties(String... ignoredUnknownProperties) {
        this.ignoredUnknownProperties = ignoredUnknownProperties;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public void setSchedulerContext(SchedulerContext schedulerContext) {
        this.schedulerContext = schedulerContext;
    }

    /**
     * Create the job instance, populating it with property values taken
     * from the scheduler context, job data map and trigger data map.
     */
    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        // 创建Job实例
        // (1) 包含applicationContext,则通过AutowireCapableBeanFactory()创建相应Job实例,实现依赖注入
        // (2) 如果applicationContext为空,则使用AdaptableJobFactory创建相应的Bean(无法实现依赖注入)
        Object job = (this.applicationContext != null ?
                        this.applicationContext.getAutowireCapableBeanFactory().createBean(
                            bundle.getJobDetail().getJobClass(), AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false) :
                        super.createJobInstance(bundle));

        if (isEligibleForPropertyPopulation(job)) {
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job);
            MutablePropertyValues pvs = new MutablePropertyValues();
            if (this.schedulerContext != null) {
                pvs.addPropertyValues(this.schedulerContext);
            }
            pvs.addPropertyValues(bundle.getJobDetail().getJobDataMap());
            pvs.addPropertyValues(bundle.getTrigger().getJobDataMap());
            if (this.ignoredUnknownProperties != null) {
                for (String propName : this.ignoredUnknownProperties) {
                    if (pvs.contains(propName) && !bw.isWritableProperty(propName)) {
                        pvs.removePropertyValue(propName);
                    }
                }
                bw.setPropertyValues(pvs);
            }
            else {
                bw.setPropertyValues(pvs, true);
            }
        }

        return job;
    }

    // 省略部分代码
}

/**
 * {@link JobFactory} implementation that supports {@link java.lang.Runnable}
 * objects as well as standard Quartz {@link org.quartz.Job} instances.
 *
 * <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1.
 *
 * @author Juergen Hoeller
 * @since 2.0
 * @see DelegatingJob
 * @see #adaptJob(Object)
 */
public class AdaptableJobFactory implements JobFactory {
    /**
     * Create an instance of the specified job class.
     * <p>Can be overridden to post-process the job instance.
     * @param bundle the TriggerFiredBundle from which the JobDetail
     * and other info relating to the trigger firing can be obtained
     * @return the job instance
     * @throws Exception if job instantiation failed
     */
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        // 获取`QuartzJobBean`的实现`class`,通过反射工具创建相应的类实例(自然无法注入Spring托管的Bean实例)
        Class<?> jobClass = bundle.getJobDetail().getJobClass();
        return ReflectionUtils.accessibleConstructor(jobClass).newInstance();
    }
}

다음은 설명 할 필요 AutowireCapableBeanFactory. 조치를
프로젝트, 일부는 달성하지 않은 Spring통합의 깊이를, 그것의 예없는 Spring컨테이너 관리.
그러나, 대한 필요성이되지 않은 Spring에서 관리 Bean도입 할 필요가 Spring컨테이너를 Bean.
이 때, 우리는 달성하는 데 필요한 AutowireCapableBeanFactory, 그래서 Spring의존성 주입 및 기타 기능.

우리는 당신이 이전 버전과 새 버전이 방법을 알려 주시기, 위의 설명과 코드 분석을 희망 Spring에 바로 통합 Quartz.
또한, Spring boot자동화 된 구성이 구성 문제의 대부분을 해결할 수 있지만, 충분한 시간, 그것은 등, 소스 코드를 읽어 권장 이해 配置细节함으로써 더 많은 자신감을 선도.

추신 :
당신이 내 기사가 도움이 생각한다면, 당신은 (페니 사랑은 임의의 숫자) 빨간 봉투 또는 스캔 코드에서 지원을받을 수있는 코드를 스캔 할 수 있습니다, 감사합니다!

Alipay의 빨간 봉투 Alipay의 마이크로 편지

추천

출처www.cnblogs.com/jason1990/p/11110196.html