什么是 AOP

(1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得
业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
(3)使用登录例子说明 AOP
图3.png

AOP(底层原理)

1、AOP 底层使用动态代理
(1)有两种情况动态代理
第一种 有接口情况,使用 JDK 动态代理
创建接口实现类代理对象,增强类的方法
图4.png

第二种 没有接口情况,使用 CGLIB 动态代理
创建子类的代理对象,增强类的方法


AOP(JDK 动态代理)

1、使用 JDK 动态代理,使用 Proxy 类里面的方法创建代理对象
QQ截图20220419202116.png
(1)调用 newProxyInstance 方法
QQ截图20220419202145.png
方法有三个参数:
第一参数,类加载器
第二参数,增强方法所在的类,这个类实现的接口,支持多个接口
第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分
2、编写 JDK 动态代理代码
(1)创建接口,定义方法

  1. public interface UserDao {
  2. public abstract int add(int a,int b);
  3. public abstract String update(String id);
  4. }

(2)创建接口实现类,实现方法

  1. public class UserDaoImpl implements UserDao {
  2. @Override
  3. public int add(int a, int b) {
  4. return a+b;
  5. }
  6. @Override
  7. public String update(String id) {
  8. return id;
  9. }
  10. }

(3)使用 Proxy 类创建接口代理对象

  1. package com.atguigu.spring5;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. import java.util.Arrays;
  6. public class JDKProxy {
  7. public static void main(String[] args) {
  8. //创建接口实现类代理对象
  9. Class[] interfaces = {UserDao.class};
  10. // Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
  11. // @Override
  12. // public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  13. // return null;
  14. // }
  15. // });
  16. UserDaoImpl userDao = new UserDaoImpl();
  17. UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
  18. int result = dao.add(1, 2);
  19. System.out.println("result"+result);
  20. }
  21. }
  22. //创建代理对象代码
  23. class UserDaoProxy implements InvocationHandler {
  24. //1 把创建的是谁的代理对象,把谁传递过来
  25. //有参数构造传递
  26. private Object obj;
  27. public UserDaoProxy(Object obj) {
  28. this.obj = obj;
  29. }
  30. //增强的逻辑
  31. @Override
  32. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  33. //方法之前
  34. System.out.println("方法之前执行...."+method.getName()+" :传递的参数..."+ Arrays.toString(args));
  35. //被增强的方法执行
  36. Object res = method.invoke(obj, args);
  37. //方法之后
  38. System.out.println("方法之后执行...."+obj);
  39. return res;
  40. }
  41. }

QQ截图20220419212438.png

AOP(术语)

1、连接点

类里面哪些方法可以被增强,这些方法称为连接点

2、切入点

实际被真正增强的方法,称为切入点

3、通知(增强)

(1)实际增强的逻辑部分称为通知(增强)
(2)通知有多钟类型
前置通知
后置通知
环绕通知
异常通知
最终通知

4、切面

是动作
把通知应用到切入点过程

图5.png

AOP 操作(准备工作)

1、Spring 框架一般都是基于 AspectJ 实现 AOP 操作

(1)AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使
用,进行 AOP 操作

2、基于 AspectJ 实现 AOP 操作

(1)基于 xml 配置文件实现
(2)基于注解方式实现(使用)

3、在项目工程里面引入 AOP 相关依赖

QQ截图20220420090246.png

4、切入点表达式

