使用AspectJ代替Spring自带的Aop,在Spring中启用AspectJ注解支持需要以下导入Jar包:
com.springsource.net.sf.cglib-2.20.jar(继承实现动态代理)
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
配置xml文件:
1 <!--组件扫描--> 2 <context:component-scan base-package="com.hxh.spring.aspectJ.annotation"/> 3 <!--基于注解使用AspectJ:主要作用是为切面中通知能作用到的目标类生成代理--> 4 <aop:aspectj-autoproxy/>
当Spring IOC容器检测到bean配置文件中的<aop:aspectj-autoproxy>时,会自动为AspectJ切面匹配的bean创建代理。
AspectJ支持的5种通知:
- 前置通知(@Before)<在方法执行之前执行>
- 后置通知(@After)<在方法执行之后执行>
- 环绕通知(@Around)<围绕着方法进行>
- 异常通知(@AfterThrowing)<在方法抛出异常之后执行>
- 返回通知(@AfterRunning)<围绕着方法执行>
前置和后置通知
1 /** 2 * 日志切面 3 */ 4 @Component //标识为一个组件,交给Spring的IOC管理 5 @Aspect//标识为一个切面 6 public class LoggingAspect { 7 /** 8 * 前置通知:在目标方法(连接点)执行之前执行。 9 * 10 */ 11 //找到目标方法(方法可能会重写,故要把参数写出来,前置通知只作用在add方法) 12 @Before("execution(public int com.hxh.spring.aspectJ.annotation.mathCalculatorImpl.add(int,int))") 13 public void beforeMethod(JoinPoint joinPoint){ 14 //方法前记录日志的通知 15 Object argss[]=joinPoint.getArgs(); 16 String methodName=joinPoint.getSignature().getName(); 17 //数组不好打印,转换成集合打印:格式为[,,,] 18 System.out.println("LoggingAspect--> The method "+methodName+"begin with "+ Arrays.asList(argss)); 19 } 20 /** 21 * 后置通知:在目标方法执行之后执行,不管目标方法有没有抛出异常,不能获取方法的结果 22 * ..表示任意数量,任意类型的参数 23 * 第一个*表示任意修饰符,任意返回值 24 * 第二个*表示任意的类 25 * 第三个*表示任意方法 26 * 27 * 连接点对象:JoinPoint 28 */ 29 @After("execution(* com.hxh.spring.aspectJ.annotation.*.*(..))") 30 public void afterMethod(JoinPoint joinPoint){ 31 //获取方法的名字: 32 String methodName=joinPoint.getSignature().getName(); 33 System.out.println("LoggingAspect--> The method "+methodName+"ends"); 34 } 35 }
Main.java
1 public class Main { 2 public static void main(String[] args) { 3 ApplicationContext ctx= 4 new ClassPathXmlApplicationContext("spring-aspectJ_annotation.xml"); 5 6 mathCalculator ac=ctx.getBean("mathCalculatorImpl",mathCalculator.class);//mathCalculatorImpl实现类 7 System.out.println(ac); 8 System.out.println(ac.getClass().getName()); 9 int result=ac.add(1,2); 10 System.out.println("result :"+result); 11 12 13 } 14 }
运行结果
com.hxh.spring.aspectJ.annotation.mathCalculatorImpl@5c4b com.sun.proxy.$Proxy8 LoggingAspect--> The method addbegin with [1, 2] LoggingAspect--> The method addends result :3
返回通知和异常通知
1 /** 2 * 返回通知:在目标方法正常执行结束后执行(也就是说有异常的时候不会执行)可以获取到方法的返回值。 3 *returning="xx",xx必须跟你方法中的一个形参名字一样,这样就会把返回值给到xx。 4 */ 5 @AfterReturning(value = "execution(* com.hxh.spring.aspectJ.annotation.*.*(..))",returning = "result") 6 public void afterReturningMethod(JoinPoint joinPoint,Object result){ 7 String methodName=joinPoint.getSignature().getName(); 8 System.out.println("LoggingAspect--> The method "+methodName+"end with "+result); 9 } 10 11 /** 12 * 异常通知:在目标方法抛出异常后执行 13 * 14 */ 15 @AfterThrowing(value = "execution(* com.hxh.spring.aspectJ.annotation.*.*(..))",throwing = "ex") 16 public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){ 17 //方法的名字 18 String methodName=joinPoint.getSignature().getName(); 19 System.out.println("LoggingAspect--> The method"+methodName+"occurs Exception: "+ex); 20 }
Main.java
result=ac.div(5,0);//by zero异常 System.out.println("result :"+result);
运行结果
LoggingAspect--> The method divends
Exception in thread "main" LoggingAspect--> The methoddivoccurs Exception: java.lang.ArithmeticException: / by zero
环绕通知(最强大)
1 /** 2 * 环绕通知:环绕着目标方法执行,可以理解为前置、后置、返回、异常通知的结合体,更像是动态代理的 3 * 整个过程。 4 * 5 */ 6 @Around("execution(* com.hxh.spring.aspectJ.annotation.*.*(..))") 7 public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){ 8 //执行目标方法 9 try { 10 //前置 11 Object result=proceedingJoinPoint.proceed(); 12 //返回 13 return result; 14 } catch (Throwable throwable) { 15 //异常通知 16 throwable.printStackTrace(); 17 }finally { 18 //后置通知 19 } 20 return null; 21 }
Tips:可以通过形参中异常的类型来设置抛出指定异常才会执行异常通知。比如说形参中写NullPointerException ex,只有
出现空指针异常才会执行异常通知。
ProceedingJoinPoin和joinPoint的关系
ProceedingJoinPoin extends joinPoint
Tips: @Order(1)//设置切面的优先级(int的最大值)2147483647,越小优先级越高
0x7fffffff 或者 Integer.MAX_VALUE可以表示int的最大值。二进制:01111111 11111111 11111111 11111111(高位是符号位,剩下的都是1)
@Pointcut("execution(* com.hxh.spring.aspectJ.annotation.*.*(..))")
public void declarePointCut(){}
这个切入点表达式可以用一个方法名来代替。例如:@AfterReturning(value="declarePointCut",returning="result")
在不同的类中使用方法名得申明是哪个类下的切入点表达式。例如在ValidationAspect.java中使用LoggingAspect中定义的切入点表达式 declarePointCut(){}
声明为:ValidationAspect.declarePointCut