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 ============环绕后==============