飞道的博客

Spring原理学习(七)JDK动态代理与CGLIB代理底层实现

589人阅读  评论(0)

AOP 底层实现方式之一是代理,由代理结合通知和目标,提供增强功能。

除此以外,aspectj 提供了两种另外的 AOP 底层实现:

  • 第一种是通过 ajc 编译器编译 class 类文件时,就把通知的增强功能,织入到目标类的字节码中

  • 第二种是通过 agent 在加载目标类时,修改目标类的字节码,织入增强功能

  • 作为对比,之前学习的代理是运行时生成新的字节码

简单比较的话:

  • aspectj 在编译和加载时,修改目标字节码,性能较高

  • aspectj 因为不用代理,能突破一些技术上的限制,例如对构造、对静态方法、对 final 也能增强

  • 但 aspectj 侵入性较强,且需要学习新的 aspectj 特有语法,因此没有广泛流行

这里开拓一下视野,在实际开发中,我们还是经常使用代理为主。

一、AOP 实现之 ajc 编译器

需要导入的依赖和插件

注意事项:

  1. 版本选择了 java 8, 因为目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16
  2. 一定要用 maven 的 compile 来编译, idea 不会调用 ajc 编译器

  
  1. <dependencies>
  2. <dependency>
  3. <groupId>org.aspectj </groupId>
  4. <artifactId>aspectjweaver </artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.aspectj </groupId>
  8. <artifactId>aspectjrt </artifactId>
  9. </dependency>
  10. </dependencies>
  11. <build>
  12. <plugins>
  13. <plugin>
  14. <groupId>org.codehaus.mojo </groupId>
  15. <artifactId>aspectj-maven-plugin </artifactId>
  16. <version>1.14.0 </version>
  17. <configuration>
  18. <complianceLevel>1.8 </complianceLevel>
  19. <source>8 </source>
  20. <target>8 </target>
  21. <showWeaveInfo>true </showWeaveInfo>
  22. <verbose>true </verbose>
  23. <Xlint>ignore </Xlint>
  24. <encoding>UTF-8 </encoding>
  25. </configuration>
  26. <executions>
  27. <execution>
  28. <goals>
  29. <!-- use this goal to weave all your main classes -->
  30. <goal>compile </goal>
  31. <!-- use this goal to weave all your test classes -->
  32. <goal>test-compile </goal>
  33. </goals>
  34. </execution>
  35. </executions>
  36. </plugin>
  37. </plugins>
  38. </build>

主程序类


  
  1. @SpringBootApplication
  2. public class A09Application {
  3. private static final Logger log = LoggerFactory.getLogger(A09Application.class);
  4. public static void main (String[] args) {
  5. ConfigurableApplicationContext context = SpringApplication.run(A09Application.class, args);
  6. MyService service = context.getBean(MyService.class);
  7. log.debug( "service class: {}", service.getClass());
  8. service.foo();
  9. context.close();
  10. }
  11. }

增强类


  
  1. @Aspect // 注意此切面并未被 Spring 管理
  2. public class MyAspect {
  3. private static final Logger log = LoggerFactory.getLogger(MyAspect.class);
  4. @Before("execution(* com.itheima.service.MyService.foo())")
  5. public void before () {
  6. log.debug( "before()");
  7. }
  8. }

被增强类


  
  1. @Service
  2. public class MyService {
  3. private static final Logger log = LoggerFactory.getLogger(MyService.class);
  4. public static void foo () {
  5. log.debug( "foo()");
  6. }
  7. }

结果:MyService的类型是原始目标,而不是代理


  
  1. [main] com.itheima.A09Application : service class: class com.itheima.service.MyService
  2. [main] com.itheima.aop.MyAspect : before()
  3. [main] com.itheima.service.MyService : foo()

这里的增强并不是spring做的增强,因为切面类并未被管理,是通过ajc编译器实现的。 

