概述
前言:
在quartz框架中,Job 是通过反射出来的实例,不受spring的管理。Scheduler现在交给Spring生成,在Spirng-context-support jar包下org.springframework.scheduling.quartz包中有个SpringBeanJobFactory的类,job实例通过该类的createJobInstance方法创建。根据Scheduler context、job data map and trigger data map填充其属性。但是创建的job实例并没被spring管理。
解决方法一:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
有的人可以,我的不行,暂时不知道
解决方法二:
1:自定义类,继承QuartzInitializerListener
QuartzServletContextListener 代码如下:
package com.tuniu.distribute.adaptor.web.hotel.quartz;
import org.quartz.SchedulerException;
import org.quartz.ee.servlet.QuartzInitializerListener;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.stereotype.Component;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
/**
* @author linliang
* @create 2018-07-10 19:13
*/
@Component
public class QuartzServletContextListener extends QuartzInitializerListener {
public static final String MY_CONTEXT_NAME = "servletContext";
@Override
public void contextDestroyed(ServletContextEvent sce) {
// TODO Auto-generated method stub
super.contextDestroyed(sce);
}
@Override
public void contextInitialized(ServletContextEvent sce) {
// TODO Auto-generated method stub
super.contextInitialized(sce);
ServletContext servletContext = sce.getServletContext();
StdSchedulerFactory factory = (StdSchedulerFactory) servletContext
.getAttribute(QuartzInitializerListener.QUARTZ_FACTORY_KEY);
try {
factory.getScheduler().getContext()
.put(QuartzServletContextListener.MY_CONTEXT_NAME, servletContext);
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
然后在Job中:
@Override
public void execute(JobExecutionContext jobContext) {
// SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
ServletContext context = null;
try {
context = (ServletContext) jobContext.getScheduler().getContext()
.get(QuartzServletContextListener.MY_CONTEXT_NAME);
} catch (SchedulerException e1) {
e1.printStackTrace();
}
WebApplicationContext cxt = (WebApplicationContext) context.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
HotelService hotelService = (HotelService) cxt.getBean("hotelServiceImpl");
}
通过https://blog.csdn.net/cjs68/article/details/78316899博文整理,但是这文中的不生效。
所以本文改造了下,亲测有效。
理解:
我们需要自定义一个类将创建的job添加到applicationContext中,该类需要继承SpringBeanJobFactory,并实现ApplicationContextAware接口。
ApplicationContextAware接口的作用:Spring容器会检测容器中的所有Bean,如果发现某个Bean实现了ApplicationContextAware接口,Spring容器会在创建该Bean之后,自动调用该Bean的setApplicationContextAware()方法,调用该方法时,会将容器本身作为参数传给该方法——该方法中的实现部分将Spring传入的参数(容器本身)赋给该类对象的applicationContext实例变量,因此接下来可以通过该applicationContext实例变量来访问容器本身。
重写SpringBeanJobFactory类中的createJobInstance方法,将创建的job实例添加到applicationContext中,交给spring管理。引文
spring-web在version2.5.1的时候,在package org.springframework.web.context.support下加入了一个工具类叫 SpringBeanAutowiringSupport ,主要用来对Spring Web Application Context之外的类提供@Autowired注入功能。官方Doc讲的更清楚点:
http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/context/support/SpringBeanAutowiringSupport.html
具体来讲,Servlet中本来不能使用@Autowired注入bean,解决办法是在Servlet的init(ServletConfig)方法中调用SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this),就可以直接使用@Autowired来注入Web Application Context下的一些Service等Bean了。(见下例)
又或者使用Quartz Job的时候,可以在Job类中使用SpringBeanAutowiringSupport,就可以直接直使Spring的bean了。(当然如果Job比较多的话,这种方法还是很不方便,推荐使用SchedulerFactoryBean来集成。另一种更方便的办法是直接将Job集成到Spring Context中,当做一个bean)。
值得注意的是,这个类的作用域是Web Application Context,如果应用中实现的是一个比如ConfigurableApplicationContext,那就不能用该方法来对Servlet或是Job或是其它目标类提供@Autowired。
1. 例子:在Servlet中使用:
- public class InitServlet extends HttpServlet {
- @Autowired
- private ServiceA serviceA;
- public void init(ServletConfig config) throws ServletException {
- super.init(config);
- SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
- assertNotNull("Service should be injected.", serviceA);
- }
- // Omitted doGet(req, res), doPost(req, res);
- }
2. 例子:在Quartz Job中使用:
- public class DumpJob implements Job {
- @Autowired
- private ServiceA serviceA;
- public void execute(JobExecutionContext context) throws JobExecutionException {
- SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
- assertNotNull("Service should be injected.", serviceA);
- }
- }
3. SpringBeanAutowiringSupport源码分析:
- /**
- * Process {@code @Autowired} injection for the given target object,
- * based on the current web application context.
- * <p>Intended for use as a delegate.
- * @param target the target object to process
- * @see org.springframework.web.context.ContextLoader#getCurrentWebApplicationContext()
- */
- public static void processInjectionBasedOnCurrentContext(Object target) {
- Assert.notNull(target, "Target object must not be null");
- WebApplicationContext cc = ContextLoader.getCurrentWebApplicationContext();
- if (cc != null) {
- AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
- bpp.setBeanFactory(cc.getAutowireCapableBeanFactory());
- bpp.processInjection(target);
- }
- else {
- if (logger.isDebugEnabled()) {
- logger.debug("Current WebApplicationContext is not available for processing of " +
- ClassUtils.getShortName(target.getClass()) + ": " +
- "Make sure this class gets constructed in a Spring web application. Proceeding without injection.");
- }
- }
- }
从方法第2行可以看出通过ContextLoader拿到当前的WebApplicationContext对象,再通过AutowiredAnnotationBeanPostProcessor类来解决当前传入的目标class的@Autowired注入能力。
(AutowiredAnnotationBeanPostProcessor在Spring2.5随着Annotation功能的扩展而增加的,我们平时用context namepace的标签<context:component-scan>时,Spring会默认生成注册AutowiredAnnotationBeanPostProcessor类来帮助解析@Autowired @Value @Inject等标签。)
4. 使用另一个工具类WebApplicationContextUtils来获取Service Bean:
- WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(config.getServletContext());
- ServiceA ServiceA = context.getBean(ServiceA.class);
当然这个方法更强大,因为直接拿到WebApplicationContext对象了!
5. 补充WebApplicationContext相关:
对于Web项目,通常使用org.springframework.web.context.ContextLoaderListener,设置属性contextConfigLocation来生成WebApplicationContext。
WebApplicationContext类图(用StarUML画的):
最后
以上就是顺心大侠为你收集整理的[问题解决]定时任务quartz的job中注入service为null的全部内容,希望文章能够帮你解决[问题解决]定时任务quartz的job中注入service为null所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复