Spring5 AOP 学习笔记

Posted Shinka_YXS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring5 AOP 学习笔记相关的知识,希望对你有一定的参考价值。

尚硅谷-Spring5框架最新版教程(idea版)- AOP P25~P32

AOP底层原理

用动态代理增强类中某个方法的功能

有两种情况的动态代理

1.有接口的情况,使用JDK动态代理:创建接口实现类的代理对象,增强类的方法
在这里插入图片描述2.没有接口的情况,使用CGLIB动态代理:创建子类的代理对象,增强类的方法
在这里插入图片描述

AOP底层原理(JDK动态代理实现)

1、使用JDK动态代理,使用java.lang.reflect.Proxy类里面的方法创建代理对象

该类中有个方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)返回接口实现类的代理对象
参数loader是 类加载器
参数interfaces是 增强方法所在的类 实现的接口,支持传入多个接口
参数h是 实现接口InvocationHandler,创建代理对象,写增强的方法

2、编写JDK动态代理代码

(1)创建接口 定义方法

public interface UserInterface {
    public int add(int a, int b);
    public String update(String id);
}

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

public class UserImpl implements UserInterface {

    @Override
    public int add(int a, int b) {
        System.out.println("add方法开始执行...");
        return a + b;
    }

    @Override
    public String update(String id) {
        System.out.println("update方法开始执行...");
        return id;
    }
}

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

public class MyJDKProxy {

    public static void main(String[] args) {
        
        // 创建接口实现类的代理对象

        Class[] interfaces = {UserInterface.class};

//        // 方式一:匿名内部类
//        Proxy.newProxyInstance(MyJDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
//            @Override
//            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//                return null;
//            }
//        });

        // 方式二:创建一个类UserInterfaceProxy实现接口InvocationHandler接口
        UserImpl userImpl = new UserImpl();
        UserInterface userInterface = (UserInterface) Proxy.newProxyInstance(MyJDKProxy.class.getClassLoader(), interfaces, new UserInterfaceProxy(userImpl));
        int result = userInterface.add(1, 2);
        System.out.println("result:" + result);
    }
}

class UserInterfaceProxy implements InvocationHandler {
    // 1.创建的是谁的代理对象 就把谁传递过来
    // 有参构造传递
    private Object obj;
    public UserInterfaceProxy(Object obj) {
        this.obj = obj;
    }

    // 增强的逻辑
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 方法执行前
        System.out.println("方法执行前===传入的方法名称:" + method.getName() + "===传递的参数:" + Arrays.toString(args));

        // 被增强的方法执行
        Object res = method.invoke(obj, args);

        // 方法执行后
        System.out.println("方法执行之后===返回结果:" + res);

        return res;
    }
}

(4)执行MyJDKProxy类中的main()方法

方法执行前===传入的方法名称:add===传递的参数:[1, 2]
add方法开始执行...
方法执行之后===返回结果:3
result:3

AOP操作术语

1、连接点:在一个类中可以被增强的方法

2、切入点:实际真正被增强的方法

3、通知(增强):实际增强的逻辑部分。通知有多重类型(前置、后置、环绕、异常、最终)

4、切面:是个动作,把通知应用到切入点的过程

AOP操作-准备工作

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

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

2、基于AspectJ实现AOP操作:xml配置文件 或者 注解

3、引入AOP相关依赖

4、切入点表达式

作用:知道对哪个类里面的哪个方法进行增强

语法结构:excution([权限修饰符][返回类型][类全路径][方法名称]([参数列表]))

权限修饰符可以省略(默认public),返回类型不能省略

举例1:对com.shinka.spring5test.UserInterface类里面的add进行增强

excution(* com.shinka.spring5test.UserInterface.add(..)) 注意 中间有个空格

举例2:对com.shinka.spring5test.UserInterface类里面的所有进行增强

excution(* com.shinka.spring5test.UserInterface.*(..))

举例3:对com.shinka.spring5test包里面所有类的所有方法进行增强

excution(* com.shinka.spring5test.*.*(..))

AOP操作-AspectJ注解

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

public class User {
    public void add() { System.out.println("add..."); }
}

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

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

public class UserProxy {
    public void before() { System.out.println("before..."); }
}

3 进行通知的配置

(1)在Spring文件中,开启注解扫描

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.shinka.spring5test.aopanno"></context:component-scan>
    
</beans>

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

在User和UserProxy类添加注解@Component

// 被增强的类
@Component
public class User {
    public void add() { System.out.println("add..."); }
}

// 增强的类
@Component
public class UserProxy {
    // 前置通知
    public void before() { System.out.println("before..."); }
}

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

// 增强的类
@Component
@Aspect		// 在增强类上面添加注解@Aspect,让它能够生成代理对象
public class UserProxy {
    ...
}

(4)在Spring配置文件中开启生成代理对象

    <!-- 开启Aspectj生成代理对象 -->
    <aop:aspectj-autoproxy/>

4 配置不同类型的通知

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

// 前置通知
@Before(value = "execution(* com.shinka.spring5test.aopanno.User.add(..))")
public void before() { System.out.println("before..."); }

// 后置通知/返回通知。被增强的方法有异常则不执行
@AfterReturning(value = "execution(* com.shinka.spring5test.aopanno.User.add(..))")
public void afterReturning() { System.out.println("afterReturning..."); }

// 最终通知。不管被增强的方法有无异常,都在方法执行之后执行
@After(value = "execution(* com.shinka.spring5test.aopanno.User.add(..))")
public void after() { System.out.println("after..."); }

// 异常通知
@AfterThrowing(value = "execution(* com.shinka.spring5test.aopanno.User.add(..))")
public void afterThrowing() { System.out.println("afterThrowing..."); }

// 环绕通知。被增强的方法有异常则"环绕之后"不执行
@Around(value = "execution(* com.shinka.spring5test.aopanno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    System.out.println("around 环绕之前...");
    // 被增强的方法执行
    proceedingJoinPoint.proceed();
    System.out.println("around 环绕之后...");
}

5 测试结果

spring5底层变了之后 通知顺序是:环绕前 - 前置 - afterReturning - 后置 - 环绕后

被增强的方法有异常的话,环绕后、afterReturning不执行、

public class TestAop {
    @Test
    public void testAopAnno() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        User user = context.getBean("user", User.class);
        user.add();
    }
}
【运行结果】

around 环绕之前...
before...
add...
afterReturning...
after...
around 环绕之后...
若add()方法内部有异常 执行结果是:

around 环绕之前...
before...
afterThrowing...
after...

6 相同切入点抽取

重用 切入点的定义

// 相同切入点抽取
@Pointcut(value = "execution(* com.shinka.spring5test.aopanno.User.add(..))")
public void pointDemo() {}

// 直接使用定义的方法名
@Before(value = "pointDemo()")

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

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

@Order(1)
public class UserProxy

// 仿照UserProxy新建PersonProxy类
@Order(5)
public class PersonProxy
【运行结果】

User Around 环绕之前...
User Before...
Person Around 环绕之前...
Person Before...
add...
Person AfterReturning...
Person After...
Person Around 环绕之后...
User AfterReturning...
User After...
User Around 环绕之后...

以上是关于Spring5 AOP 学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

Spring5学习笔记 — “Spring AOP底层原理(动态代理)”

Spring5学习笔记 — “Spring AOP底层原理(动态代理)”

Spring5学习笔记 — “AOP操作—AspectJ注解”

Spring5学习笔记 — “AOP操作—AspectJ注解”

Spring5 AOP 学习笔记

Spring | Spring5学习笔记 #yyds干货盘点#