查看MyService的class文件,发现foo()调用了增强类的方法。

那我们去掉与spring相关的东西,重新再测试


  
  1. public class A09Application {
  2. private static final Logger log = LoggerFactory.getLogger(A09Application.class);
  3. public static void main (String[] args) {
  4. new MyService().foo();
  5. }
  6. }

发现还是被增强了,原因:修改的是class文件,自然可以生效


  
  1. [main] DEBUG com.itheima.aop.MyAspect - before()
  2. [main] DEBUG com.itheima.service.MyService - foo()

总结

  1. 编译器也能修改 class 实现增强

  2. 编译器增强能突破代理仅能通过方法重写增强的限制:可以对构造方法、静态方法等实现增强

二、AOP 实现之 agent 类加载

类加载时可以通过 agent 修改 class 实现增强

需要导入的依赖


  
  1. <dependency>
  2. <groupId>org.aspectj </groupId>
  3. <artifactId>aspectjweaver </artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.aspectj </groupId>
  7. <artifactId>aspectjrt </artifactId>
  8. </dependency>

主程序类


  
  1. @SpringBootApplication
  2. public class A10Application {
  3. private static final Logger log = LoggerFactory.getLogger(A10Application.class);
  4. public static void main (String[] args) {
  5. ConfigurableApplicationContext context = SpringApplication.run(A10Application.class, args);
  6. MyService service = context.getBean(MyService.class);
  7. log.debug( "service class: {}", service.getClass());
  8. service.foo();
  9. }
  10. }

 增强类


  
  1. @Aspect // 注意此切面并未被 Spring 管理
  2. public class MyAspect {
  3. private static final Logger log = LoggerFactory.getLogger(MyAspect.class);
  4. @Before("execution(* com.itheima.service.MyService.foo())")
  5. public void before () {
  6. log.debug( "before()");
  7. }
  8. }

被增强类


  
  1. @Service
  2. public class MyService {
  3. private static final Logger log = LoggerFactory.getLogger(MyService.class);
  4. public static void foo () {
  5. log.debug( "foo()");
  6. }
  7. }

运行时需要在 VM options里加入

-javaagent:C:/Users/manyh/.m2/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar

把其中 C:/Users/manyh/.m2/repository 改为你自己 maven 仓库起始地址类加载阶段,在class文件看不到。

结果:MyService的类型是原始目标,而不是代理


  
  1. [main] com.itheima.A09Application : service class: class com.itheima.service.MyService
  2. [main] com.itheima.aop.MyAspect : before()
  3. [main] com.itheima.service.MyService : foo()

由于是在类加载阶段(即运行期间)被增强,在class文件看不到,我们需要借助Arthas工具查看。可以进入官网下载jar包。

具体操作如下:

 反编译命令:

jad com.itheima.service.MyService 

发现MyService在运行期间被增强

三、AOP 实现之 proxy

代理类与普通类的差别:

  • 普通类:java原代码 -> 字节码 -> 类加载 -> 使用
  • 代理类:运行期间直接生成代理类的字节码

1、JDK动态代理


  
  1. public class JdkProxyDemo {
  2. interface Foo {
  3. void foo ();
  4. }
  5. static class Target implements Foo {
  6. public void foo () {
  7. System.out.println( "target foo");
  8. }
  9. }
  10. // jdk 只能针对接口代理
  11. public static void main (String[] param) throws IOException {
  12. //目标对象
  13. Target target = new Target();
  14. //用来加载在运行期间动态生成的字节码
  15. ClassLoader classLoader = JdkProxyDemo.class.getClassLoader();
  16. //参数二:代理类要实现的接口 参数三:代理类调用代理类方法时执行的行为
  17. Foo proxy = (Foo) Proxy.newProxyInstance(classLoader, new Class[]{Foo.class}, new InvocationHandler() {
  18. @Override
  19. public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
  20. System.out.println( "before...");
  21. //反射调用目标方法
  22. Object result = method.invoke(target);
  23. System.out.println( "after...");
  24. return result; //让代理类返回目标方法执行的结果
  25. }
  26. });
  27. proxy.foo();
  28. }
  29. }

