Spring5 AOP 学习笔记

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

AOP底层原理

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

有两种情况的动态代理

1.有接口的情况,使用JDK动态代理:创建接口实现类的代理对象,增强类的方法
Spring5 AOP 学习笔记_第1张图片2.没有接口的情况,使用CGLIB动态代理:创建子类的代理对象,增强类的方法
Spring5 AOP 学习笔记_第2张图片

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文件中,开启注解扫描


<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配置文件中开启生成代理对象

    
    <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 环绕之后...

你可能感兴趣的:(Spring,Spring,AOP)