Spring定時任務計劃中注入service、資料來源的問題
阿新 • • 發佈:2019-02-15
前兩天寫了一個定時同步資料的問題,但是用著用著就發現問題了,由於我的定時任務用的spring的管理quartz Job類MethodInvokingJobDetailFactoryBean(注:這樣的好處是可以自由管控定時排程的執行類、方法、時間等),如圖:
但是發現這種方式無法通過註解的方式注入service或資料來源,因為它是這種方式定時執行的job方法
/** * 建立/新增計劃任務 * * @param tbcq * 計劃任務配置物件 * @throws Exception */ public void createCronTriggerBean(QuartzTask tbcq) throws Exception { // 新建一個基於Spring的管理Job類 MethodInvokingJobDetailFactoryBean mjdfb = new MethodInvokingJobDetailFactoryBean(); mjdfb.setName(tbcq.getJobdetailname());// 設定Job名稱 // 如果定義的任務類為Spring的定義的Bean則呼叫 getBean方法 if (tbcq.getIsspringbean().equals("1")) { mjdfb.setTargetObject(beanFactory.getBean(tbcq.getTargetobject()));// 設定任務類 } else { // 否則直接new物件 mjdfb.setTargetObject(Class.forName(tbcq.getTargetobject()) .newInstance());// 設定任務類 } mjdfb.setTargetMethod(tbcq.getMethodname()); // 設定任務方法 mjdfb.setConcurrent(tbcq.getConcurrent().equals("0") ? false : true); // 設定是否併發啟動任務 mjdfb.afterPropertiesSet(); // 將管理Job類提交到計劃管理類 // 將Spring的管理Job類轉為Quartz管理Job類 JobDetail jobDetail = new JobDetail(); jobDetail = (JobDetail) mjdfb.getObject(); jobDetail.setName(tbcq.getJobdetailname()); scheduler.addJob(jobDetail, true); // 將Job新增到管理類 // 新一個基於Spring的時間類 CronTriggerBean c = new CronTriggerBean(); c.setCronExpression(tbcq.getCronexpression()); // 設定時間表達式 c.setName(tbcq.getTriggername()); // 設定名稱 c.setJobDetail(jobDetail); // 注入Job c.setJobName(tbcq.getJobdetailname()); // 設定Job名稱 scheduler.scheduleJob(c); // 注入到管理類 scheduler.rescheduleJob(tbcq.getTriggername(), Scheduler.DEFAULT_GROUP, c); // 重新整理管理類 log.info(new Date() + ": 新建" + tbcq.getTriggername() + "計劃任務"); }
而我的定時方法中可以是需要持久層資料來源的(這裡用的jdbcTemplate),開始的時候用的這種方式獲取bean
ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml");
jdbcTemplate = (JdbcTemplate)appContext.getBean("jdbcTemplate");
而後來專案引入redis,又多了一個redisContext.xml的配置檔案,這時再執行排程時發現不好使了,因為applicationContext.xml檔案中有
而redis配置中引入了很多專案工廠類,例如這種<!-- 自動掃描與裝配bean --> <context:component-scan base-package="com.zxtj"></context:component-scan>
啟動專案時會報錯,原因是這種自動裝配的屬性沒有被定義或被set,get,在上面ClassPathXmlApplicationContext是獲取了spring的上下文全部內容,於是乎做了簡單的修改
這時問題解決了,可以正常運行了。ApplicationContext appContext = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml","redisContext.xml"}); jdbcTemplate = (JdbcTemplate)appContext.getBean("jdbcTemplate");
但是同事看後提出了疑問:這樣bean是不是會被建立多次,因為在啟動專案時spring 中bean已經被例項化了,現在只需要獲取對應的bean就可以了。
後來在同事的幫助下用到了更好的實現方式:spring服務定位模式ServiceLocator
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
public class ServiceLocator implements BeanFactoryAware {
private static BeanFactory beanFactory = null;
private static ServiceLocator servlocator = null;
public void setBeanFactory(BeanFactory factory) throws BeansException {
this.beanFactory = factory;
}
public BeanFactory getBeanFactory() {
return beanFactory;
}
public static ServiceLocator getInstance() {
if (servlocator == null)
servlocator = (ServiceLocator) beanFactory.getBean("serviceLocator");
return servlocator;
}
/**
* 根據提供的bean名稱得到相應的服務類
* @param servName bean名稱
*/
public static Object getService(String servName) {
return beanFactory.getBean(servName);
}
/**
* 根據提供的bean名稱得到對應於指定型別的服務類
* @param servName bean名稱
* @param clazz 返回的bean型別,若型別不匹配,將丟擲異常
*/
public static Object getService(String servName, Class clazz) {
return beanFactory.getBean(servName, clazz);
}
}
於是乎,我再封裝了一下,在原有的排程類中繼承一個獲取資料來源的類
public class DataSourceFactory {
public static JdbcTemplate jdbcTemplate = null;
static{
ServiceLocator service = ServiceLocator.getInstance();
jdbcTemplate = (JdbcTemplate)service.getService("jdbcTemplate");
}
}
這時在排程類中就可以直接引用了
public class PvisFlowSynTask extends DataSourceFactory{...}
public void insertFlowHis(final List<FlowBean> list){
try {
System.out.println("實時流量pvis資料同步排程 inserting...");
jdbcTemplate.batchUpdate(
"insert into T_UI_FLOWANALYSE_HIS (STATIS_DATE, STATIS_HOUR, AREA_ID, PEO_ID, STATIS_TYPE, USER_COUNT, CYCLE_TYPE) values(?,?,?,?,?,?,?)",
new BatchPreparedStatementSetter() {
public int getBatchSize() {
return list.size();
}
public void setValues(PreparedStatement ps, int i)
throws SQLException {
FlowBean bean = list.get(i);
ps.setString(1, bean.getInsertDate());
ps.setString(2, bean.getBeginTime().substring(8, 12));
ps.setString(3, bean.getAreaId());
ps.setString(4, bean.getPeoId());
ps.setString(5, bean.getStatisType());
ps.setString(6, bean.getTotalCount());
ps.setString(7, bean.getCycleType());
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
ok,特此記錄下
注:上傳不了檔案,需要核心原始碼的可以聯絡我。