结果:


  
  1. before...
  2. target foo
  3. after...

注意:

jdk 动态代理要求目标必须实现接口,生成的代理类实现相同接口,因此代理与目标之间是平级兄弟关系  

2、Cglib代理


  
  1. public class CglibProxyDemo {
  2. static class Target {
  3. public void foo () {
  4. System.out.println( "target foo");
  5. }
  6. }
  7. // 代理是子类型, 目标是父类型
  8. public static void main (String[] param) {
  9. Target target = new Target();
  10. Target proxy = (Target) Enhancer.create(Target.class, new MethodInterceptor() {
  11. @Override
  12. public Object intercept (Object p, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  13. System.out.println( "before...");
  14. //用反射调用目标方法
  15. Object result = method.invoke(target);
  16. System.out.println( "after...");
  17. return result;
  18. }
  19. });
  20. proxy.foo();
  21. }
  22. }

结果:


  
  1. before...
  2. target foo
  3. after...

注意:

cglib 不要求目标实现接口,它生成的代理类是目标的子类,因此代理与目标之间是子父关系

根据上述分析 final 类,final方法无法被 cglib 增强,因为是子父类的关系,相当于代理类对目标类方法的重写。

invoke 与 invokeSuper的区别

与jdk动态代理不同的是,cglib可以避免使用反射调用目标方法。

使用methodProxy的invoke 或 invokeSuper方法


  
  1. public static void main (String[] param) {
  2. Target target = new Target();
  3. Target proxy = (Target) Enhancer.create(Target.class, new MethodInterceptor() {
  4. @Override
  5. public Object intercept (Object p, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  6. System.out.println( "before...");
  7. Object result = methodProxy.invoke(target, args);
  8. System.out.println( "after...");
  9. return result;
  10. }
  11. });
  12. proxy.foo();
  13. }

两者区别


  
  1. //需要目标对象
  2. methodProxy.invoke(target, args)
  3. //需要代理对象
  4. methodProxy.invokeSuper(p, args);

spring使用的是invoke方法

四、JDK动态代理进阶

学习目标:代理类的内部原理

  1. 方法重写可以增强逻辑
  2. 通过接口回调将【增强逻辑】置于代理类之外
  3. 配合接口方法反射(也是多态),就可以再联动调用目标方法

1、模拟JDK动态代理 


  
  1. public class A12 {
  2. interface Foo {
  3. void foo ();
  4. int boo ();
  5. }
  6. //目标类
  7. static class Target implements Foo {
  8. public void foo () {
  9. System.out.println( "target foo");
  10. }
  11. @Override
  12. public int boo () {
  13. System.out.println( "target boo");
  14. return 100;
  15. }
  16. }
  17. //提供动态执行增强逻辑的方法
  18. interface InvocationHandler {
  19. Object invoke (Object proxy, Method method, Object[] args) throws Throwable;
  20. }
  21. }

代理类


  
  1. public class $Proxy0 implements Foo {
  2. private InvocationHandler invocationHandler;
  3. public $Proxy0() {
  4. }
  5. public $Proxy0(InvocationHandler invocationHandler) {
  6. this.invocationHandler = invocationHandler;
  7. }
  8. static Method foo;
  9. static Method boo;
  10. static {
  11. try {
  12. //根据方法名获取Method属性
  13. foo = Foo.class.getMethod( "foo");
  14. boo = Foo.class.getMethod( "boo");
  15. } catch (NoSuchMethodException e) {
  16. throw new NoSuchMethodError(e.getMessage());
  17. }
  18. }
  19. @Override
  20. public void foo () {
  21. try {
  22. invocationHandler.invoke( this, foo, new Object[ 0]);
  23. } catch (RuntimeException | Error e) {
  24. throw e;
  25. } catch (Throwable e){
  26. throw new UndeclaredThrowableException(e);
  27. }
  28. }
  29. @Override
  30. public int boo () {
  31. try {
  32. int result = ( int) invocationHandler.invoke( this, boo, new Object[ 0]);
  33. return result;
  34. } catch (RuntimeException | Error e) {
  35. throw e;
  36. } catch (Throwable e){
  37. throw new UndeclaredThrowableException(e);
  38. }
  39. }
  40. }

