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

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

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都有极其丰富的实现,我们选熟悉的来使用。

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

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

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

 

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对jdk和cglib的统一 

         先说结论:

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

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

        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();

        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();

        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();

  

        目标类没继承接口:

// 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();