(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构: execution([权限修饰符] [返回类型] [类全路径] 方法名称 )
举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强
execution( com.atguigu.dao.BookDao.add(..))
表示任意的修饰符,返回类型可以省略,..表示方法中的参数。

举例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强
execution( com.atguigu.dao.BookDao. (..))

举例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强
execution( com.atguigu.dao..* (..))

AOP 操作(AspectJ 注解)

1、创建类,在类里面定义方法

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

2、创建增强类(编写增强逻辑)

(1)在增强类里面,创建方法,让不同方法代表不同通知类型

  1. //增强的类
  2. public class UserProxy {
  3. public void before(){//前置通知
  4. System.out.println("before......");
  5. }
  6. }

3、进行通知的配置

(1)在 spring 配置文件中,开启注解扫描

  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
  7. http://www.springframework.org/schema/beans/spring-beans.xsd
  8. http://www.springframework.org/schema/context
  9. http://www.springframework.org/schema/context/spring-context.xsd
  10. http://www.springframework.org/schema/aop
  11. http://www.springframework.org/schema/aop/spring-aop.xsd">
  12. <!-- 开启注解扫描 -->
  13. <context:component-scan base-package="com.atguigu.spring5.aopanno"></context:component-scan>

(2)使用注解创建 User 和 UserProxy 对象

  1. //被增强的类
  2. @Component
  3. public class User {
  4. public void add(){
  5. System.out.println("add......");
  6. }
  7. }
  8. //增强的类
  9. @Component
  10. public class UserProxy {
  11. public void before(){//前置通知
  12. System.out.println("before......");
  13. }
  14. }

(3)在增强类上面添加注解 @Aspect

  1. //增强的类
  2. @Component
  3. @Aspect //生成代理对象
  4. public class UserProxy {
  5. public void before(){//前置通知
  6. System.out.println("before......");
  7. }
  8. }

@Aspect:作用是把当前类标识为一个切面供容器读取
(4)在 spring 配置文件中开启生成代理对象

  1. <!-- 开启 Aspect 生成代理对象-->
  2. <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

4、配置不同类型的通知

(1)在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置

  1. package com.atguigu.spring5.aopanno;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.*;
  4. import org.springframework.stereotype.Component;
  5. //增强的类
  6. @Component
  7. @Aspect //生成代理对象
  8. public class UserProxy {
  9. //前置通知
  10. //@Before 注解表示作为前置通知
  11. @Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
  12. public void before() {
  13. System.out.println("before.........");
  14. }
  15. //后置通知(返回通知),方法返回值之后执行,方法有异常不执行
  16. @AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
  17. public void afterReturning() {
  18. System.out.println("afterReturning.........");
  19. }
  20. //最终通知,方法之后执行
  21. @After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
  22. public void after() {
  23. System.out.println("after.........");
  24. }
  25. //异常通知
  26. @AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
  27. public void afterThrowing() {
  28. System.out.println("afterThrowing.........");
  29. }
  30. //环绕通知
  31. @Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
  32. public void around(ProceedingJoinPoint proceedingJoinPoint) throws
  33. Throwable {
  34. System.out.println("环绕之前.........");
  35. //被增强的方法执行
  36. proceedingJoinPoint.proceed();
  37. System.out.println("环绕之后.........");
  38. }
  39. }

QQ截图20220420135754.png

5、相同的切入点抽取

  1. //相同切入点抽取
  2. @Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
  3. public void pointdemo() {
  4. }
  5. //前置通知
  6. //@Before 注解表示作为前置通知
  7. @Before(value = "pointdemo()")
  8. public void before() {
  9. System.out.println("before.........");
  10. }

@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
————————————————
版权声明:本文为CSDN博主「狂丰」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/fz13768884254/article/details/83538709
不要忘记小括号哦

6、有多个增强类对同一个方法进行增强,设置增强类优先级

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

  1. @Component
  2. @Aspect
  3. @Order(1)
  4. public class PersonProxy {
  5. @Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
  6. public void afterReturning() {
  7. System.out.println("Person ......");
  8. }
  9. }

7、完全使用注解开发

(1)创建配置类,不需要创建 xml 配置文件

  1. @Configuration
  2. @ComponentScan(basePackages = {"com.atguigu"})
  3. @EnableAspectJAutoProxy(proxyTargetClass = true)
  4. public class ConfigAop {
  5. }

proxyBeanMethods属性默认值是true,也就是说该配置类会被代理(CGLIB),在同一个配置文件中调用其它被@Bean注解标注的方法获取对象时会直接从IOC容器之中获取;
注解的意思是proxyBeanMethods配置类是用来指定@Bean注解标注的方法是否使用代理,默认是true使用代理,直接从IOC容器之中取得对象;如果设置为false,也就是不使用注解,每次调用@Bean标注的方法获取到的对象和IOC容器中的都不一样,是一个新的对象,所以我们可以将此属性设置为false来提高性能;
————————————————
版权声明:本文为CSDN博主「艾米莉Emily」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yaomingyang/article/details/108238121

AOP 操作(AspectJ 配置文件)

1、创建两个类,增强类和被增强类,创建方法

  1. public class Book {
  2. public void buy(){
  3. System.out.println("buy......");
  4. }
  5. }
  1. public class BookProxy {
  2. public void before(){
  3. System.out.println("before......");
  4. }
  5. }

2、在 spring 配置文件中创建两个类对象

  1. <!--创建对象-->
  2. <bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean>
  3. <bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>

3、在 spring 配置文件中配置切入点

  1. <!--配置 aop 增强-->
  2. <aop:config>
  3. <!--切入点-->
  4. <aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopxml.Book.buy(..))"/>
  5. <!--配置切面-->
  6. <aop:aspect ref="bookProxy">
  7. <!--增强作用在具体的方法上-->
  8. <aop:before method="before" pointcut-ref="p"/>
  9. <!-- method对应的是增强类中的增强方法,pointcut-ref对应切入点-->
  10. </aop:aspect>
  11. </aop:config>