在現在項目中注解應用越來越廣泛。為了有更深的理解,前面學習了java注解使用的一些原理,做了相關的總結和梳理,對注解有了更深的認識。趁熱打鐵,利用理解到的注解做點東西吧。結合日常工作中的一個點,利用注解做一些改造,也可以知道注解在實際項目中的用處。方便以后碰到相關情況可以利用。
廢話不多說,直入正題:
一般的管理系統中,都會有定時執行的任務,一般用於按一定規律進行統計。比如日,周,月的統計,業務邏輯不需要和人為結合的。這種情況就不需要在系統中做一個模塊功能讓用戶自己點擊觸發了。可以利用框架中的定時觸發器來做。設定時間,到點觸發執行。我們項目組中俗稱:“日終”,但並不准確。還是定時器比較好。
定時器實現現在比較流行的是:spring + quartz 的框架。應用起來也比較簡單:
1、定義需要定時觸發的業務類;
之后就是xml中的配置。
2、包裝業務類為定時器認識的類;
3、為需要定時出發的類,聲明一個定時器,並聲明出發時間;
4、將定時器注入到定時器的factory;
如下:
a、業務類:
/** * 業務類 * * @author yanbin * */ public class Job1 { public void execute() { System.out.println("job1 say"); } }
b、spring trigger.xml的配置
<!--1、 注入配置日終 --> <bean id="job1" class="trigger.Job1" /> <!--2、包裝業務類為定時器類 --> <bean id="task" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <!-- 調用的類--> <property name="targetObject"> <ref bean="job1"/> </property> <!-- 調用類中的方法 --> <property name="targetMethod"> <value>execute</value> </property> </bean> <!-- 3、定義觸發時間 --> <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail"> <ref bean="task"/> </property> <!-- cron定時表達式 --> <property name="cronExpression"> <value>0/3 * * * * ?</value> </property> </bean> <!-- 4、注入factory設置調度 --> <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="cronTrigger" /> </list> </property> </bean>
這樣就完成了一個定時器的實現,定時器會在spring容器啟動的時候同時啟動。在應用正常運行的情況下,到指定的時間調用業務類執行。
但是看了這樣的一個實現方式,如果再需要配置一個定時器,job2,配置業務bean,MethodInvokingJobDetailFactoryBean,CronTriggerBean,添加到SchedulerFactoryBean。同樣類似的配置。漸漸的在系統中業務復雜了,定時器需求越來越多,配置越來越多。這個trigger.xml將越來越龐大,可能會導致不好維護。
這個時候,注解就派上用場了。自己嘗試了利用注解對這個實現進行改造。利用的其實還是注解的基本原理來實現(mark前文)。主要思路:
1、定義注解
2、標記業務類,執行業務方法。(使用注解)
3、從spring容器中,獲取標記的業務類,業務方法。
4、繼承spring的trigger解析,重寫實現方法。注入到SchedulerFactoryBean中。(解析注解)
上碼:
a、定義兩個注解:
/** * 定時器標記類注解 * * @author yanbin * */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface TriggerType { String value() default ""; /** 定義定時器觸發時間 */ String cronExpression() default ""; }
/** * 定時器執行方法標記注解 * * @author yanbin * */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TriggerMethod { String value() default ""; }
b、業務類使用注解:
/** * 業務類 * * @author yanbin * */ @TriggerType(cronExpression = "0/3 * * * * ?") //指定定時時間 public class Job1 { @TriggerMethod public void execute() { System.out.println("job1 say"); } }
c、繼承重寫spring實現,加入解析注解
/** * 解析日終類 * * @author yanbin * */ public class MySchedulerFactoryBean extends SchedulerFactoryBean { /** 日志 */ protected Log log = LogFactory.getLog(MySchedulerFactoryBean.class.getName()); /** Spring 上下文 */ private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } @Override public void registerJobsAndTriggers() throws SchedulerException { try { // 獲取所有bean name String[] beanNames = applicationContext.getBeanNamesForType(Object.class); for (String beanName : beanNames) { Class<?> targetClass = applicationContext.getType(beanName); // 循環判斷是否標記了TriggerType注解 if (targetClass.isAnnotationPresent(TriggerType.class)) { Object targetObject = applicationContext.getBean(beanName); TriggerType triggerType = (TriggerType) targetClass.getAnnotation(TriggerType.class); // 獲取時間表達式 String cronExpression = triggerType.cronExpression(); String targetMethod = ""; // 確定標記了TriggerMethod注解的方法名 Method[] methods = targetClass.getDeclaredMethods(); for (Method method : methods) { if (method.isAnnotationPresent(TriggerMethod.class)) { targetMethod = method.getName(); } } // 注冊定時器業務類 registerJobs(targetObject, targetMethod, beanName, cronExpression); } } } catch (Exception e) { log.error(e); } } /** * 注冊定時器 * * @param targetObject * @param targetMethod * @param beanName * @param cronExpression * @throws Exception */ private void registerJobs(Object targetObject, String targetMethod, String beanName, String cronExpression) throws Exception { // 聲明包裝業務類 MethodInvokingJobDetailFactoryBean jobDetailFactoryBean = new MethodInvokingJobDetailFactoryBean(); jobDetailFactoryBean.setTargetObject(targetObject); jobDetailFactoryBean.setTargetMethod(targetMethod); jobDetailFactoryBean.setBeanName(beanName); jobDetailFactoryBean.afterPropertiesSet(); // 獲取JobDetail JobDetail jobDetail = jobDetailFactoryBean.getObject(); // 聲明定時器 CronTriggerBean cronTriggerBean = new CronTriggerBean(); cronTriggerBean.setJobDetail(jobDetail); cronTriggerBean.setCronExpression(cronExpression); cronTriggerBean.setBeanName(beanName + "CronBean"); cronTriggerBean.afterPropertiesSet(); // 將定時器注冊到factroy List<Trigger> triggerList = new ArrayList<Trigger>(); triggerList.add(cronTriggerBean); Trigger[] triggers = (Trigger[]) triggerList.toArray(new Trigger[triggerList.size()]); setTriggers(triggers); super.registerJobsAndTriggers(); } }
d、在spring的容器xml配置文件中,將這個解析類的bean放入容器中,開始就初始化。
<!-- 自定義設置計時器調度 --> <bean id="scheduler" class="trigger.MySchedulerFactoryBean"/>
搞定,這樣就可以了,啟動項目,定時器就會按時的執行。在以后如果在需要添加一個業務定時器,只需要定義類,並標注注解就行了,不需要額外的配置。維護起來只需關注定時器這個包下的類,方便維護。
水平有限,只是做這么個改造實現,可能考慮的還不夠多,例如:解析類中的代碼效率啊,資源損耗啊。但主要還是說明下,注解在系統中的應用。還是很多場合可以用到。任何事物都是有兩面性的,注解也是有利有弊。根據自己的項目,合理利用注解。
