飞道的博客

JDK动态代理原理分析看完都说妙啊

467人阅读  评论(0)

首先我们要明白代理的含义,假如有一天张三想要买衣服,他是不是有两种方式,第一:自己去生产衣服的地方买,第二:请一个代购,他只需要提要求,然后代购帮助他买,假设我自己就是一个代购,我知道一个工厂生产男装。

在代理中有三个重要的角色:被代理类,代理类,接口

在上面的例子中分别对应的就是,被代理类:生产男装的工厂,代理类:我自己,接口:提供服务

(如果这里都会了,请移步到下面动态代理原理分析)

静态代理

先说一下静态代理,静态代理是指在代码编写阶段就已经确定好了的,被代理类、代理类、接口这三个之间的关系

定义接口,里面包含一个提供服务的接口


  
  1. public interface Serve { //一个提供服务的接口,工厂和代购都必须实现这个接口,提供服务
  2. void service(float price); //提供服务
  3. }

定义一个被代理类,生产男装的工厂,他只负责生产让客户满意衣服


  
  1. public class RealityFactory implements Serve {
  2. @Override
  3. public void service(float price) {
  4. System.out.println( "根据您的要求,本工厂为您定制衣服,价格是:" + price);
  5. }
  6. }

定义一个代理类,相当于我自己,是一个代购,我会帮张三找到工厂,并告诉工厂张三的要求,把工厂生产出来的衣服邮寄到张三的家里


  
  1. public class Purchasing implements Serve {
  2. Serve realityFactory;
  3. public PurchasingAgency(Serve realityFactory) {
  4. this.realityFactory= realityFactory; //注入一个被代理类
  5. }
  6. @Override
  7. public void service(float price) {
  8. System.out.println( "根据市场调研寻找到合适的工厂"); //前置增强
  9. realityFactory.service(price); //工厂只负责制作衣服
  10. System.out.println( "工厂制作完毕,我负责帮您邮寄到家"); //后置增强
  11. }
  12. }

测试一下


  
  1. public class Test{
  2. public static void main(String[] args) {
  3. Serve realityFactory = new RealityFactory(); //定义被代理类工厂
  4. Serve purchasing = new Purchasing(realityFactory); //定义代理类我自己,并将被代理类注入
  5. purchasing.service( 20.0f); //执行代理类的service方法,增强了被代理类作用
  6. }
  7. }

  
  1. 根据市场调研寻找到合适的工厂
  2. 根据您的要求,本工厂为您定制衣服,价格是: 20.0
  3. 工厂制作完毕,我负责帮您邮寄到家

通过静态代理我们可以看到,第一:被代理类实际上只有一个作用就是生产男装,但通过代理类代理之后,方法得到了增强,拥有了三个作用,寻找工厂,制作衣服,寄送衣服。第二:静态代理存在缺点,一个代理类只能为同一个接口的类提供增强,假如有不同的接口,你就需要定义很多的代理类,假如,有一天张三的老婆需要买衣服也找到了我,这样子,这个生产男装的工厂就没办法提供服务了,就需要新的工厂来提供服务。

动态代理

动态代理应运而生,它就可以帮我们解决接口不匹配的问题,可以为多个类提供增强

动态代理的底层原理就是通过反射动态构建了一个代理类,这样子就可以为每一个接口的

在例子中,动态代理类就相当于我自己开了一个代购公司,每次有客户来找我买衣服的时候,我就在公司里面找一个人来服务这个客户

在动态代理类中,接口和被代理类是和静态代理类一样的

定义接口,里面包含一个提供服务的接口,其子类生产男装


  
  1. public interface Serve { //一个提供服务的接口,工厂必须实现这个接口,提供服务
  2. void service(float price); //提供服务
  3. }

定义一个被代理类,生产男装的工厂,他只负责生产让客户满意衣服


  
  1. public class RealityFactory implements Serve {
  2. @Override
  3. public void service(float price) {
  4. System.out.println( "根据您的要求,本工厂为您定制男装,价格是:" + price);
  5. }
  6. }

主要就是下面不一样了,下面我们定义一个动态代理类,这里主要使用到两个主要的类:InvocationHandler和Proxy

InvocationHandler类提供真实的服务,最后实际执行的方法在InvocationHandler中,就相当于是一个我们的客户,为他们提供优质的服务

Proxy类用来动态的生成代理类,就相当于是代购公司中的职员,他们有丰富的资源,可以帮客户找到心仪的产品

