我是靠谱客的博主 顺心大侠,最近开发中收集的这篇文章主要介绍[问题解决]定时任务quartz的job中注入service为null,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言:

        在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中使用: 
Java代码   收藏代码
  1. public class InitServlet extends HttpServlet {  
  2.          
  3.     @Autowired  
  4.     private ServiceA serviceA;  
  5.       
  6.     public void init(ServletConfig config) throws ServletException {  
  7.         super.init(config);  
  8.         SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);  
  9.         assertNotNull("Service should be injected.", serviceA);  
  10.     }  
  11.       
  12.         // Omitted doGet(req, res), doPost(req, res);  
  13. }  


2. 例子:在Quartz Job中使用: 
Java代码   收藏代码
  1. public class DumpJob implements Job {  
  2.       
  3.     @Autowired  
  4.     private ServiceA serviceA;  
  5.   
  6.     public void execute(JobExecutionContext context) throws JobExecutionException {  
  7.         SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);  
  8.         assertNotNull("Service should be injected.", serviceA);  
  9.     }  
  10. }  


3. SpringBeanAutowiringSupport源码分析: 
Java代码   收藏代码
  1. /** 
  2.      * Process {@code @Autowired} injection for the given target object, 
  3.      * based on the current web application context. 
  4.      * <p>Intended for use as a delegate. 
  5.      * @param target the target object to process 
  6.      * @see org.springframework.web.context.ContextLoader#getCurrentWebApplicationContext() 
  7.      */  
  8.     public static void processInjectionBasedOnCurrentContext(Object target) {  
  9.         Assert.notNull(target, "Target object must not be null");  
  10.         WebApplicationContext cc = ContextLoader.getCurrentWebApplicationContext();  
  11.         if (cc != null) {  
  12.             AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();  
  13.             bpp.setBeanFactory(cc.getAutowireCapableBeanFactory());  
  14.             bpp.processInjection(target);  
  15.         }  
  16.         else {  
  17.             if (logger.isDebugEnabled()) {  
  18.                 logger.debug("Current WebApplicationContext is not available for processing of " +  
  19.                         ClassUtils.getShortName(target.getClass()) + ": " +  
  20.                         "Make sure this class gets constructed in a Spring web application. Proceeding without injection.");  
  21.             }  
  22.         }  
  23.     }  

从方法第2行可以看出通过ContextLoader拿到当前的WebApplicationContext对象,再通过AutowiredAnnotationBeanPostProcessor类来解决当前传入的目标class的@Autowired注入能力。 
(AutowiredAnnotationBeanPostProcessor在Spring2.5随着Annotation功能的扩展而增加的,我们平时用context namepace的标签<context:component-scan>时,Spring会默认生成注册AutowiredAnnotationBeanPostProcessor类来帮助解析@Autowired @Value @Inject等标签。) 

4. 使用另一个工具类WebApplicationContextUtils来获取Service Bean: 
Java代码   收藏代码
  1. WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(config.getServletContext());  
  2. ServiceA ServiceA = context.getBean(ServiceA.class);  

当然这个方法更强大,因为直接拿到WebApplicationContext对象了! 

5. 补充WebApplicationContext相关: 
对于Web项目,通常使用org.springframework.web.context.ContextLoaderListener,设置属性contextConfigLocation来生成WebApplicationContext。 

WebApplicationContext类图(用StarUML画的): 

SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);

最后

以上就是顺心大侠为你收集整理的[问题解决]定时任务quartz的job中注入service为null的全部内容,希望文章能够帮你解决[问题解决]定时任务quartz的job中注入service为null所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(49)

评论列表共有 0 条评论

立即
投稿
返回
顶部