注解版AOP怎么玩?#
AOP面向切面编程,指的是在程序运行期间,动态的将某段代码,切入到指定方法的指定位置额运行的编程方式
导入AOP模块#
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.3.7.RELEASE</version> </dependency>
创建业务逻辑类#
- 我们要做的就是在动态的在div运行前,运行后,出现异常时,正常结束,异常结束等不同情况下打印日志
public class MathAop { public int div(int a,int b){ return a-b; } }
定义一个日志切面类#
切面类上的方法会动态的感知到,切入点div的状态,在其指定的状态上,做出不同的反应而无序更改仍和div的代码
五种通知方法,对应五种不同的注解,具体的细节,在下面的代码中有注释
@Component @Aspect public class AopLog {
抽取切入表达式#
- 注解
@Pointcut
- 表达式可以使用* 通配
- execution(pulbic com.changwu.tryspring.aop.MathAop.*(int,int))
- execution(* com.changwu.tryspring.aop.MathAop.div(..))
- 本类使用 : 直接用方法名--> pointCut()
- 其他类使用: 使用带包名的全路径--> com.changwu.tryspring.aop.AopLog.pointCut()
@Pointcut("execution(* com.changwu.tryspring.aop.MathAop.div(..))") public void pointCut(){}
前置通知#
- 注解
@Before("切入点表达式")
- 调用时机: 在目标方法执行前执行
- 可选参数: JoinPoint,里面封装着切入点方法的全部信息,比如方法名,参数等等
@Before("com.changwu.tryspring.aop.AopLog.pointCut()") public void before(JoinPoint joinPoint){ Object[] args = joinPoint.getArgs(); // 获取参数 String name = joinPoint.getSignature().getName(); // 获取方法名 System.out.println("前置通知....参数:"+ Arrays.asList(args)+" 方法名: "+name); }
后置通知#
- 注解
@After("切入点表达式")
- 调用时机:无论方法正常结束还是异常结束,都调用
@After("execution(* com.changwu.tryspring.aop.MathAop.div(..))") public void after(){ System.out.println("后置通知执行了!!!"); }
返回通知#
- 注解
@AfterReturning(value="切入点表达式",returning="XXX")
- 调用时机: 方法正常返回执行
- 注意点: 函数的入参,参数名和注解中的XXX要相同,里面封装着,函数的返回值结果
@AfterReturning(value = "com.changwu.tryspring.aop.AopLog.pointCut()",returning = "result") public void returning(Object result){ System.out.println("切入点正常执行...运行结果..{"+result+"}"); }
异常通知#
- 注解
@AfterThrowing(value="切入点表达式",throwing="XXX")
- 调用时机: 切入点出现异常
- 注意点:函数的入参,参数名和注解中的XXX要相同,里面封装着,函数的返回值结果
- XXX里面封装着方法的异常信息
@AfterThrowing(value = "com.changwu.tryspring.aop.AopLog.pointCut()",throwing = "expection") public void afterThrowing(Exception expection){ System.out.println("切入点出现异常,异常结果..{"+expection+"}.."); }
环绕通知#
- 注解
@Around("切入点表达式")
- 调用时机: 切入点执行前后都会执行
- 参数:
ProceedingJoinPoint
里面封装了关于切入点的所有信息 - proceedingJoinPoint.proceed();返回值: 为切入点的返回值, 必须返回
- 环绕通知里面的所有异常全部抛出去,,一旦我们try起来了,异常通知就获取不到异常,进而返回通知就认为方法是正常结束的,结果为NULL
@Around("com.changwu.tryspring.aop.AopLog.pointCut()") public Object arounding(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕通知开始: 方法名: "+ proceedingJoinPoint.getSignature().getName() + "将要被执行 参数为: "+Arrays.asList(proceedingJoinPoint.getArgs())); Object result = proceedingJoinPoint.proceed();// 代理调用方法,如过调用方法add抛出异常,就不会执行后面的代码(所以要抛出去!) // 调用方法之后执行 System.out.println("环绕通知,调用方法之后执行,获取的结果是:"+result); return result; } }
注意点: 业务逻辑类,切面类都要添加进IOC
注意点: 切面类都要添加注解
@Aspects
补充: JointPoint 可以获取的切入点的信息 而且,必须在参数的第一位
好继续准备,回顾xml版本的spring开发方式需要我们添加如下的配置
<aop:aspectj-autoproxy></aop:aspectj-autopeoxy>
基于注解,开启切面--->@EnableAspectjAutoProxy
#
@Configuration @EnableAspectJAutoProxy @ComponentScan("com.changwu.tryspring.aop") public class MainAopConfig { }
测试:#
@Test public void text14(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainAopConfig.class); // 根据类型获取bean MathAop mathAop = applicationContext.getBean(MathAop.class); mathAop.div(2,1); applicationContext.close(); }