AOP:面向切面,在不修改源码的情况下增加新的功能

使用动态代理+反射实现

1.底层使用动态代理,有两种情况:

1.有接口情况,使用JDK的动态代理,创建接口实现类代理对象,增强类方法(java基础有例子)
2.无接口,使用CGLIB动态代理,创建子类的代理对象,增强类方法
spring会根据要代理的类是否有接口动态选择代理方式

2.AOP术语

1.连接点
类中哪些方法可以被增强,这些方法就叫做连接点
2.切入点
实际上真正被增强的方法
3.通知(增强)
1.方法中实际增强的逻辑部分,也就是在原来基础上新增加的部分
2.通知有多种类型:
前置通知:在原方法之前执行
后置通知:在原方法之后执行
环绕通知:在原方法前后都执行
异常通知:在原方法出异常后执行
最终通知:类似于finally,即有无异常都会执行
4.切面
是一个动作,把通知应用到切入点的过程

3.AOP操作(准备)

1.spring一般是基于AspectJ实现AOP操作

AspectJ是独立的AOP框架,一般是AspectJ和Spring一起进行AOP操作

2.AspectJ实现AOP

1.基于XML
2.基于注解(常用)

3.引入依赖

image.png
image.png

4.切入点表达式

1.作用:知道对哪个类的哪个方法进行增强
2.语法:
execution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]) )
例1:对add方法增强
execution( com.dao.BookDao.add(..)):权限修饰可以省略,表示任意返回类型,(..)表示参数
例1:对BookDao中所有方法增强
execution( com.dao.BookDao.(..))

4.AOP操作(AspectJ注解)

1.创建类,定义方法

  1. public class User {
  2. public void add(){
  3. System.out.println();
  4. }
  5. }

2.创建增强类(要增强的逻辑)

  1. public class UserProxy {
  2. /**
  3. * 前置通知
  4. */
  5. public void before(){
  6. System.out.println("before");
  7. }
  8. }

3.进行通知的配置

1.开启注解扫描

  1. <!-- 开启注解扫描-->
  2. <context:component-scan base-package="com.spring5.aopAnnotation"/>

2.使用注解创建对象

  1. @Component
  2. public class User {
  3. public void add(){
  4. System.out.println();
  5. }
  6. }
  1. @Component
  2. public class UserProxy {
  3. /**
  4. * 前置通知
  5. */
  6. public void before(){
  7. System.out.println("before");
  8. }
  9. }

3.增强类上添加@Aspect注解

  1. @Aspect
  2. @Component
  3. public class UserProxy {
  4. /**
  5. * 前置通知
  6. */
  7. public void before(){
  8. System.out.println("before");
  9. }
  10. }

4.开启生成代理对象

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
  8. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
  9. <!-- 除了注解扫描的context名称空间外,还有个aop名称空间用于AspectJ实现AOP操作-->
  10. <!-- 开启注解扫描-->
  11. <context:component-scan base-package="com.spring5.aopAnnotation"/>
  12. <!-- 开启AspectJ自动生成代理对象-->
  13. <aop:aspectj-autoproxy/>
  14. </beans>

4.配置不同类型的通知

  1. /**
  2. * 前置通知
  3. */
  4. //before注解表示作为前置通知,值使用切入点表达式配置
  5. @Before(value = "execution(* com.spring5.aopAnnotation.User.add(..))")
  6. public void before() {
  7. System.out.println("before");
  8. }
  9. /**
  10. * 后置通知
  11. */
  12. @AfterReturning(value = "execution(* com.spring5.aopAnnotation.User.add(..))")
  13. public void afterReturning() {
  14. System.out.println("afterReturning");
  15. }
  16. /**
  17. * 最终通知,注意和@AfterReturning区别
  18. */
  19. @After(value = "execution(* com.spring5.aopAnnotation.User.add(..))")
  20. public void after() {
  21. System.out.println("after");
  22. }
  23. /**
  24. * 异常通知
  25. */
  26. @AfterThrowing(value = "execution(* com.spring5.aopAnnotation.User.add(..))")
  27. public void afterThrowing() {
  28. System.out.println("afterThrowing");
  29. }
  30. /**
  31. * 环绕通知
  32. */
  33. @Around(value = "execution(* com.spring5.aopAnnotation.User.add(..))")
  34. public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
  35. System.out.println("环绕之前...");
  36. //执行被增强的方法
  37. proceedingJoinPoint.proceed();
  38. System.out.println("环绕之后...");
  39. }

测试:

  1. /**
  2. * 测试AOP
  3. */
  4. @Test
  5. public void testAopAnno(){
  6. ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");
  7. //增强的哪个类,就获得哪个类的对象
  8. User user = context.getBean("user", User.class);
  9. user.add();
  10. }

image.pngimage.png

5.抽取公共切入点

  1. /**
  2. * 抽取公共切入点
  3. */
  4. @Pointcut(value = "execution(* com.spring5.aopAnnotation.User.add(..))")
  5. public void publicPoint(){
  6. }
  7. /**
  8. * 前置通知
  9. */
  10. //调用publicPoint()配置切入点
  11. @Before(value = "publicPoint()")
  12. public void before() {
  13. System.out.println("before");
  14. }

6.多个增强类对同一个方法增强,设置增强类优先级

在增强类上加注解@Order(数字值),越小优先级越高

  1. /**
  2. * 增强类2
  3. */
  4. @Component
  5. @Aspect
  6. @Order(3)
  7. public class PersonProxy {
  8. /**
  9. * 抽取公共切入点
  10. */
  11. @Pointcut(value = "execution(* com.spring5.aopAnnotation.User.add(..))")
  12. public void publicPoint() {
  13. }
  14. @Before(value = "publicPoint()")
  15. public void before() {
  16. System.out.println("person before");
  17. }
  18. }