浅谈JDK动态代理技术

说到代理,大家不由想到23种设计模式中的代理模式。JDK的动态代理技术正是基于代理模式,并且核心使用反射机制实现。

首先,便于理解,我们看看demo级别的代理模式(也就对应地叫做静态代理):

interface Person {  
public void doSomething();  
}  
  
class User implements Person {  
public void doSomething() {  
  System.out.print("基类方法");  
}  
}   
   
class Proxy implements Person {  
User  user;   
public Proxy() { // 初始类时得到被代理类的实例  
  this.user = new User();  
}  

public void doSomething() {  
  System.out.print("前-");  
  user.doSomething();  
  System.out.print("-后");  
}  
}   

//////////////////////////////////////测试////////////////////////////////////////  
public void main(String[] strs){  
  Person proxy=new Proxy();  
  proxy.doSomething();  
}  
复制代码

输出:

前-基类方法-后
复制代码

对于以上实现,我们发现了静态代理模式的一些繁琐之处:每需要代理一个类时,需要程序员手动为其构造类似如上代理类代码。于是JDK出现了动态代理技术来解决这一窘境。下面先看看它的基本使用方式:

// 调用处理器:代理类的相应方法被执行会均会调用该处理器,而该处理器则会调用被代理类方法完成操作  
class MyInvocationHandler implements InvocationHandler {  
private Object metaObj;  
  
public MyInvocationHandler(Object obj) {  
  this.metaObj = obj;  
}

@Override  
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  System.out.print("前-");  
  Object obj = method.invoke(metaObj, args);  
  System.out.println("-后");  
  return obj;  
}
}

////////////////////////////////////测试/////////////////////////////////////////  
public static void main(String[] args) {
  MyInvocationHandler myHandler = new MyInvocationHandler(new User());  
  Person proxy= (Person) Proxy.newProxyInstance(User.class.getClassLoader(), User.class.getInterfaces(),myHandler);  
  proxy.doSomething();
}
复制代码

输出:

前-基类方法-后
复制代码

对于以上就是动态代理的一个简单使用,我们可以发现:使用动态代理,我们将能够应对任何需要代理的类,并为其生产代理类。仅需要在调用生成代理实例的newProxyInstance方法时传入适当的参数(该类的类加载器,该类所实现的所有接口,调用处理器)。我们来看看它的大概实现,下面给出关键代码(我只是将核心代码进行内联,便于观看,其实远不止这点代码):

@CallerSensitive  
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){  
Class<?> proxyClass = null;  //待生成的代理类的类类型  
   
  //根据提供的接口生成代理类字节数组流。ProxyGenerator为rt.jar包中负责生成代理类字节数组流工具,proxyName为生成的代理           
  //class文件的名字,由省略代码给出  
  byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);   
     
  //由类加载器将字节数组流生成为一个新类,并加载到内存中,然后返回其类类型。  
  proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);  
  
  //根据代理类的类类型获取其中含有{ InvocationHandler.class } 参数的构造器  
  final Constructor<?> cons = proxyClass .getConstructor({ InvocationHandler.class });  
  
  //根据该构造器,实例化代理类,并让调用处理器进入该实例中  
  return cons.newInstance(new Object[] {h} );
}
复制代码

由上代码,我们可以看到,通过指定类装载器和一组相关接口就可以生成代理类实例。而InvocationHandler调用处理器仅是作为一个参数进入到了生成的这个实例中。那么此时有两个问题:

1. 上面含有{ InvocationHandler.class } 参数的构造器是怎么产生的? 代理类起自ProxyGenerator.generateProxyClass方法,究其实现可发现如下代码:

methods.add(generateConstructor());
复制代码
private MethodInfo generateConstructor()throws IOException{  
  MethodInfo methodinfo = new MethodInfo("<init>", "(Ljava/lang/reflect/InvocationHandler;)V", 1);  
  DataOutputStream dataoutputstream = new DataOutputStream(methodinfo.code);  
  code_aload(0, dataoutputstream);  
  code_aload(1, dataoutputstream);  
  dataoutputstream.writeByte(183);  
  dataoutputstream.writeShort(cp.getMethodRef("java/lang/reflect/Proxy", "<init>", "(Ljava/lang/reflect/InvocationHandler;)V"));  
  dataoutputstream.writeByte(177);  
  methodinfo.maxStack = 10;  
  methodinfo.maxLocals = 2;  
  methodinfo.declaredExceptions = new short[0];  
  return methodinfo;  
}  
复制代码

很明显,构造器就是在这里被加入进去的,且参数类型明确指出是Ljava/lang/reflect/InvocationHandler

2. InvocationHandler的作用又到底是什么呢? 先看看代理类生成为class文件的相关核心代码:

public final void doSomething(){  
    this.invocationHandler.invoke(this, m3, null); //this表示代理类实例  
    return;
    }
static{
    m3 = Class.forName("My程序.动态代理.Person").getMethod("doSomething", new Class[0]);  
    return;
    }
复制代码

结合调用处理器看:

// 调用处理器:代理类的相应方法被执行会均会调用该处理器,而该处理器则会调用被代理类方法完成操作  
class MyInvocationHandler implements InvocationHandler {
private Object metaObj;
public MyInvocationHandler(Object obj) {
  this.metaObj = obj;  
}  
@Override  
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  System.out.print("前-");  
  Object obj = method.invoke(metaObj, args);  
  System.out.println("-后");  
  return obj;  
}  
}
复制代码

我们需要让代理类去管控被代理类(代理模式设计原则),也就是比如在调用之前或者之后能进行一些额外操作(如上面的“前-”、“-后”)。而代理类都是运行时动态生成的,那么这些额外操作怎么才能放进去呢?这时,InvocationHandler作用就出现了。额外操作的编码就放在Invocation中,然后让InvocationHandler进入动态生成的代理类实例,实例来调用InvocationHandler的方法即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值