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>目前不知何解