这里说明一下异常处理:

  • 运行时异常:RuntimeException,Error,无需捕获,直接抛出
  • 检查异常:Throwable,接口不一定有Throwable,需要将检查异常转换为运行时异常抛出

测试


  
  1. Foo foo = new $Proxy0( new InvocationHandler() {
  2. @Override
  3. public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
  4. System.out.println( "before...");
  5. Object result = method.invoke(target);
  6. System.out.println( "after...");
  7. return result;
  8. }
  9. });
  10. foo.foo();
  11. foo.boo();

结果:


  
  1. before...
  2. target foo
  3. after...
  4. before...
  5. target boo
  6. after...

JDK生成代理类时并没有经历源码阶段,编译阶段,而是直接进入字节码阶段,现在看到的java源码是通过Arthas工具对它进行了反编译, 直接生成字节码的底层技术是ASM,被广泛应用于JDK,Spring等框架,它的作用就是在运行期间动态生成字节码。

和我们自己写的代理类进行比较发现比我们多实现了Object中的toString,equals,hashCode方法,并且继承了Proxy类。


  
  1. package com.itheima.a11;
  2. import com.itheima.a11.JdkProxyDemo;
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Proxy;
  6. import java.lang.reflect.UndeclaredThrowableException;
  7. final class $Proxy0 extends Proxy implements JdkProxyDemo.Foo {
  8. private static Method m1;
  9. private static Method m2;
  10. private static Method m3;
  11. private static Method m0;
  12. public $Proxy0(InvocationHandler invocationHandler) {
  13. super(invocationHandler);
  14. }
  15. static {
  16. try {
  17. m1 = Class.forName( "java.lang.Object").getMethod( "equals", Class.forName( "java.lang.Object"));
  18. m2 = Class.forName( "java.lang.Object").getMethod( "toString", new Class[ 0]);
  19. m3 = Class.forName( "com.itheima.a11.JdkProxyDemo$Foo").getMethod( "foo", new Class[ 0]);
  20. m0 = Class.forName( "java.lang.Object").getMethod( "hashCode", new Class[ 0]);
  21. return;
  22. }
  23. catch (NoSuchMethodException noSuchMethodException) {
  24. throw new NoSuchMethodError(noSuchMethodException.getMessage());
  25. }
  26. catch (ClassNotFoundException classNotFoundException) {
  27. throw new NoClassDefFoundError(classNotFoundException.getMessage());
  28. }
  29. }
  30. public final boolean equals (Object object) {
  31. try {
  32. return (Boolean) this.h.invoke( this, m1, new Object[]{object});
  33. }
  34. catch (Error | RuntimeException throwable) {
  35. throw throwable;
  36. }
  37. catch (Throwable throwable) {
  38. throw new UndeclaredThrowableException(throwable);
  39. }
  40. }
  41. public final String toString () {
  42. try {
  43. return (String) this.h.invoke( this, m2, null);
  44. }
  45. catch (Error | RuntimeException throwable) {
  46. throw throwable;
  47. }
  48. catch (Throwable throwable) {
  49. throw new UndeclaredThrowableException(throwable);
  50. }
  51. }
  52. public final int hashCode () {
  53. try {
  54. return (Integer) this.h.invoke( this, m0, null);
  55. }
  56. catch (Error | RuntimeException throwable) {
  57. throw throwable;
  58. }
  59. catch (Throwable throwable) {
  60. throw new UndeclaredThrowableException(throwable);
  61. }
  62. }
  63. public final void foo () {
  64. try {
  65. this.h.invoke( this, m3, null);
  66. return;
  67. }
  68. catch (Error | RuntimeException throwable) {
  69. throw throwable;
  70. }
  71. catch (Throwable throwable) {
  72. throw new UndeclaredThrowableException(throwable);
  73. }
  74. }
  75. }

