切面和自定义注解的配合使用

一.概述

       基于切面的编程(AOP)也许大家并不陌生,大家经常使用切面编程来解决事务问题。但是切面也有弊端,以笔者的经验看来,使用切面的不方便之处在于切面的精确度问题,我们很难把事务加到大量的指定类的指定方法上,本文就通过Spring AOP和自定义注解的配合使用来解决该问题。

二.Spring AOP切面的使用和问题

使用:
       以事务为例,我们在搭建J2EE项目框架时,势必要处理事务。通常我们的做法是给service层的DML操作方法增加事务,具体代码如下:

// 对数据源进行管理
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="add*"     propagation="REQUIRED"  rollback-for="Exception" />
        <tx:method name="save*"    propagation="REQUIRED"  rollback-for="Exception" />
        <tx:method name="insert*"  propagation="REQUIRED"  rollback-for="Exception" />
        <tx:method name="update*"  propagation="REQUIRED"  rollback-for="Exception" />
        <tx:method name="modify*"  propagation="REQUIRED"  rollback-for="Exception" />
        <tx:method name="delete*"  propagation="REQUIRED"  rollback-for="Exception" />
        <tx:method name="remove*"  propagation="REQUIRED"  rollback-for="Exception" />
    tx:attributes>
tx:advice>

    <aop:config proxy-target-class="true">
        <aop:advisor pointcut="execution(* com.etc..service.impl.*ServiceImpl.*(..))" advice-ref="txAdvice" />
    aop:config>

       通过上面的配置,可以实现事务的处理,但是这个配置却非常不灵活,他限制了类必须在service层,其次,方法名必须以add、save、insert等开头,才能被切面管理到,如果不符合这些规则改怎么办呢?
新的需求:

       在曾经的开发中遇到这样一个需求,系统中的部分业务方法在被调用到时,需要向log表插入日志,业务方法名没有规律,你不可能把所有的业务方法名都配置到上面,那么问题就来了,是否有一种语法可以在每个需要记录日志的方法中进行标识,让Spring AOP感知到,这就是下面要讲解的–自定义注解。

三.自定义注解

       Annotation(注解)是JDK1.5以后引入的新特性,它是以‘@注解名’的形式在代码中存在的,它可以出现在类、方法和属性的上方,一旦被标记了注解,那么在编译期或运行期就可以动态的获取哪些类、哪些方法或属性被标记了注解,从而针对这些类、属性和方法进行对应的处理,正式因为这个特性,才可以解决我们上面提到的问题。
如何自定义注解:

       在了解自定义注解钱,需要了解元注解。元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:

@Target
       @Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

@Retention
       @Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。例如我们常见的@Override注解,他的@Retention就在source阶段,因为他仅仅是针对方法覆盖进行语法检查,只要符合了规则即可,在运行阶段就不需要了。

@Documented
       @Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

@Inherited
       @Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

四.具体实现

注解代码:

@Target({ElementType.PARAMETER, ElementType.METHOD})    
@Retention(RetentionPolicy.RUNTIME)    
@Documented   
public @interface LogOperation {

}
   在代码中,我们定义了,改注解是方法级别,并且是在运行阶段有效的。

切面代码:

@Aspect
@Component
public class LogOperationAspect {

    Logger log = LoggerFactory.getLogger(LogOperationAspect.class);

    @Autowired
    ILogOperationService logOperationService;


    // 这里是关键点,把切面的连接点放在了我们的注解上
    @Pointcut("@annotation(com.etc.annotation.LogOperation)")
    public void controllerAspect() {
    }

    // 在这里定义前置切面
    @Before("controllerAspect()")
    public void beforeMethod(JoinPoint joinPoint) {
        // 这里执行保存日志的动作
        this.saveLogOperation(joinPoint, StatusEnum.SUCCESS);
    }
}

使用切面的代码:

// 只需要增加这样一个注解,就可以被切面管理
@LogOperation
@ResponseBody
@RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
public Map getDemo(@PathVariable Integer id, HttpServletRequest request,
        HttpServletResponse response) {
    DemoVO vo = this.demoService.selectById(id);
    return ResultMapper.convertSuccess(vo);
}

五.总结

       通过上面代码,我们可以看到,只要我们在期望记录日志的方法上增加@LogOperation注解,该方法的动作就会被记录进日志表,不管方法叫什么名字,类在什么位置,都可以轻松的解决,而且没有代码入侵,期望本篇博客对大家有所帮助。

你可能感兴趣的:(开源框架)