最近做项目用到定时任务,在设计的时候,考虑到每条job对应一个jobClass和jobMethod,会需要用到反射技术。
通过反射技术获取指定的方法,然后去执行。但是!!!在执行invoke方法的时候,一直报空指针异常,百度了一下午才找到解决方案。现在把解决方法分享给大家。
先看下代码:
首先是一个测试类:
@Test
public void test() {
String classBeanName = "com.bandweaver.tunnel.service.mam.measobj.MeasObjModuleCenter";
String targetMethod = "saveSOSchedule";
try {
Class<?> clazz = Class.forName(classBeanName);
Method method = clazz.getDeclaredMethod(targetMethod);
LogUtil.info("Get method : " + method);
method.setAccessible(true);
method.invoke(clazz.newInstance());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
我的目的是要执行下面这个方法,代码不需要细看,你只需要知道我这个方法里需要用到@autowired注解去获取我需要的bean,这就是为什么报空指针的原因了。
/**SO
*
* @author shaosen
* @throws Exception
* @Date 2018年9月10日
*/
public void saveSOSchedule(){
//从缓存中获取数据,然后定时向对象表中更新数据,并同时保存到value表中
List<MeasObjSO> measObjSOs = getMeasObjSOs();
LogUtil.info("*******************************************************************");
LogUtil.info("*******************************************************************");
LogUtil.info("*******************************************************************");
LogUtil.info("measObjSOs.size: " + measObjSOs.size() );
for (MeasObjSO measObjSO : measObjSOs) {
//step1:保存数据到value表中
if(measValueSOMapper == null ) { measValueSOMapper = SpringContextHolder.getBean("measValueSOMapper");}
List<MeasValueSO> list = measValueSOMapper.getByObjectId(measObjSO.getId());
LogUtil.info("Get list : " + list);
if(list != null && list.size() >0 ) {
MeasValueSO measValueSO = list.get(0);
if(measValueSO.getTime() != null && measValueSO.getTime().getTime()>=measObjSO.getRefreshTime().getTime()) {
LogUtil.info("SO监测对象[" + measObjSO.getId() + "]缓存数据暂无更新 ");
continue;
}
}
MeasValueSO measValueSO = new MeasValueSO();
measValueSO.setObjectId(measObjSO.getId());
measValueSO.setTime(measObjSO.getRefreshTime());
measValueSO.setCV(measObjSO.getCV());
LogUtil.info("Start to save : " + measValueSO);
measValueSOMapper.addMeasValueSO(measValueSO);
LogUtil.info("Success !");
//step2:更新对象表
if(measObjSOMapper == null ) { measObjSOMapper = SpringContextHolder.getBean("measObjSOMapper");}
MeasObjSO fromDb = measObjSOMapper.getMeasObjSO(measObjSO.getId());
if(fromDb.getRefreshTime() != null && fromDb.getRefreshTime().getTime() >= measObjSO.getRefreshTime().getTime()) {
LogUtil.info("SO监测对象[" + measObjSO.getId() + "]缓存数据暂无更新 ");
continue;
}
fromDb.setRefreshTime(measObjSO.getRefreshTime());
fromDb.setCV(measObjSO.getCV());
LogUtil.info("Start to update : " + fromDb);
measObjSOMapper.update(fromDb);
LogUtil.info("Success !");
}
}
原因:method.invoke(clazz.newInstance());事实上class.newInstance()生成的实例中,是获取不到@autowired组件的
解决方案:method.invoke(ApplicationContextUtils.getBean("measObjModuleCenter"))
把代码修改成如下:
@Test
public void test() {
String classBeanName = "com.bandweaver.tunnel.service.mam.measobj.MeasObjModuleCenter";
String targetMethod = "saveSOSchedule";
try {
Class<?> clazz = Class.forName(classBeanName);
Method method = clazz.getDeclaredMethod(targetMethod);
LogUtil.info("Get method : " + method);
method.setAccessible(true);
method.invoke(SpringContextHolder.getBean("measObjModuleCenter"));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
顺手把SpringContextHolder工具类也粘贴了吧
package com.bandweaver.tunnel.common.platform.util;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import com.bandweaver.tunnel.common.platform.log.LogUtil;
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return applicationContext;
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
LogUtil.debug("从SpringContextHolder中取出Bean:" + name);
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
public static void clearHolder() {
LogUtil.debug("清除SpringContextHolder中的ApplicationContext:"
+ applicationContext);
applicationContext = null;
}
/**
* 实现ApplicationContextAware接口, 注入Context到静态变量中.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
// LogUtil.debug("注入ApplicationContext到SpringContextHolder:{}", applicationContext);
if (SpringContextHolder.applicationContext != null) {
LogUtil.info("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
}
SpringContextHolder.applicationContext = applicationContext; // NOSONAR
}
/**
* 实现DisposableBean接口, 在Context关闭时清理静态变量.
*/
public void destroy() throws Exception {
SpringContextHolder.clearHolder();
}
/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
if(applicationContext == null) {
throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
}
}
}