spring AOP 初尝试

spring AOP 可使用注解方式,和配置方式来使用

注解方式:

一般这样创建一个 Aspect 的类如:

public class ClearCacheAspect{
       @Pointcut("execution(* com.kangzye.service.UserService.updatePwd(..)) &&args(name,passwd)")
       public void anyNameMethod(){}
       @Before("anyNameMethod()")
       public void doSomethingWhenBegin(Pointcut jp,String name,String passwd){
              System.out.println("begin to update pwd:"+passwd+" for user:"+name);
       }
       @After("anyNameMethod()")
       public void doSomethingWhenAfter(Pointcut jp,String name,String passwd){
              System.out.println("done");
       }
}

然后目标类及方法示例如:

package com.kangzye.service;
public class UserService{
       public void updatePwd(String name,String passwd){
             userDao.updatePwd(name,passwd);
       }
       @Resource
       private UserDao userDao;
}

 示例做的事情就是在更新前 输出更新内容到控制台,更新之后输出‘done’。

现在在看看 xml配置方式,最后在强调一些注意点。

Aspect类信息(没有了注解):

public class ClearCacheAspect{
       
       public void doSomethingWhenBegin(Pointcut jp,String name,String passwd){
              System.out.println("begin to update pwd:"+passwd+" for user:"+name);
       }

       public void doSomethingWhenAfter(Pointcut jp,String name,String passwd){
              System.out.println("done");
       }
}

UserService 类还是上面的不变。

配置文件如:

<bean id="clearCacheAspect" class="com.kangzye.aspect.ClearCacheAspect"/>
<aop:config>
     <aop:aspect id="idJust" ref="clearCacheAspect">
            <aop:before method="doSomethingWhenBegin" pointcut="execution(* com.kangzye.servcie.UserService.updatePwd(..)) and args(name,passwd)" />
            <aop:after method="doSomethingWhenAfter" pointcut="execution(* com.kangzye.servcie.UserService.updatePwd(..)) and args(name,passwd)" />
     </aop:aspect>
</aop:config>

 这样配置后,即可生效.

要点讲解:

Pointcut 的定义,可以单独提出来定义,也可以匿名定义,如类方式的时候,我是给出单独定义的,而配置文件方式,我是使用的匿名定义的。

其中 execution 是用来匹配目标方法,在示例中即为UserService 的updatePwd 方法。其表达式,请去百度找,一堆一堆的。

要点在于,我这示例中是需要在“通知方法”(即doSomethingWhenBegin和 doSomethingWhenAfter)中获得目标方法(示例中为 UserSerivce 的 updatePwd 方法)的输入参数,由于execution 无法传递参数给 “通知方法”,但是 args 却可以,所以示例中 的pointcut 中属性了 args(name,passwd) 。表示 目标方法必须有 name 和 passwd 2个参数。如果不需要在“通知方法”中使用这些参数,其实args(name,passwd) 是不需要写的。

绝对重点是:name 和 passwd 名字必须和 目标 方法的参数名字一样。这种要求,真是tmd真少见。表示,args中指定的参数名称,必须是和 UserService.udpatePwd 的参数名一样的。它是根据名称来匹配的。

execution(* com.kangzye.servcie.UserService.updatePwd(..)

这里的 .. 2个点,表示匹配任意个数参数。意思是匹配任意个参数的 updatePwd 方法。

另外,xml配置的方式,不可和类注解方式一样使用  && 来表示 “并且”关系,而是使用  and 来写。 || 则对于  or 。

我在真实使用中其实是想使用 自定义注释 方式来匹配目标方法(那样我就可以在任意方法上加上注释,即可表示我要切入它),但是捣鼓了好久不成功,最后发现问题是由于我测试的那个目标方法已经被其他切面切过了,导致我的自定义注解会丢失。从而导致我写的 aop 表达式不生效。

我测试时是这么写的:

public @interface ClearCache{}

public class UserService{
      @ClearCache
      public void updatePwd(String name, String passwd){
            //.............
      }
}

public class ClearCacheAspect{
      @Pointcut("@annotation(com.kangzye.ClearCache)")
      public void pointcutName(){}
      @Begein("pointcutName()")
      public void doSomething(){//..}
}

其实这样的写法是正确的。在所有的 标记了 @ClearCache 的方法上会进行切面植入。

我测试时出现的问题在于,另一个切面首先切入了此方法,导致切入之后的代理Service对象中对应的updatePwd 方法中并没有带上该注解,于是,注解方式没有生效。大家如遇到类似问题需特别注意。

 =============== 2015-05-17 跟新 ====================================================

经过多次次数,发现在用注解方式的时候,有比较奇怪的情况,相同的 切入点表达式,如果使用匿名方式则可以,如果使用命名切入点方式则不行:

命名切入点方式(错误)

public class LoggerAspecter{
      @Pointcut("@annotation(com.kangzye.annotation.LoggerIt) && args (form,id)")
      public void justName(){}
      @Before("justName()")
      public void doAspect(Pointcut jp,UserFrom form,String id){
            
      }
}
 匿名切入点方式(正确)
public class LoggerAspecter{  
      @Before("@annotation(com.kangzye.annotation.LoggerIt) && args (jp,form,id)")
      public void doAspect(Pointcut jp,UserFrom form,String id){  
           //dosomething  
      }  
}  

注意一个细节就是,匿名方式的切入点表达式并非完全一样,其中多了个 jp ,和方法的参数jp 是同名。如果不加jp 在前面,也报错。

但是奇怪的是,如果是使用配置文件的方式,则表达式不用加 jp 如:

<aop:config>
	<aop:aspect id="nameDictionary" ref="nameDictionaryClearAspect">
		<aop:before method="clearCacheInConfig" pointcut="execution(* com.kangzye.service.impls.AccountServiceImpl.updateAccount(..)) and args(form,..)" />
	</aop:aspect>
</aop:config>
 目前不知何解

猜你喜欢

转载自kangzye.iteye.com/blog/2211002