Proxy类内部已经定义了 InvocationHandler


  
  1. public class Proxy implements java.io.Serializable {
  2. protected InvocationHandler h;
  3. protected Proxy (InvocationHandler h) {
  4. Objects.requireNonNull(h);
  5. this.h = h;
  6. }
  7. }

注意:代理增强是借助多态来实现,因此成员变量、静态方法、final 方法均不能通过代理实现

2、方法反射优化

由于ASM学习成本比较高,这里直接给出结论,感兴趣的小伙伴可以去看视频。

优化:

  1. 前 16 次反射性能较低

  2. 第 17 次调用会生成代理类,优化为非反射调用

五、cglib 代理进阶

和 JDK 动态代理原理查不多

  1. 回调的接口换了一下,InvocationHandler 改成了 MethodInterceptor

  2. 调用目标时有所改进,见下面代码片段

    1. method.invoke 是反射调用,必须调用到足够次数才会进行优化

    2. methodProxy.invoke 是不反射调用,它会正常(间接)调用目标对象的方法(Spring 采用)

    3. methodProxy.invokeSuper 也是不反射调用,它会正常(间接)调用代理对象的方法,可以省略目标对象

1、模拟cglib代理

目标类


  
  1. public class Target {
  2. public void save () {
  3. System.out.println( "save()");
  4. }
  5. public void save (int i) {
  6. System.out.println( "save(int)");
  7. }
  8. public void save (long j) {
  9. System.out.println( "save(long)");
  10. }
  11. }

代理类


  
  1. public class Proxy extends Target{
  2. private MethodInterceptor methodInterceptor;
  3. public Proxy () {
  4. }
  5. public Proxy (MethodInterceptor methodInterceptor) {
  6. this.methodInterceptor = methodInterceptor;
  7. }
  8. static Method save0;
  9. static Method save1;
  10. static Method save2;
  11. static {
  12. try {
  13. save0 = Target.class.getMethod( "save");
  14. save1 = Target.class.getMethod( "save", int.class);
  15. save2 = Target.class.getMethod( "save", long.class);
  16. } catch (NoSuchMethodException e) {
  17. throw new NoSuchMethodError(e.getMessage());
  18. }
  19. }
  20. @Override
  21. public void save () {
  22. try {
  23. methodInterceptor.intercept( this, save0, new Object[ 0], null);
  24. } catch (RuntimeException | Error e){
  25. throw e;
  26. } catch (Throwable throwable) {
  27. throw new UndeclaredThrowableException(throwable);
  28. }
  29. }
  30. @Override
  31. public void save (int i) {
  32. try {
  33. methodInterceptor.intercept( this, save1, new Object[]{i}, null);
  34. } catch (RuntimeException | Error e){
  35. throw e;
  36. } catch (Throwable throwable) {
  37. throw new UndeclaredThrowableException(throwable);
  38. }
  39. }
  40. @Override
  41. public void save (long j) {
  42. try {
  43. methodInterceptor.intercept( this, save2, new Object[]{j}, null);
  44. } catch (RuntimeException | Error e){
  45. throw e;
  46. } catch (Throwable throwable) {
  47. throw new UndeclaredThrowableException(throwable);
  48. }
  49. }
  50. }

