Spring原理学习(六):Spring实现动态代理时对jdk和cglib的选择

目录

〇、前言

一、AOP中的一些基本概念 

二、两个切面的概念

三、advisor的使用

3.1 前置知识 

3.2 使用步骤

四、spring对jdk和cglib的统一 


〇、前言

        对jdk和cglib 实现动态代理的原理不清楚的兄弟们,可以参考前文:Spring原理学习(五):一篇讲清楚动态代理(jdk和cglib)的使用、原理和源码_玉面大蛟龙的博客-CSDN博客

        spring当中不需要我们直接去用jdk或者cglib,它提供了ProxyFactory来方便地创建代理,那么他如何选择代理方法呢?我打算通过AOP中的切面来讲解这一部分。 

一、AOP中的一些基本概念 

        复习一下AOP基本的概念,对AOP不熟悉的可以参考我的博客:Spring5学习(七):注解方式进行AOP操作 及 多种通知类型的测试_玉面大蛟龙的博客 

        切点相当于匹配规则。并不是所有方法都要增强,符合切点的方法才会增强。

        通知就是增强的逻辑。

        切面 = 切点 + 通知。

二、两个切面的概念

  • aspect:可以包含多个切面
aspect = 
    通知1(advice) + 切点1(pointcut)
    通知2(advice) + 切点2(pointcut)
    通知3(advice) + 切点3(pointcut)
    ...
  • advisor:更细粒度的切面。包含一个通知和一个切点

        aspect在底层逻辑中也是拆分为多个advisor进行处理的,所以我们就使用更细粒度的advisor吧。

三、advisor的使用

3.1 前置知识 

        这是Adivsor的简要的类图。Adivsor是由切点(Pointcut)和Advice(通知)组成,Pointcut 和 Advice都有极其丰富的实现,我们选熟悉的来使用。

Spring原理学习(六):Spring实现动态代理时对jdk和cglib的选择_第1张图片

        下图是PointCut的实现,我们选择 AspectJExpressionPointcut (根据AspectJ表达式的切点)来实现。

Spring原理学习(六):Spring实现动态代理时对jdk和cglib的选择_第2张图片

        下图是Advice的实现,我们选取 MethodInterceptor 来实现。本质上它是一种环绕通知。

        注意,这里的 MethodInterceptor 是 spring使用的 MethodInterceptor,包名为org.aopalliance.intercept.MethodInterceptor,跟我们前面介绍的cglib中的不是一回事,只是同名而已。 

 Spring原理学习(六):Spring实现动态代理时对jdk和cglib的选择_第3张图片

3.2 使用步骤

        使用切面的步骤如下:

  1. 备好切点
  2. 备好通知
  3. 备好切面
  4. 创建代理
public class A15 {
    public static void main(String[] args) {
        /*
            两个切面概念
            aspect =
                通知1(advice) +  切点1(pointcut)
                通知2(advice) +  切点2(pointcut)
                通知3(advice) +  切点3(pointcut)
                ...
            advisor = 更细粒度的切面,包含一个通知和切点
         */

        // 1. 备好切点
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        // 匹配所有类中的foo方法,因此Target中的bar方法不会被匹配到
        pointcut.setExpression("execution(* foo())");

        // 2. 备好通知
        MethodInterceptor advice = invocation -> {
            // 前置增强
            System.out.println("before...");
            // 调用目标
            Object result = invocation.proceed();
            // 后置增强
            System.out.println("after...");
            return result;
        };

        // 3. 备好切面
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);

        // 4. 创建代理
        Target1 target = new Target1();
        // 这里不需要我们直接去用jdk或者cglib,spring提供了ProxyFactory来方便地创建代理
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvisor(advisor);
        I1 proxy = (I1) factory.getProxy();
        System.out.println(proxy.getClass());
        proxy.foo();
        proxy.bar();
    }

    interface I1 {
        void foo();

        void bar();
    }

    /**
     * Target1实现了接口
     */
    static class Target1 implements I1 {
        public void foo() {
            System.out.println("target1 foo");
        }

        public void bar() {
            System.out.println("target1 bar");
        }
    }

    /**
     * Target2没有实现接口
     */
    static class Target2 {
        public void foo() {
            System.out.println("target2 foo");
        }

        public void bar() {
            System.out.println("target2 bar");
        }
    }
}

        运行后发现,使用的代理方式是cglib。那么,spring是如何选择代理方式的呢? 

 Spring原理学习(六):Spring实现动态代理时对jdk和cglib的选择_第4张图片