定义一个动态代理类,必须实现InvocationHandler方法


  
  1. public class Mediation implements InvocationHandler{ //必须实现InvocationHandler接口
  2. Object object; //接收一个通用类
  3. public Mediation(Object object) { //将通用类注入
  4. this.object = object;
  5. }
  6. @Override
  7. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //增强方法
  8. System.out.println( "根据市场调研寻找到合适的工厂");
  9. method.invoke(object, args); //通过反射调用实际的方法,method为对应的方法,args为方法的参数,如果没有则为null
  10. System.out.println( "工厂制作完毕,我负责帮您邮寄到家");
  11. return null;
  12. }
  13. public Object getProxyInstance() { //通过反射得到一个代理类的实例
  14. return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
  15. }
  16. }

测试一下


  
  1. public class Test{
  2. public static void main(String[] args) {
  3. Serve realityFactory = new RealityFactory(); //实例化一个生产男装的工厂
  4. Mediation mediation = new Mediation(realityFactory); //将这个工厂注入动态代理类
  5. Serve serviceMediation = (Serve) mediation.getProxyInstance(); //通过Proxy类得到一个代理类的实例
  6. serviceMediation.service( 20.0f); //调用代理类的service方法,会将该方法映射到Mediation的invoke方法
  7. }
  8. }

结果


  
  1. 根据市场调研寻找到合适的工厂
  2. 根据您的要求,本工厂为您定制男装,价格是: 20.0
  3. 工厂制作完毕,我负责帮您邮寄到家

假如我们想要为另外一个接口代理呢?

定义另外一个接口,该接口是一个服务接口,其子类生产女装


  
  1. public interface ServeWomen { //一个提供服务的接口,工厂必须实现这个接口,提供服务
  2. void service(float price); //提供服务
  3. }

定义一个被代理类,假设这个类是一个工厂,主要生产女装


  
  1. public class WomenRealityFactory implements ServeWomen {
  2. @Override
  3. public void service(float price) {
  4. System.out.println( "根据您的要求,本工厂为您定制女装,价格是:" + price);
  5. }
  6. }

使用了动态代理,就不用再定义动态代理类,直接使用以前的动态代理类,写测试代码


  
  1. public class Test{
  2. public static void main(String[] args) {
  3. ServeWomen womenRealityFactory = new WomenRealityFactory(); //实例化一个生产女装的工厂
  4. Mediation mediation = new Mediation(womenRealityFactory); //将这个工厂注入动态代理类
  5. ServeWomen serviceWomenMediation = (ServeWomen) mediation.getProxyInstance(); //通过Proxy类得到一个代理类的实例
  6. serviceWomenMediation.service( 20.0f); //调用代理类的service方法,会将该方法映射到Mediation的invoke方法
  7. }
  8. }

结果


  
  1. 根据市场调研寻找到合适的工厂
  2. 根据您的要求,本工厂为您定制女装,价格是: 20.0
  3. 工厂制作完毕,我负责帮您邮寄到家

使用动态代理的好处一下就体现出来了,只要编写了一次动态代理类,并且被代理类实现了一个接口,就可以为被代理类生成一个代理类的实例,这个就是JDK动态代理的实现方式。

JDK动态代理原理

在谈这个之前我们先来看看,一个类的生命周期

java源文件(.java文件):编译之后,就变成java字节码文件

java字节码(.class文件):类加载之后,变成一个Class对象

Class对象:实例化之后,变成一个实例对象

实例对象

卸载

 

 

到现在很多人很好奇,为什么通过Proxy类的静态方法newProxyInstance就可以得到一个ServeWomen的一个实例?通过Proxy类得到ServeWomen的实例到底是哪里来的?这个实例对象到底是哪个类实例化来的?

带着这些问题,我们开启Debug模式查看一下Proxy类生成的实例到底是哪个类,不看不知道,一看吓一跳,$Proxy0这个类是哪里来的?我们没有编写这个类啊,现在好了问题越来越复杂,我们只能去看一下Proxy类的静态方法newProxyInstance里面到底干了什么

我们点开newProxyInstance方法,我们传给了newProxyInstance方法三个参数:

ClassLoader loader,被代理类的类加载器

Class<?>[] interfaces,被代理类实现的接口的Class对象,可能有多个所以是数组

InvocationHandler h,动态代理类的实例

根据JDK代码里面的注释会发现,箭头指向的那一行代码就是核心的,生成代理类的代码,它把我们传递的类加载器和接口的Class对象往下面传递了,我们试着点进去

里面进行了接口数量的验证,重点代码就在get方法里面,我们试着点进去

 首先在ConcurrentMap中查找是否之前已经创建过了,如果没有则自己去创建,我们点进去查看apply方法,这个方法是在接口里面定义的方法,按照下面的步骤查看实现类的定义

 

