使用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)<围绕着方法执行>
前置和后置通知
/**
* 日志切面
*/
@Component //标识为一个组件,交给Spring的IOC管理
@Aspect//标识为一个切面
public class LoggingAspect {
/**
* 前置通知:在目标方法(连接点)执行之前执行。
*
*/
//找到目标方法(方法可能会重写,故要把参数写出来,前置通知只作用在add方法)
@Before("execution(public int com.hxh.spring.aspectJ.annotation.mathCalculatorImpl.add(int,int))")
public void beforeMethod(JoinPoint joinPoint){
//方法前记录日志的通知
Object argss[]=joinPoint.getArgs();
String methodName=joinPoint.getSignature().getName();
//数组不好打印,转换成集合打印:格式为[,,,]
System.out.println("LoggingAspect--> The method "+methodName+"begin with "+ Arrays.asList(argss));
}
/**
* 后置通知:在目标方法执行之后执行,不管目标方法有没有抛出异常,不能获取方法的结果
* ..表示任意数量,任意类型的参数
* 第一个*表示任意修饰符,任意返回值
* 第二个*表示任意的类
* 第三个*表示任意方法
*
* 连接点对象:JoinPoint
*/
@After("execution(* com.hxh.spring.aspectJ.annotation.*.*(..))")
public void afterMethod(JoinPoint joinPoint){
//获取方法的名字:
String methodName=joinPoint.getSignature().getName();
System.out.println("LoggingAspect--> The method "+methodName+"ends");
}
}
Main.java
public class Main {
public static void main(String[] args) {
ApplicationContext ctx=
new ClassPathXmlApplicationContext("spring-aspectJ_annotation.xml");
mathCalculator ac=ctx.getBean("mathCalculatorImpl",mathCalculator.class);//mathCalculatorImpl实现类
System.out.println(ac);
System.out.println(ac.getClass().getName());
int result=ac.add(1,2);
System.out.println("result :"+result);
}
}
运行结果
com.hxh.spring.aspectJ.annotation.mathCalculatorImpl@5c4b
com.sun.proxy.$Proxy8
LoggingAspect–> The method addbegin with [1, 2]
LoggingAspect–> The method addends
result :3
返回通知和异常通知
/**
* 返回通知:在目标方法正常执行结束后执行(也就是说有异常的时候不会执行)可以获取到方法的返回值。
*returning="xx",xx必须跟你方法中的一个形参名字一样,这样就会把返回值给到xx。
*/
@AfterReturning(value = "execution(* com.hxh.spring.aspectJ.annotation.*.*(..))",returning = "result")
public void afterReturningMethod(JoinPoint joinPoint,Object result){
String methodName=joinPoint.getSignature().getName();
System.out.println("LoggingAspect--> The method "+methodName+"end with "+result);
}
/**
* 异常通知:在目标方法抛出异常后执行
*
*/
@AfterThrowing(value = "execution(* com.hxh.spring.aspectJ.annotation.*.*(..))",throwing = "ex")
public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
//方法的名字
String methodName=joinPoint.getSignature().getName();
System.out.println("LoggingAspect--> The method"+methodName+"occurs Exception: "+ex);
}
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
环绕通知(最强大)
/**
* 环绕通知:环绕着目标方法执行,可以理解为前置、后置、返回、异常通知的结合体,更像是动态代理的
* 整个过程。
*
*/
@Around("execution(* com.hxh.spring.aspectJ.annotation.*.*(..))")
public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){
//执行目标方法
try {
//前置
Object result=proceedingJoinPoint.proceed();
//返回
return result;
} catch (Throwable throwable) {
//异常通知
throwable.printStackTrace();
}finally {
//后置通知
}
return null;
}
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*