测试


  
  1. public class A13 {
  2. public static void main (String[] args) {
  3. Target target = new Target();
  4. Proxy proxy = new Proxy( new MethodInterceptor() {
  5. @Override
  6. public Object intercept (Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  7. System.out.println( "before...");
  8. return method.invoke(target, args);
  9. }
  10. });
  11. proxy.save();
  12. proxy.save( 1);
  13. proxy.save( 2L);
  14. }
  15. }

结果:


  
  1. before...
  2. save()
  3. before...
  4. save( int)
  5. before...
  6. save( long)

 上面的代码其实和模拟JDK动态代理的代码差不多,重点是下面的避免反射调用的代码。

2、cglib 避免反射调用

给代理类增加原始功能的方法以及MethodProxy


  
  1. public class Proxy extends Target{
  2. private MethodInterceptor methodInterceptor;
  3. public Proxy () {
  4. }
  5. public Proxy (MethodInterceptor methodInterceptor) {
  6. this.methodInterceptor = methodInterceptor;
  7. }
  8. static Method save0;
  9. static Method save1;
  10. static Method save2;
  11. static MethodProxy save0Proxy;
  12. static MethodProxy save1Proxy;
  13. static MethodProxy save2Proxy;
  14. static {
  15. try {
  16. save0 = Target.class.getMethod( "save");
  17. save1 = Target.class.getMethod( "save", int.class);
  18. save2 = Target.class.getMethod( "save", long.class);
  19. /*
  20. 参数一:目标类型 参数二:代理类型
  21. 参数三:方法参数描述符
  22. 参数四:带增强功能的方法名 参数五:带原始功能的方法名
  23. */
  24. save0Proxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper");
  25. save1Proxy = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper");
  26. save2Proxy = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper");
  27. } catch (NoSuchMethodException e) {
  28. throw new NoSuchMethodError(e.getMessage());
  29. }
  30. }
  31. //>>>>>>>>>>>>>>>>>>>>>>>带原始功能的方法
  32. public void saveSuper (){
  33. super.save();
  34. }
  35. public void saveSuper (int i){
  36. super.save(i);
  37. }
  38. public void saveSuper (long j){
  39. super.save(j);
  40. }
  41. //>>>>>>>>>>>>>>>>>>>>>>>带增强功能的方法
  42. @Override
  43. public void save () {
  44. try {
  45. methodInterceptor.intercept( this, save0, new Object[ 0], save0Proxy);
  46. } catch (RuntimeException | Error e){
  47. throw e;
  48. } catch (Throwable throwable) {
  49. throw new UndeclaredThrowableException(throwable);
  50. }
  51. }
  52. @Override
  53. public void save (int i) {
  54. try {
  55. methodInterceptor.intercept( this, save1, new Object[]{i}, save1Proxy);
  56. } catch (RuntimeException | Error e){
  57. throw e;
  58. } catch (Throwable throwable) {
  59. throw new UndeclaredThrowableException(throwable);
  60. }
  61. }
  62. @Override
  63. public void save (long j) {
  64. try {
  65. methodInterceptor.intercept( this, save2, new Object[]{j}, save2Proxy);
  66. } catch (RuntimeException | Error e){
  67. throw e;
  68. } catch (Throwable throwable) {
  69. throw new UndeclaredThrowableException(throwable);
  70. }
  71. }
  72. }

当调用 MethodProxy 的 invoke 或 invokeSuper 方法时, 会动态生成两个类

  • ProxyFastClass 配合代理对象一起使用, 避免反射
  • TargetFastClass 配合目标对象一起使用, 避免反射 (Spring 用的这种)

这两个类会继承FastClass抽象类,为了演示简单,下面的getIndex 和 invoke是实现它的两个方法。