进到这个方法里面我们就看到了一个熟悉东西$Proxy,这个就是类的前缀,apply方法的前面进行了大量的验证重点代码在后面,我们滑到下面

 根据代码的注释,我们很轻易的找到了生成Class对象的代码,底层就是这样子为你提供了一个实例对象,它直接跳过了java源文件,直接生成了.class文件,然后.class文件加载进入JVM,生成一个Class对象,然后通过Class对象生成一个实例对象

现在已经很清晰,既然可以找到生成.class文件的代码,那我们可不可以将.class文件保存到本地,然后通过反编译看一下这个类的源代码是怎么样的呢?当然可以

编写代码将.class文件保存到本地


  
  1. public class Bytecode {
  2. public static void main(String[] args) {
  3. String fileName = "$ppp"; //class文件的名字
  4. //这一句代码是在源码中找到的生成class文件的代码,第二个参数需要一个Class对象数组类型
  5. byte[] classFile = ProxyGenerator.generateProxyClass(
  6. fileName, new Class[] {ServeWomen.class}, Modifier.FINAL);
  7. FileOutputStream out = null;
  8. String filePath = "G:\\"+fileName+ ".class";
  9. try {
  10. out = new FileOutputStream(filePath);
  11. out.write(classFile); //写入文件
  12. out.flush();
  13. } catch (FileNotFoundException e) {
  14. // TODO Auto-generated catch block
  15. e.printStackTrace();
  16. } catch (IOException e) {
  17. // TODO Auto-generated catch block
  18. e.printStackTrace();
  19. } finally {
  20. try {
  21. if(out != null) {
  22. out.close();
  23. }
  24. } catch (IOException e) {
  25. // TODO Auto-generated catch block
  26. e.printStackTrace();
  27. }
  28. }
  29. }
  30. }

查看本地,果然保存下来了.class文件

使用反编译工具查看源码,源码如下


  
  1. import com.sxt.server.dynamic.ServeWomen;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. import java.lang.reflect.UndeclaredThrowableException;
  6. final class $ppp extends Proxy implements ServeWomen {
  7. private static Method m1;
  8. private static Method m2;
  9. private static Method m3;
  10. private static Method m0;
  11. public $ppp(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); }
  12. public final boolean equals(Object paramObject) {
  13. try {
  14. return ((Boolean) this.h.invoke( this, m1, new Object[] { paramObject })).booleanValue();
  15. } catch (Error|RuntimeException error) {
  16. throw null;
  17. } catch (Throwable throwable) {
  18. throw new UndeclaredThrowableException(throwable);
  19. }
  20. }
  21. public final String toString() {
  22. try {
  23. return (String) this.h.invoke( this, m2, null);
  24. } catch (Error|RuntimeException error) {
  25. throw null;
  26. } catch (Throwable throwable) {
  27. throw new UndeclaredThrowableException(throwable);
  28. }
  29. }
  30. public final void service(float paramFloat) {
  31. try {
  32. this.h.invoke( this, m3, new Object[] { Float.valueOf(paramFloat) });
  33. return;
  34. } catch (Error|RuntimeException error) {
  35. throw null;
  36. } catch (Throwable throwable) {
  37. throw new UndeclaredThrowableException(throwable);
  38. }
  39. }
  40. public final int hashCode() {
  41. try {
  42. return ((Integer) this.h.invoke( this, m0, null)).intValue();
  43. } catch (Error|RuntimeException error) {
  44. throw null;
  45. } catch (Throwable throwable) {
  46. throw new UndeclaredThrowableException(throwable);
  47. }
  48. }
  49. static {
  50. try {
  51. m1 = Class.forName( "java.lang.Object").getMethod( "equals", new Class[] { Class.forName( "java.lang.Object") });
  52. m2 = Class.forName( "java.lang.Object").getMethod( "toString", new Class[ 0]);
  53. m3 = Class.forName( "com.sxt.server.dynamic.ServeWomen").getMethod( "service", new Class[] { float.class });
  54. m0 = Class.forName( "java.lang.Object").getMethod( "hashCode", new Class[ 0]);
  55. return;
  56. } catch (NoSuchMethodException noSuchMethodException) {
  57. throw new NoSuchMethodError(noSuchMethodException.getMessage());
  58. } catch (ClassNotFoundException classNotFoundException) {
  59. throw new NoClassDefFoundError(classNotFoundException.getMessage());
  60. }
  61. }
  62. }

我们发现这个类实现了我们自定义的接口ServeWomen,查看service方法,发现里面是h.invoke(),这个是不是很眼熟,是的,这个h,就是继承了Proxy类得到的,就是我们前面通过this传递下去的动态代理类的实例

所以当我们调用service方法时,实际上它调用的是,动态代理类中的invoke方法

你学会了没有/偷笑


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