四、spring对jdk和cglib的统一 

         先说结论:

  • proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
  • proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现
  • proxyTargetClass = true, 总是使用 cglib 实现

        介绍一下 ProxyTargetClass:它是ProxyFactory的父类的一个属性:

Spring原理学习(六):Spring实现动态代理时对jdk和cglib的选择_第5张图片

        proxyTargetClass属性其实就是是用来配置是否代理目标类。简而言之就是是否所有的代理对象都通过 CGLIB 的方式来创建,在之后的流程中,Spring 会根据 Bean 实例来判断是采用 JDK 动态代理的方式创建代理对象,还是通过 CGLIB 的方式创建代理对象,如果proxyTargetClass属性配置为true,则全部采用 CGLIB 的方式。

        演示结果: 

        1、 proxyTargetClass = false, 目标实现了接口, 用 jdk 实现

// 4. 创建代理
        Target1 target = new Target1();
        // 这里不需要我们直接去用jdk或者cglib,spring提供了ProxyFactory来方便地创建代理
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvisor(advisor);
        // 告诉工厂,这个方法上继承了什么接口
        factory.setInterfaces(target.getClass().getInterfaces());
        // 设置ProxyTargetClass参数
        factory.setProxyTargetClass(false);
        I1 proxy = (I1) factory.getProxy();
        System.out.println(proxy.getClass());
        proxy.foo();
        proxy.bar();

Spring原理学习(六):Spring实现动态代理时对jdk和cglib的选择_第6张图片

        2、 proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现

// 4. 创建代理
        Target2 target = new Target2();
        // 这里不需要我们直接去用jdk或者cglib,spring提供了ProxyFactory来方便地创建代理
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvisor(advisor);
        // 告诉工厂,这个方法上继承了什么接口
        factory.setInterfaces(target.getClass().getInterfaces());
        // 设置ProxyTargetClass参数
        factory.setProxyTargetClass(false);
        Target2 proxy = (Target2) factory.getProxy();
        System.out.println(proxy.getClass());
        proxy.foo();
        proxy.bar();

Spring原理学习(六):Spring实现动态代理时对jdk和cglib的选择_第7张图片

        3、 proxyTargetClass = true, 总是使用 cglib 实现

        目标类继承了接口 

// 4. 创建代理
        Target1 target = new Target1();
        // 这里不需要我们直接去用jdk或者cglib,spring提供了ProxyFactory来方便地创建代理
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvisor(advisor);
        // 告诉工厂,这个方法上继承了什么接口
        factory.setInterfaces(target.getClass().getInterfaces());
        // 设置ProxyTargetClass参数
        factory.setProxyTargetClass(true);
        I1 proxy = (I1) factory.getProxy();
        System.out.println(proxy.getClass());
        proxy.foo();
        proxy.bar();

Spring原理学习(六):Spring实现动态代理时对jdk和cglib的选择_第8张图片  

        目标类没继承接口:

// 4. 创建代理
        Target2 target = new Target2();
        // 这里不需要我们直接去用jdk或者cglib,spring提供了ProxyFactory来方便地创建代理
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvisor(advisor);
        // 告诉工厂,这个方法上继承了什么接口
        factory.setInterfaces(target.getClass().getInterfaces());
        // 设置ProxyTargetClass参数
        factory.setProxyTargetClass(true);
        Target2 proxy = (Target2) factory.getProxy();
        System.out.println(proxy.getClass());
        proxy.foo();
        proxy.bar();

Spring原理学习(六):Spring实现动态代理时对jdk和cglib的选择_第9张图片

你可能感兴趣的:(java,java,spring,动态代理,AOP)