1. 程式人生 > >Spring定時任務計劃中注入service、資料來源的問題

Spring定時任務計劃中注入service、資料來源的問題

前兩天寫了一個定時同步資料的問題,但是用著用著就發現問題了,由於我的定時任務用的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檔案中有
<!-- 自動掃描與裝配bean -->
	<context:component-scan base-package="com.zxtj"></context:component-scan>
而redis配置中引入了很多專案工廠類,例如這種

啟動專案時會報錯,原因是這種自動裝配的屬性沒有被定義或被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,特此記錄下

注:上傳不了檔案,需要核心原始碼的可以聯絡我。