Spring5(七)——AOP注解

一、AOP注解

1、介绍

  上一节介绍了 AspectJ 框架如何实现 AOP,具体的实现方式是通过 xml 来进行配置的。xml 方式思路清晰,便于理解,但是书写过于麻烦。这一节介绍注解的方式来进行 AOP 配置。

2、案例(注解)

  定义目标对象(被代理的对象)

 1 // 定义一个接口
 2 public interface ITeacher {
 3     void teach();
 4     int add(int i, int j);
 5 }
 6 
 7 // 定义目标对象
 8 @Service
 9 public class Teacher implements ITeacher {
10     @Override
11     public void teach() {
12         System.out.println("老师正在上课");
13     }
14 
15     @Override
16     public int add(int i, int j) {
17         int add = i + j;
18         System.out.println("执行目标方法:老师正在做加法,结果为:" + add);
19         // int throwable = 10 / 0; 测试异常通知
20         return add;
21     }
22 
23     // 目标对象自己的方法,此方法不是接口所以无法代理
24     public void sayHello() {
25         System.out.println("老师会说hello");
26     }
27 
28 }

  编写一个切面类(通知)

 1 // 创建切面类(包含各种通知)
 2 @Component
 3 @Aspect
 4 public class MyAspect {
 5 
 6     // 1.先定义切入点表达式
 7     @Pointcut("execution(* com.lx.spring.day4.ITeacher.*(..))")
 8     private void myPointcut() {
 9 
10     }
11 
12     // 2.标识此方法为一个前置通知,用来切满足后面切点表达式的方法
13     @Before("myPointcut()")
14     public void myBefore(JoinPoint joinPoint) {
15         System.out.println("前置通知:方法增强myBefore()" + " , -->" + joinPoint.getSignature().getName());
16     }
17 
18     @AfterReturning(value = "myPointcut()", returning = "object")
19     public void myAfterReturning(JoinPoint joinPoint, Object object) {
20         System.out.println("后置通知:方法增强myAfterReturning()" + " , -->" + joinPoint.getSignature().getName() + " , -->" + object);
21     }
22 
23     public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
24         System.out.println("============环绕前==============");
25         Object obj = joinPoint.proceed(); // 手动执行目标方法
26         System.out.println("============环绕后==============");
27         return obj;
28     }
29 
30     public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
31         System.out.println("抛出异常通知:" + e.getMessage());
32     }
33 
34     public void myAfter() {
35         System.out.println("最终通知:方法增强myAfter()");
36     }
37 
38 }

  编写配置文件 application.xml

1 <!-- 1.自动扫描(自动注入bean) -->
2 <context:component-scan base-package="com.lx.spring.day4"/>
3 
4 <!-- 2.扫描 @Aspect 告诉 spring 这是一个切面类 -->
5 <aop:aspectj-autoproxy/>
 1 // 测试类
 2 public class Main {
 3     public static void main(String[] args) {
 4         ApplicationContext app = new ClassPathXmlApplicationContext("app4.xml");
 5         ITeacher iTeacher = app.getBean(ITeacher.class);
 6 
 7         iTeacher.add(11, 24);
 8     }
 9 }
10 
11 // 结果
12 前置通知:方法增强myBefore() , -->add
13 执行目标方法:老师正在做加法,结果为:35
14 后置通知:方法增强myAfterReturning() , -->add , -->35

  说明:对比 xml 的配置,不难理解注解的方式。

  @Service @Component
  <context:component-scan base-package=”com.lx.spring.day4″/>

  用于 Spring 扫描并注册bean。

  @Aspect:指明这是一个切面类
  <aop:aspectj-autoproxy/>:开启切面注解扫描

3、优先级

  有多个增强类对同一个方法进行增强,设置增强类优先级,在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高。

1 @Component
2 @Aspect
3 @Order(1)
4 public class MyAspect2 {}

  优先级:这里的优先级,只会影响两个增强类对应的方法,执行的先后顺序。并不会只执行优先级高的。

二、AOP+自定义注解

  通过AOP+自定义注解的方式,可以实现前面说的抽取公共非业务模块,对业务逻辑的增强。比如:

  需求:①想要对业务逻辑层的所有方法,打印出入参和出参,做日志管理。②对业务逻辑层的方法入口,开启事务,逻辑执行后,提交事务,等。

  自定义注解

1 // 用于日志打印
2 @Target({ElementType.METHOD})
3 @Retention(RetentionPolicy.RUNTIME)
4 @Documented
5 public @interface Log {
6 
7     String value() default "";
8 }
  编写切面类(通知)
 1 @Component
 2 @Aspect
 3 public class MyAspect {
 4 
 5     // 定义切入点为 有注解Log的方法
 6     @Pointcut("@annotation(com.lx.spring.day5.Log) ")
 7     private void myLogPointcut() {
 8 
 9     }
10     
11     // 为切入点增强一个环绕通知,可以在这里写打印入参出参的逻辑
12     @Around("myLogPointcut()")
13     public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
14         System.out.println("============环绕前==============");
15         Object obj = joinPoint.proceed(); // 手动执行目标方法
16         System.out.println("============环绕后==============");
17         return obj;
18     }
19 
20 }
  在相应的方法上加注解
 1 @Service
 2 public class Teacher implements ITeacher {
 3 
 4     // 为需要打印入参出参的方法 加上@Log注解即可
 5     @Log
 6     @Override
 7     public int add(int i, int j) {
 8         int add = i + j;
 9         System.out.println("执行目标方法:老师正在做加法,结果为:" + add);
10         // int throwable = 10 / 0; 测试异常通知
11         return add;
12     }
13 
14 }
  测试类
 1 public class Main {
 2     public static void main(String[] args) {
 3         ApplicationContext app = new ClassPathXmlApplicationContext("app4.xml");
 4         ITeacher iTeacher = app.getBean(ITeacher.class);
 5 
 6         iTeacher.add(11, 24);
 7     }
 8 }
 9 
10 // 结果
11 ============环绕前==============
12 执行目标方法:老师正在做加法,结果为:35
13 ============环绕后==============

 

Tags: