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文件中有


	
而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 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,特此记录下

注:上传不了文件,需要核心源码的可以联系我。







你可能感兴趣的:(java心得)