1)invoke方法的无反射演示


  
  1. public class TargetFastClass {
  2. //方法签名
  3. static Signature s0 = new Signature( "save", "()V");
  4. static Signature s1 = new Signature( "save", "(I)V");
  5. static Signature s2 = new Signature( "save", "(J)V");
  6. // 获取目标方法的编号
  7. /*
  8. Target
  9. save() 0
  10. save(int) 1
  11. save(long) 2
  12. signature 包括方法名字、参数返回值
  13. */
  14. public int getIndex (Signature signature) {
  15. if (signature.equals(s0)){
  16. return 0;
  17. } else if (signature.equals(s1)){
  18. return 1;
  19. } else if (signature.equals(s2)){
  20. return 2;
  21. }
  22. return - 1;
  23. }
  24. // 根据方法编号, 正常调用目标对象方法
  25. public Object invoke (int index, Object target, Object[] args) {
  26. if (index == 0){
  27. ((Target) target).save();
  28. return null;
  29. } else if (index == 1){
  30. ((Target) target).save(( int) args[ 0]);
  31. return null;
  32. } else if (index == 2){
  33. ((Target) target).save(( long) args[ 0]);
  34. return null;
  35. } else {
  36. throw new RuntimeException( "无此方法");
  37. }
  38. }
  39. //模拟操作
  40. public static void main (String[] args) {
  41. //首次使用MethodProxy方法时被创建
  42. TargetFastClass fastClass = new TargetFastClass();
  43. //MethodProxy在创建时由于记录了方法签名,所以能够调用getIndex方法得到方法编号
  44. int i = fastClass.getIndex( new Signature( "save", "()V"));
  45. //调用MethodProxy的invoke方法,间接会调用到fastClass的invoke方法
  46. fastClass.invoke(i, new Target(), new Object[ 0]);
  47. }
  48. }

2)invokeSuper方法的无反射演示


  
  1. public class ProxyFastClass {
  2. /*
  3. 调用的是代理类带原始功能的方法而不是增强方法,不然会陷入死循环
  4. */
  5. static Signature s0 = new Signature( "saveSuper", "()V");
  6. static Signature s1 = new Signature( "saveSuper", "(I)V");
  7. static Signature s2 = new Signature( "saveSuper", "(J)V");
  8. // 获取代理方法的编号
  9. /*
  10. Target
  11. saveSuper() 0
  12. saveSuper(int) 1
  13. saveSuper(long) 2
  14. signature 包括方法名字、参数返回值
  15. */
  16. public int getIndex (Signature signature) {
  17. if (signature.equals(s0)){
  18. return 0;
  19. } else if (signature.equals(s1)){
  20. return 1;
  21. } else if (signature.equals(s2)){
  22. return 2;
  23. }
  24. return - 1;
  25. }
  26. // 根据方法编号, 正常调用目标对象方法
  27. public Object invoke (int index, Object proxy, Object[] args) {
  28. if (index == 0){
  29. ((Proxy) proxy).saveSuper();
  30. return null;
  31. } else if (index == 1){
  32. ((Proxy) proxy).saveSuper(( int) args[ 0]);
  33. return null;
  34. } else if (index == 2){
  35. ((Proxy) proxy).saveSuper(( long) args[ 0]);
  36. return null;
  37. } else {
  38. throw new RuntimeException( "无此方法");
  39. }
  40. }
  41. public static void main (String[] args) {
  42. ProxyFastClass fastClass = new ProxyFastClass();
  43. int i = fastClass.getIndex( new Signature( "saveSuper", "()V"));
  44. fastClass.invoke(i, new Proxy(), new Object[ 0]);
  45. }
  46. }

总结

为什么有这么麻烦的一套东西呢?

  • 避免反射,提高性能,代价是一个代理类配两个 FastClass 类,代理类中还得增加仅调用 super 的一堆方法

  • 用编号处理方法对应关系比较省内存,另外,最初获得方法顺序是不确定,这个过程没法固定死

与JDK动态代理相比,CGLIB代理类数目相对较少,只有两个类。而JDK动态代理在调用到第十七次后会生成代理类去优化为非反射调用,并且是一个方法对应一个代理类。


转载:https://blog.csdn.net/qq_51409098/article/details/127689455
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场