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

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

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

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

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

静态代理

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

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

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

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

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

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

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

测试一下

public class Test{
	public static void main(String[] args) {
		Serve realityFactory = new RealityFactory();	//定义被代理类工厂
		Serve purchasing = new Purchasing(realityFactory);	//定义代理类我自己,并将被代理类注入
		purchasing.service(20.0f);	//执行代理类的service方法,增强了被代理类作用
	}
}
根据市场调研寻找到合适的工厂
根据您的要求,本工厂为您定制衣服,价格是:20.0
工厂制作完毕,我负责帮您邮寄到家

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

动态代理

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

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

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

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

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

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

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

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

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

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

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

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

public class Mediation implements InvocationHandler{    //必须实现InvocationHandler接口
	
	Object object;    //接收一个通用类
	public Mediation(Object object) {    //将通用类注入
		this.object = object;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //增强方法
		System.out.println("根据市场调研寻找到合适的工厂");
		method.invoke(object, args);    //通过反射调用实际的方法,method为对应的方法,args为方法的参数,如果没有则为null
		System.out.println("工厂制作完毕,我负责帮您邮寄到家");
		return null;
	}

	public Object getProxyInstance() {    //通过反射得到一个代理类的实例
		return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
	}
}

测试一下

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

结果

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

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

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

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

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

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

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

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

结果

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

使用动态代理的好处一下就体现出来了,只要编写了一次动态代理类,并且被代理类实现了一个接口,就可以为被代理类生成一个代理类的实例,这个就是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文件保存到本地

public class Bytecode {
	public static void main(String[] args) {
		String fileName = "$ppp";	//class文件的名字
		//这一句代码是在源码中找到的生成class文件的代码,第二个参数需要一个Class对象数组类型
		byte[] classFile = ProxyGenerator.generateProxyClass(
				fileName, new Class[] {ServeWomen.class}, Modifier.FINAL);	
		
		FileOutputStream out = null;
		String filePath = "G:\\"+fileName+".class";
		try {
			out = new FileOutputStream(filePath);
			out.write(classFile);	//写入文件
			out.flush();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			try {
				if(out != null) {
					out.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
}

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

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

import com.sxt.server.dynamic.ServeWomen;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class $ppp extends Proxy implements ServeWomen {
  private static Method m1;
  
  private static Method m2;
  
  private static Method m3;
  
  private static Method m0;
  
  public $ppp(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); }
  
  public final boolean equals(Object paramObject) {
    try {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  public final String toString() {
    try {
      return (String)this.h.invoke(this, m2, null);
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  public final void service(float paramFloat) {
    try {
      this.h.invoke(this, m3, new Object[] { Float.valueOf(paramFloat) });
      return;
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  public final int hashCode() {
    try {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  static  {
    try {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m3 = Class.forName("com.sxt.server.dynamic.ServeWomen").getMethod("service", new Class[] { float.class });
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    } catch (NoSuchMethodException noSuchMethodException) {
      throw new NoSuchMethodError(noSuchMethodException.getMessage());
    } catch (ClassNotFoundException classNotFoundException) {
      throw new NoClassDefFoundError(classNotFoundException.getMessage());
    } 
  }
}

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

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

你学会了没有/偷笑

  • 20
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值