java注解应用

在现在项目中注解应用越来越广泛。为了有更深的理解,前面学习了 java 注解使用的一些原理,做了相关的总结和梳理,对注解有了更深的认识。趁热打铁,利用理解到的注解做点东西吧。结合日常工作中的一个点,利用注解做一些改造,也可以知道注解在实际项目中的用处。方便以后碰到相关情况可以利用。

废话不多说,直入正题:

一般的管理系统中,都会有定时执行的任务,一般用于按一定规律进行统计。比如日,周,月的统计,业务逻辑不需要和人为结合的。这种情况就不需要在系统中做一个模块功能让用户自己点击触发了。可以利用框架中的定时触发器来做。设定时间,到点触发执行。我们项目组中俗称:“日终”,但并不准确。还是定时器比较好。

定时器实现现在比较流行的是:spring + quartz 的框架。应用起来也比较简单:

1、定义需要定时触发的业务类;

之后就是 xml 中的配置。

2、包装业务类为定时器认识的类;

3、为需要定时出发的类,声明一个定时器,并声明出发时间;

4、将定时器注入到定时器的 factory;

如下:

a、业务类:

/**
 * 业务类
 * 
 * @author yanbin
 * 
 */
public class Job1 {
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> execute() {
    System.out.println(</span>"job1 say"<span style="color: rgba(0, 0, 0, 1)">);
}

}

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() </span><span style="color: rgba(0, 0, 255, 1)">default</span> ""<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> 定义定时器触发时间 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
String cronExpression() </span><span style="color: rgba(0, 0, 255, 1)">default</span> ""<span>;

}

/**
 * 定时器执行方法标记注解
 * 
 * @author yanbin
 * 
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TriggerMethod {
String value() </span><span style="color: rgba(0, 0, 255, 1)">default</span> ""<span style="color: rgba(0, 0, 0, 1)">;

}

 

b、业务类使用注解:

/**
 * 业务类
 * 
 * @author yanbin
 * 
 */
@TriggerType(cronExpression = "0/3 * * * * ?") //指定定时时间
public class Job1 {
@TriggerMethod
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> execute() {
    System.out.println(</span>"job1 say"<span style="color: rgba(0, 0, 0, 1)">);
}

}

c、继承重写 spring 实现,加入解析注解

/**
 * 解析日终类
 * 
 * @author yanbin
 * 
 */
public class MySchedulerFactoryBean extends SchedulerFactoryBean {
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> 日志 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">protected</span> Log log = LogFactory.getLog(MySchedulerFactoryBean.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">.getName());

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> Spring 上下文 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> ApplicationContext applicationContext;

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setApplicationContext(ApplicationContext applicationContext) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.applicationContext =<span style="color: rgba(0, 0, 0, 1)"> applicationContext;
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> registerJobsAndTriggers() <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> SchedulerException {
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取所有bean name</span>
        String[] beanNames = applicationContext.getBeanNamesForType(Object.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
        </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (String beanName : beanNames) {
            Class</span>&lt;?&gt; targetClass =<span style="color: rgba(0, 0, 0, 1)"> applicationContext.getType(beanName);
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 循环判断是否标记了TriggerType注解</span>
            <span style="color: rgba(0, 0, 255, 1)">if</span> (targetClass.isAnnotationPresent(TriggerType.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)) {
                Object targetObject </span>=<span style="color: rgba(0, 0, 0, 1)"> applicationContext.getBean(beanName);
                TriggerType triggerType </span>= (TriggerType) targetClass.getAnnotation(TriggerType.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取时间表达式</span>
                String cronExpression =<span style="color: rgba(0, 0, 0, 1)"> triggerType.cronExpression();
                String targetMethod </span>= ""<span style="color: rgba(0, 0, 0, 1)">;
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 确定标记了TriggerMethod注解的方法名</span>
                Method[] methods =<span style="color: rgba(0, 0, 0, 1)"> targetClass.getDeclaredMethods();
                </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (Method method : methods) {
                    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (method.isAnnotationPresent(TriggerMethod.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)) {
                        targetMethod </span>=<span style="color: rgba(0, 0, 0, 1)"> method.getName();
                    }
                }
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 注册定时器业务类</span>

registerJobs(targetObject, targetMethod, beanName, cronExpression);
}
}
}
catch (Exception e) {
log.error(e);
}
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 注册定时器
 * 
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> targetObject
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> targetMethod
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> beanName
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> cronExpression
 * </span><span style="color: rgba(128, 128, 128, 1)">@throws</span><span style="color: rgba(0, 128, 0, 1)"> Exception
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> registerJobs(Object targetObject, String targetMethod, String beanName, String cronExpression)
        </span><span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> Exception {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 声明包装业务类</span>
    MethodInvokingJobDetailFactoryBean jobDetailFactoryBean = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> MethodInvokingJobDetailFactoryBean();
    jobDetailFactoryBean.setTargetObject(targetObject);
    jobDetailFactoryBean.setTargetMethod(targetMethod);
    jobDetailFactoryBean.setBeanName(beanName);
    jobDetailFactoryBean.afterPropertiesSet();

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取JobDetail</span>
    JobDetail jobDetail =<span style="color: rgba(0, 0, 0, 1)"> jobDetailFactoryBean.getObject();

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 声明定时器</span>
    CronTriggerBean cronTriggerBean = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> CronTriggerBean();
    cronTriggerBean.setJobDetail(jobDetail);
    cronTriggerBean.setCronExpression(cronExpression);
    cronTriggerBean.setBeanName(beanName </span>+ "CronBean"<span style="color: rgba(0, 0, 0, 1)">);
    cronTriggerBean.afterPropertiesSet();

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将定时器注册到factroy</span>
    List&lt;Trigger&gt; triggerList = <span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList&lt;Trigger&gt;<span style="color: rgba(0, 0, 0, 1)">();
    triggerList.add(cronTriggerBean);
    Trigger[] triggers </span>= (Trigger[]) triggerList.toArray(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Trigger[triggerList.size()]);
    setTriggers(triggers);
    </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.registerJobsAndTriggers();
}

}

d、在 spring 的容器 xml 配置文件中,将这个解析类的 bean 放入容器中,开始就初始化。

<!-- 自定义设置计时器调度 -->
<bean id="scheduler" class="trigger.MySchedulerFactoryBean"/>

搞定,这样就可以了,启动项目,定时器就会按时的执行。在以后如果在需要添加一个业务定时器,只需要定义类,并标注注解就行了,不需要额外的配置。维护起来只需关注定时器这个包下的类,方便维护。

水平有限,只是做这么个改造实现,可能考虑的还不够多,例如:解析类中的代码效率啊,资源损耗啊。但主要还是说明下,注解在系统中的应用。还是很多场合可以用到。任何事物都是有两面性的,注解也是有利有弊。根据自己的项目,合理利用注解。