在1,2节里面我们已经大致了解了AOP的工作原理,以及Spring下AOP的配置与实现,BeanNameAutoProxyCreator,DefaultAdvisorAutoProxyCreator已经部分简化了AOP配置,然而还是很繁琐:
首先要编写xxxAdvice类(需要自己实现MethodBeforeAdvice、MethodAfterAdvice、ThrowsAdvice、MethodInterceptor接口之一),然后还要在xml中配置Advisor,还要在Advisor中注入Advice,最后还要将Advisor加入ProxyFactoryBean、BeanNameAutoProxyCreator或者DefaultAdvisorAutoProxyCreator中。
而Spring2.x以后引入了@AspectJ注解,可以通过Java5的注解以AspectJ的语法在Spring中配置AOP了,使用@AspectJ后,这一切都将变得异常简单:
只需要定义一个Aspect类,在Aspect中声明Advice(可以同时声明多个),然后在xml中配置这个Aspect,最后添加一行<aop:aspectj-autoproxy/>就可以搞定。
这种方式更加灵活、简单。
下面是一个Demo:
然后在Spring的xml文件中配置:
如果想添加更多的Advice,只需要在LoggerAspect类中声明一个类似logBefore的方法即可,而不是像之前的那样需要再重新编写一个Advice类。例如:
1,2节里面还介绍过一个“环绕通知”,也就是在方法的前后异常时都进行了增强,AspectJ 的@Around注解同样可以实现:
介绍完AspectJ的注解式AOP后,我们发现这样做还是有重复的代码:excution(* Service.login(username,..))
其实这就是一个Pointcut,我们可以在Logger类中声明一个方法为一个Pointcut,然后在后面的Advice中重用这个Pointcut:
这里简单介绍一下AspectJ的Pointcut,在Spring中使用最多的是excution切入点,匹配方法的执行,完整的表达式如下:
excution([修饰符] 返回类型 [声明类型] 方法名称 (参数类型) [异常类型])
其中返回类型、方法名称、参数类型是必须的,其他为可选的。
最常用的匹配模式是“*”,用来表示任意的返回类型或匹配部分命名模式。
“()”匹配一个无参数的方法。
“(..)”匹配一个含有任意个参数的方法。
“(String,*)”匹配含有两个参数的方法,首个参数类型为String。
“(String,..)”匹配至少含有一个参数的方法,首个参数类型为String。
匹配任意的public方法:
excution(public * *(..))
匹配任何以set开头的方法:
excution(* set*(..))
匹配Service接口中的方法:
excution(* *Service.*(..))
匹配会抛出IO异常的方法:
excution(* *(..) throws java.io.IOException)
一般情况下,若需要使用Spring AOP,最好使用AspectJ注解方式,这样代码更简单,配置更少,易于维护。
首先要编写xxxAdvice类(需要自己实现MethodBeforeAdvice、MethodAfterAdvice、ThrowsAdvice、MethodInterceptor接口之一),然后还要在xml中配置Advisor,还要在Advisor中注入Advice,最后还要将Advisor加入ProxyFactoryBean、BeanNameAutoProxyCreator或者DefaultAdvisorAutoProxyCreator中。
而Spring2.x以后引入了@AspectJ注解,可以通过Java5的注解以AspectJ的语法在Spring中配置AOP了,使用@AspectJ后,这一切都将变得异常简单:
只需要定义一个Aspect类,在Aspect中声明Advice(可以同时声明多个),然后在xml中配置这个Aspect,最后添加一行<aop:aspectj-autoproxy/>就可以搞定。
这种方式更加灵活、简单。
下面是一个Demo:
//定义一个切面
@Aspect
public class LoggerAspect{
@Before("excution(* Service.login(username,..))")
public void logBefore(String username){
System.out.println("[Logger] User " + username + "try to login");
}
}
然后在Spring的xml文件中配置:
<!--定义切面-->
<bean id="loggerAspect" class="com.xxx.LoggerAspect"/>
<!--定义业务逻辑-->
<bean id="userService" .../>
<!--配置Aspect支持-->
<aop:aspectj-autoproxy/>
如果想添加更多的Advice,只需要在LoggerAspect类中声明一个类似logBefore的方法即可,而不是像之前的那样需要再重新编写一个Advice类。例如:
@AfterReturning("excution(* Service.login(username,..))")
public void logSuc(String username){
System.out.println("[Logger] User " + username + "login successfully");
}
1,2节里面还介绍过一个“环绕通知”,也就是在方法的前后异常时都进行了增强,AspectJ 的@Around注解同样可以实现:
@Aspect
public class SecurityAspect{
@Around("excution(* Service.login(username,..))")
public Object securityCheck(ProceedingJionPoint pjp) throws Throwable{
//调用之前进行用户检查
String username=(String)pjp.getArgs()[0];
if(!"zhangsan".equals(username)){
//对除zhangsan之外的用户开放
return pjp.proceed();
}
//抛出异常,禁止zhangsan登陆
throw new RuntimeException("Reject login!");
}
}
介绍完AspectJ的注解式AOP后,我们发现这样做还是有重复的代码:excution(* Service.login(username,..))
其实这就是一个Pointcut,我们可以在Logger类中声明一个方法为一个Pointcut,然后在后面的Advice中重用这个Pointcut:
@Aspect
public class LoggerAspect{
//声明切入点
@Pointcut("excution(* Service.login(username,..))")
public void login(){};
@Before(value="login()")
public void logBefore(String username){
System.out.println("[Logger] User " + username + "try to login");
}
@AfterReturing(value="login()")
public void logSuc(String username){
System.out.println("[Logger] User " + username + "login successfully");
}
@AfterThrowing(pointcut="login()",throwing="e")
public void logFailure(RuntimeException e){
System.out.println("[Logger] Exception:" + e.getMessage());
}
}
这里简单介绍一下AspectJ的Pointcut,在Spring中使用最多的是excution切入点,匹配方法的执行,完整的表达式如下:
excution([修饰符] 返回类型 [声明类型] 方法名称 (参数类型) [异常类型])
其中返回类型、方法名称、参数类型是必须的,其他为可选的。
最常用的匹配模式是“*”,用来表示任意的返回类型或匹配部分命名模式。
“()”匹配一个无参数的方法。
“(..)”匹配一个含有任意个参数的方法。
“(String,*)”匹配含有两个参数的方法,首个参数类型为String。
“(String,..)”匹配至少含有一个参数的方法,首个参数类型为String。
匹配任意的public方法:
excution(public * *(..))
匹配任何以set开头的方法:
excution(* set*(..))
匹配Service接口中的方法:
excution(* *Service.*(..))
匹配会抛出IO异常的方法:
excution(* *(..) throws java.io.IOException)
一般情况下,若需要使用Spring AOP,最好使用AspectJ注解方式,这样代码更简单,配置更少,易于维护。