`
dark_wind_master
  • 浏览: 11433 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Quartz与Spring的整合-Quartz中的job如何自动注入spring容器托管的对象

阅读更多

转自  http://blog.arganzheng.me/posts/quartz-and-spring-integration-ioc-autowire.html

 

 

Quartz与Spring的整合-Quartz中的job如何自动注入spring容器托管的对象

December 29, 2012

问题

Quartz中的job是由Quartz框架动态创建的(配置该job的classname,通过反射创建),而job一般会依赖到配置在spring中的bean,怎样获取或者更好的自动注入这些依赖bean呢?

预期效果

我们希望达到这样的效果:

/**
 * 
 * 取消超时未支付订单的任务。 
 *
 * @author arganzheng
 */
public class CancelUnpaidOrderTask implements Job {
    @Autowired
    private AppOrderService orderService;

    @Override
    public void execute(JobExecutionContext ctx) throws JobExecutionException {
        ...
}

关键在这一行:

@Autowired
private AppOrderService orderService;

orderService是配置在spring容器中的,而CancelUnpaidOrderTask则是配置在Quartz数据库中,由org.springframework.scheduling.quartz.SpringBeanJobFactory 运行时调用`

protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception;` 方法创建的。

解决方案

Spring提供了一种机制让你可以获取ApplicationContext。就是ApplicationContextAware接口。对于一个实现了ApplicationContextAware接口的类,Spring会实例化它的同时,调用它的public void setApplicationContext(ApplicationContext applicationContext) throws BeansException;接口,将该bean所属上下文传递给它。

一般利用这个来做ServicesLocator:

public class FooServicesLocator implemnts ApplicationContextAware{
    private static ApplicationContext applicationContext;

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

    public static FooService getFooService() {
        return applicationContext.getBean(FooService.class);
    }
}

当然,你需要在你的xml配置文件中定义FooServicesLocator和FooService。然后在需要引用FooService的地方,就可以这样子获取FooService了:FooServicesLocator.getFoobarServic(); 得到Spring托管的FooService。

不过这样是依赖查询,不是注入,要实现DI,可以使用AutowireCapableBeanFactory进行autowire。

applicationContext.getAutowireCapableBeanFactory().autowireBean(existingBean);

于是对于上面的那个问题,就有了如下的解决方案:

package me.arganzheng.study.quartz.task.SpringBeanJobFactory;

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * 自定义SpringBeanJobFactory,用于对Job注入ApplicationContext等。
 * 
 * @author arganzheng
 */
public class SpringBeanJobFactory extends org.springframework.scheduling.quartz.SpringBeanJobFactory implements ApplicationContextAware {
    private ApplicationContext applicationContext;

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

    /**
     * 这里我们覆盖了super的createJobInstance方法,对其创建出来的类再进行autowire。
     */
    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        Object jobInstance = super.createJobInstance(bundle);
        applicationContext.getAutowireCapableBeanFactory().autowireBean(jobInstance);
        return jobInstance;
    }
}

然后在Spring中配置Quartz的入口:

<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="jobFactory">
            <bean class="me.arganzheng.study.quartz.task.SpringBeanJobFactory" />
        </property>

        ...

    </bean>
</beans>

对于数据库配置方式的Quartz,配置非常简单,就一个入口类org.springframework.scheduling.quartz.SchedulerFactoryBean。我们这里通过配置它的jobFactory为我们自定义的JobFactory来实现自动注入功能:

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="jobFactory">
            <bean class=”me.arganzheng.study.quartz.task.SpringBeanJobFactory" />
        </property>
        ...
</bean>

注意 :上面的XML配置采用了直接配置jobFactory属性的方式将jobFactory配置为我们自定义的jobFactory类,默认是org.springframework.scheduling.quartz.SpringBeanJobFactory。虽然Quartz允许我们通过org.quartz.scheduler.jobFactory.class配置项配置自定义的jobFactory:

org.quartz.scheduler.jobFactory.class

The class name of the JobFactory to use. The default is org.quartz.simpl.SimpleJobFactory, you may like to tryorg.quartz.simpl.PropertySettingJobFactory. A JobFatcory is responsible for producing instances of JobClasses. SimpleJobFactory simply calls newInstance() on the class. PropertySettingJobFactory does as well, but also reflectively sets the job’s bean properties using the contents of the JobDataMap.

但是注意到我们配置的是Spring封装的是org.springframework.scheduling.quartz.SchedulerFactoryBean,它并不认这个配置项目。因为它已经将它的jobFactory由org.quartz.simpl.SimpleJobFactory改为org.springframework.scheduling.quartz.SpringBeanJobFactory,所以只能采用配置jobFactory属性的方式修改jobFactory为我们的自定义factory。

spring的AutowireCapableBeanFactory其实非常强大,Spring3.0允许任何通过Spring配置的bean都可以自动注入它所属的上下文,也就是说默认所有的bean都自动实现了ApplicationContextAware接口,这样就不用显示实现ApplicationContextAware接口了( 是不是更POJO:) ): How to inject dependencies into a self-instantiated object in Spring?

public class SpringBeanJobFactory extends org.springframework.scheduling.quartz.SpringBeanJobFactory{
    @Autowire
    private AutowireCapableBeanFactory beanFactory;

    /**
     * 这里我们覆盖了super的createJobInstance方法,对其创建出来的类再进行autowire。
     */
    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        Object jobInstance = super.createJobInstance(bundle);
        beanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

关于使用ApplicationContextAwareAutowireCapableBeanFactory实现@Autowired功能,在stackoverflow上这个帖子有很详细的说明,可以参考一下:How to get beans created by FactoryBean spring managed?

其他解决方案

对于Quartz与Spring的整合问题,spring其实提供了很多内建方案:

  1. 使用org.springframework.scheduling.quartz.JobDetailBean+jobDataAsMap:比如这个:Spring 3 + Quartz 1.8.6 Scheduler Example。不过貌似不推荐.
  2. 使用org.springframework.scheduling.quartz.SchedulerFactoryBean+schedulerContextAsMap:比如这个:Quartz and Spring Integration
  3. 使用org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean:这个可以让任何定义在spring中的类成为Quartz要求的job。比如这个:25.6 Using the OpenSymphony Quartz Scheduler
  4. 使用org.springframework.scheduling.quartz.SchedulerFactoryBean+applicationContextSchedulerContextKey:比如这个:Accessing Spring beans from Quartz jobs

每种方法笔者都认真的看过,而且找的例子都是非常不错的例子。个人感觉3和4不错,尤其是4。3使用起来有点像spring的事务配置,4使用起来有点像在web层通过WebApplicationContextUtils得到spring的ApplicationContext。不过这几种方式都不是依赖注入,而且配置信息比较多。所以还是推荐上面的org.springframework.scheduling.quartz.SchedulerFactoryBean+AutowireCapableBeanFactory的SpringBeanJobFactory解决方案:)


@Autowired注解大大节省了Spring的xml配置,将bean依赖关系声明转移到类文件和运行期。即: 原来需要这样的配置:

<bean id="thisClass" class="me.arganzheng.study.MyClass" />
  <property name="anotherClass" ref="anotherClass" />
</bean>
<bean id="anotherClass" class="me.arganzheng.study.AnotherClass">
</bean>

package me.arganzheng.study;

public class MyClass { 
  private Another anotherClass;

  public void setAnotherClass(AnotherClass anotherClass) {
    this.anotherClass = anotherClass; 
  }
}

使用@Autowired注解可以简化为:

<bean id="thisClass" class="me.arganzheng.study.MyClass" />
</bean>
<bean id="anotherClass" class="me.arganzheng.study.AnotherClass">
</bean>

package me.arganzheng.study;

public class MyClass { 
  @Autowired
  private Another anotherClass;
}

不过这样MyClass本身在Spring配置文件中定义,而它的依赖又是在自身类文件通过@Autowired注解声明,需要有种方式告诉Spring说当你根据配置文件创建我的时候,麻烦也扫描一下我的注解,把通过注解声明的依赖也注入进来。这可以通过Spring的<context:spring-configured/>配置+AspectJ的 Configurable注解来实现运行期依赖注入:Aspect Oriented Programming with Spring

@Configurable
public class MyClass {
   @Autowired 
   private AnotherClass instance;
}

Then at creation time it will automatically inject its dependencies. You also should have <context:spring-configured/> in your application context xml. 说明:其实还需要MyClass注册在application context xml文件中。

不用AspectJ的注解,其实Spring3也有类似的注解,主要用于Spring MVC:

注意 :这里面有一个非常重要的前提,就是所有的类(如上面的MyClassAnotherClass)都必须已经在spring中配置,只是这些bean直接的依赖关系(如上面的MyClass依赖于AntherClass),可以通过注解(如@autowired)实现运行期自动注入。而且要让spring在根据配置文件创建该这些bean的时候,还额外的去解析该bean的注解并且注入通过注解声明的依赖bean,需要在配置文件中额外的配置来告诉spring。比如上面的<context:spring-configured/>就是做这样的事情。

一个完整的Configurable例子见这篇文档:Spring, Aspects, @Configurable and Compile Time Weaving using maven

如果bean本身(不即使依赖关系)也不想使用Spring配置文件注册,那么就需要额外的配置告诉Spring哪些类是需要你托管的,一般是包扫描:<context:component-scan>+特殊的类注解如@Controller,@Component, etc. 15. Web MVC framework-15.3.1 Defining a controller with @Controller

15.3.1 Defining a controller with @Controller

The @Controller annotation indicates that a particular class serves the role of a controller. Spring does not require you to extend any controller base class or reference the Servlet API. However, you can still reference Servlet-specific features if you need to.

The @Controller annotation acts as a stereotype for the annotated class, indicating its role. The dispatcher scans such annotated classes for mapped methods and detects @RequestMapping annotations (see the next section).

You can define annotated controller beans explicitly, using a standard Spring bean definition in the dispatcher’s context. However, the @Controller stereotype also allows for autodetection, aligned with Spring general support for detecting component classes in the classpath and auto-registering bean definitions for them.

To enable autodetection of such annotated controllers, you add component scanning to your configuration. Use the spring-context schema as shown in the following XML snippet:

<?xml version="1.0" encoding="UTF-8"?>
  <beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
      http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/context 
      http://www.springframework.org/schema/context/spring-context-3.0.xsd">

     <context:component-scan base-package="org.springframework.samples.petclinic.web"/>

     // ...

</beans>

stackoverflow上有一个非常详细讲解<context:annotation-config><context:component-scan>的帖子: Difference between <context:annotation-config> vs <context:component-scan>。很长,这里就不quote了。简单来说就是两个步骤:

  1. 扫描类:<context:component-scan> + @Controller@Component, etc.
  2. 通过注解方式注入该类的依赖:<context:annotation-config/>

如果配置了1,那么自动包含2.

当然,回到我们的主题,如果有些bean不应该由Spring托管(不是xml配置,也不是anotation注解+包路径扫描),而是由框架或者应用创建的,那么就需要使用我们一开始介绍的方法来处理了。

—EOF—

 

分享到:
评论

相关推荐

    quartz和spring-quartz

    Spring-Quartz的集成则将这些概念与Spring的IoC(Inversion of Control)容器相结合,提供了更好的依赖注入支持。以下是一些关键点: 1. **Bean集成**: Spring可以自动将Job实例作为Spring Bean管理,这样你就可以...

    Quartz与Spring整合练习源代码

    将Quartz与Spring整合,可以方便地管理和控制定时任务,同时利用Spring的依赖注入和管理功能,提高代码的可维护性和可测试性。 Quartz的核心概念包括Job、Trigger和Scheduler。Job是实际需要执行的任务,Trigger是...

    Spring中的Quartz配置-Spring-定时器-java定时器.doc

    Spring 中的 Quartz 配置-Spring 定时器-java 定时器 在 Spring 框架中,Quartz 是一个非常流行的开源作业调度器,可以实现任务的定时执行。在本篇文章中,我们将讨论如何在 Spring 中配置 Quartz,以实现 Java ...

    quartz整合springbatch动态集群定时实现mysql参考

    Quartz与Spring的集成使得在Spring应用中管理任务变得更加容易,可以利用Spring的依赖注入和生命周期管理。 Spring Batch是一个用于处理大量数据的批处理框架。它提供了诸如事务管理、错误处理、读写器和处理器抽象...

    spring-boot-starter-quartz:Quartz-Scheduler的Spring-Boot自动配置

    Quartz Scheduler的Spring-Boot自动配置只是Quartz Scheduler的Spring-Boot启动器。 当然,Quartz Scheduler已经有好几个启动器,但是它们都不能满足我的所有需求,因此我创建了自己的启动器。 这只是一个业余项目。...

    spring2与quartz在Web整合

    在将 Spring 2 与 Quartz 整合到 Web 应用中时,主要涉及以下几个关键知识点: 1. **Spring 2 的核心概念**:Spring 2 提供了 IoC(Inversion of Control,控制反转)和 AOP(Aspect-Oriented Programming,面向切...

    quartz-spring-demo

    在 "quartz-spring-demo" 这个项目中,我们将会看到如何在 Spring 应用程序中集成 Quartz。以下是一些关键的知识点: 1. **Quartz 简介**:Quartz 提供了一个完全线程化的模型来执行定时任务,支持复杂的调度策略,...

    spring和quartz整合示例

    所需jar如下: spring-beans-3.2.4.RELEASE.jar spring-core-3.2.4.RELEASE.jar spring-expression-3.2.4.RELEASE....quartz-all-2.1.7.jar spring-tx-3.2.4.RELEASE.jar slf4j-log4j12-1.6.1.jar slf4j-api-1.6.1.jar

    spring-context-support-4.2.2和quartz-2.2.3所需Jar包

    标题中的"spring-context-support-4.2.2和quartz-2.2.3所需Jar包"涉及到两个关键的Java库:Spring Context Support 4.2.2版本和Quartz Scheduler 2.2.3版本。这两个库在企业级Java应用开发中扮演着重要角色,特别是...

    spring-context-support.jar,quartz-all-1.6.0.jar

    而当我们提到`spring-context-support.jar`和`quartz-all-1.6.0.jar`时,我们是在讨论Spring框架中的任务调度功能,特别是与Quartz库的集成。 `spring-context`是Spring框架的核心模块之一,它提供了上下文...

    spring整合quartz动态定时任务demo-spring-quartz-demo.zip

    这个"spring-quartz-demo"项目就是一个很好的示例,它展示了如何在Spring中整合Quartz以实现动态定时任务。 首先,我们要了解Spring和Quartz的基本概念。Spring是一个全面的Java企业级应用开发框架,它提供了依赖...

    Spring整合Quartz

    集成Spring与Quartz的步骤主要包括以下几点: 1. **引入依赖**:在项目中添加Quartz和Spring相关的jar包。在给定的描述中提到,已经包含了Quartz 1.5.2的jar包,但请注意,这是一个较旧的版本,可能不支持最新的...

    Java_Spring与Quartz的整合

    以下将详细介绍如何进行Spring与Quartz的整合。 1. **集成步骤** - 添加依赖:首先,确保在项目中添加了Spring和Quartz的相关库。如果是Maven项目,可以在pom.xml文件中加入对应的依赖。 - 配置Quartz:在Spring...

    Quartz注入Spring的Bean

    3. **Spring注入Bean到Job**:在Job执行时,我们可以直接注入其他由Spring管理的Bean,无需在Job中创建这些对象,从而减少了代码的复杂性,增强了可重用性。 4. **JobFactory的自定义**:Quartz默认使用...

    ssm-springMVC-整合Quartz(解决了无法注入service问题).doc

    ssm-springMVC-整合Quartz(解决了无法注入service问题).doc

    spring-context-support-5.3.15-API文档-中英对照版.zip

    赠送jar包:spring-context-support-5.3.15.jar; 赠送原API文档:spring-context-support-5.3.15-javadoc.jar; 赠送源代码:spring-context-support-5.3.15-sources.jar; 赠送Maven依赖信息文件:spring-context-...

    spring-boot-starter-quartz-2.1.6.RELEASE.jar

    java运行依赖jar包

    Quartz和Spring整合

    Spring与Quartz的整合,主要目的是利用Spring的依赖注入(DI)和面向切面编程(AOP)能力,简化Quartz的配置和任务的管理。在Spring中,我们可以声明式地配置Quartz,将Job和Trigger作为Spring的bean管理,这样可以...

    ssm-springMVC-整合Quartz(解决了无法注入service问题)-项目示例

    当需要在Quartz作业中调用由Spring管理的服务时,会出现依赖注入的问题,因为Quartz的工作线程与Spring的ApplicationContext不一致。 为了解决这个问题,我们需要以下步骤: 1. **配置Spring上下文**:首先,在...

    quartz-2.3.0-API文档-中文版.zip

    赠送jar包:quartz-2.3.0.jar; 赠送原API文档:quartz-2.3.0-javadoc.jar; 赠送源代码:quartz-2.3.0-sources.jar; 赠送Maven依赖信息文件:quartz-2.3.0.pom; 包含翻译后的API文档:quartz-2.3.0-javadoc-API...

Global site tag (gtag.js) - Google Analytics