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


    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.aspectjgroupId>
    4. <artifactId>aspectjweaverartifactId>
    5. dependency>
    6. <dependency>
    7. <groupId>org.aspectjgroupId>
    8. <artifactId>aspectjrtartifactId>
    9. dependency>
    10. dependencies>
    11. <build>
    12. <plugins>
    13. <plugin>
    14. <groupId>org.codehaus.mojogroupId>
    15. <artifactId>aspectj-maven-pluginartifactId>
    16. <version>1.14.0version>
    17. <configuration>
    18. <complianceLevel>1.8complianceLevel>
    19. <source>8source>
    20. <target>8target>
    21. <showWeaveInfo>trueshowWeaveInfo>
    22. <verbose>trueverbose>
    23. <Xlint>ignoreXlint>
    24. <encoding>UTF-8encoding>
    25. configuration>
    26. <executions>
    27. <execution>
    28. <goals>
    29. <goal>compilegoal>
    30. <goal>test-compilegoal>
    31. goals>
    32. execution>
    33. executions>
    34. plugin>
    35. plugins>
    36. 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.aspectjgroupId>
    3. <artifactId>aspectjweaverartifactId>
    4. dependency>
    5. <dependency>
    6. <groupId>org.aspectjgroupId>
    7. <artifactId>aspectjrtartifactId>
    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动态代理在调用到第十七次后会生成代理类去优化为非反射调用,并且是一个方法对应一个代理类。

  • 相关阅读:
    周杰伦十五张专辑(珍藏版)2000-2022年
    如何爬出 Kotlin 协程死锁的坑?
    非零基础自学Java (老师:韩顺平) 第12章 异常 - Exception
    Lesson 04 模板入门
    ESP8266_01S+刷入AT固件+保姆级教学+USB验证AT指令
    【备战csp-j】 csp常考题目详解(4)
    从一条河、一个村、一座市场,看长沙市雨花区非凡十年
    memset函数详解
    (每日一题)[leetCode] 622设计循环队列
    如何抑制开关电源的启动浪涌电流?看这一文,6种方法总结,秒懂
  • 原文地址:https://blog.csdn.net/qq_51409098/article/details/127689455