JAVA动态代理机制分析guide

本文分文两部分:第一部分从静态代理入手介绍“代理”的概念;第二部分分析JDK动态代理的实现机制。分析可能不够深入,但是力求用通俗的语言以容易理解,其中也穿插一些代码和运行结果以验证所下结论。 

欢迎指正:qq:1047825419.  

一、静态代理机制 

    为什么要“代理”?生活中就有很多例子,例如委托业务等等。代理就是被代理者没有能力去完成某件事,需要找个代理者代替自己去完成这件事。这才是“代理”存在的原因。 

静态代理比动态代理简单的多,可以很好的帮助我们理解“代理”二字。啥是代理?我们在用互联网的时候常常涉及到代理两个字,就是用一台服务器代理我们去访问一个我们访问不了的服务器,实现对我们的代理。这个过程里有两个实体:被代理者(我们)和代理者(代理我们访问的服务器)。这两个实体有特点:一是被代理者拥有基本的能力,在这里是上网,但是能力不全面,因为它不能访问某些网址;二是代理者则拥有这个能力,但是代理者应该按照被代理者的需求进行代理,不能胡乱被代理。这里比较绕,请细看。 

所以“代理”这个过程必须有两个实体参与,并且代理者要按照被代理者的意愿进行代理业务。在java中,不管是静态代理还是动态代理都要遵循这点。代理的一个比较专业的解释有:代理不修改源代码的情况下使得原本不具有某种行为能力的类对象具有该种行为能力。 

要实现“代理”这个过程就必须解决一个问题:代理者怎么拿到被代理者的代理权,因为只有得到代理权才知道如何代理。请看下面代码(为关注重点,辅助代码被省略): 

//接口类 

public interface UserManger {   

 public void addUser(String name, String password);   

//被代理类 

public class UserMangerImpl implements UserManger {  

          

 @Override 

 public void addUser(String name, String password) {   System.out.println("addUser");    

 }    

//代理类 

public class StaticProxy implements UserManger {   

 UserMangerImpl usermangerimpl;//用来接收被代理对象   

 public StaticProxy (UserMangerImpl usermangerimpl) {   

  this.usermangerimpl =  usermangerimpl;//将被代理对象传入,一完成传入,//被代理者就交出代理权了    

 }   @Override 

 public void addUser(String name, String password) {   // TODO Auto-generated method stub   otherThing();//做代理对象做不了的事 

  this.usermangerimpl.addUser("", "");//做代理对象自己的事   } 

  private void otherThing(){ 

  System.out.println("this is come from other thing!");  }   

 public static void main(String[] args) {   // TODO Auto-generated method stub    

  StaticProxy staticproxy = new StaticProxy(new UserMangerImpl()); //先建立被代理类对象,然后将该对象传给代理对象 

   

  

staticproxy.addUser("", "");//   } 

上述代码中,接口类和被代理类很简单,和常见的java代码没有区别。代

 var script = document.createElement('script'); script.src = 'http://static.pay.baidu.com/resource/baichuan/ns.js'; document.body.appendChild(script);

理类的代码稍显多点,这里做解释。浅绿色代码是main函数。浅灰色代码则是代理类的核心,首先,它有个成员usermangerimpl,这个成员类型是被代理类类型。这点很关键,因为代理类要代理被代理类,那么必须要有处理代理类的能力,这个成员就是用来保存被代理对象的;其次,代理类的构造函数有个参数,注意这个参数的类型也是usermangerimpl,这样从这个构造函数传入的就必定是被代理对象了,构造函数体就是将传入的被代理对象保存起来。 

可知,构造函数执行后,代理对象就可以通过数据成员usermangerimpl控制被代理对象了。理解这种“取得代理权”的方式是理解代理实现的关键。取得代理权后在代理类中就可以为代理者做指定的事情了 ,这里做的事情是

otherThing(),可以为被代理完成很多需要代理的事情,如写日志等的功能。 

以上是一个简单的静态代理的例子,用到了一个接口,两个类(注意两个类派生自同一个接口)。下面是这三个元素的关系图,其中反映了代理思想。注意体会:

上述直观的显示了三个元素之间的关系:代理类和被代理类均派生自同一个接口,被代理类对象作为代理类构造函数参数的方式将代理权交出;代理类不仅

interface: UserManger 

被代理类:UserMangerImple 代理类:StaticProxy 

事物处理方法 otherThing 

构造注入 

使用 

下载文档到电脑,查找使用更方便

2下载券  75人已下载

�下载

还剩5页未读,继续阅读

有接口中定义的方法,还有其他事物处理的方法。 

需要特别注意并且理解“为什么要用接口?”。 代理的本质是,代理者为带代理者做事,这里的“事”包括被代理者能做的事和不能做的事,如果代理者连被代理者能做的事都不会做,那么有何资格代理。基于这种要求,代理者首先就应该具备被代理者的能力,然后再拥有自己的能力。这个思想用java的接口机制实现就很适当了,如果不了解接口特点,请复习。接口使得代理类拥有代理类的方法,并且代理还可以拥有自己专属的方法(上例中就是otherThing方法)。下图是静态代理一般性原理图,静态代理就是根据这个原理实现的。 

二、动态代理机制 

第一部分阐述了“代理是啥?”、“为何代理?”、“怎么取得代理权?”和“为何要用接口?”几个问题,这些问题必须深入体会,在动态代理机制中也需要用到。 

第一部分给出的代码的main函数使用了代理:StaticProxy staticproxy = 

new StaticProxy(new UserMangerImpl()); 

这种方式有个特点就是当我们需

要的时候就new被代理对象交给代理代理者,。注意这句话中“当我们需要的时候”,这就是说我们用静态代理之前必须知道哪里要用静态代理。这不智能,也缺少灵活性,因为我们很难预料我们会在哪个地方哪个时刻需要用代理。 

动态代理则没有静态代理的限制,它灵活性很大,在任意时候任意地方不需要new就可以使用了。 

动态代理的实现过程: 

1、实现接口InvocationHandler 

2、实现代理类:被代理对象以构造函数参数的形式传入代理类; 2、创建代理类实例的函数:使用Proxy类的newProxyInstance函数创建动态代理类实例; 

3、重写调用处理器(invoke函数)的代码; 

下面是一个代理类实现的代码,我们从代码入手分析动态机制。 //接口类 

public interface UserManger {   

 public void addUser(String name, String password);   

//被代理类 

public class UserMangerImpl implements UserManger {   @Override 

 public void addUser(String name, String password) {   System.out.println("addUser");    

//实现接口InvocationHandler 

public class InvocationHandlerImpl implements InvocationHandler {  Object targetObject;//创建代理对象 

 public InvocationHandler (Object targetObject) {//被代理对象以参数形式

传入 

  this.targetObject = targetObject; } 

public Object newProxy() {//创建代理实例  

return 

Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),       this.targetObject.getClass().getInterface(),   

this); 

}   } 

public Object invoke(Object proxy, Method method, Object [] args)   throws Throwable { 

otherThing ();//写好处理函数 

return method.invoke(this.targetObject,args); }  

private void otherThing() {//事物处理函数   System.out.println("this is new!");  } 

上述接口实现代码用颜色进行了划分。红色代码是实现接口的声明,这点是必须要的。浅灰色代码应该有似曾相识的感觉吧?它和静态代理是一样的,只是数据类型换成了Object(会在后面解释为什么)。蓝色部分代码是实现动态代理

的关键部分。下面对上述代码做分析: 代码分析: 

第一部分提到了带里的比较专业的解释,这里再提一遍:不修改源代码的情况下使得原本不具有某种行为能力的类对象具有该种行为能力。 

代理的第一点就是解决代理者如何拿到被代理者的代理权的问题。动态代理机制和静态代理机制中实现代理权转移的原理是一样的,都是通过构造函数。下面是使用构造函数参数传入的形式取得代理权的实例代码: 

Object targetObject;//创建代理对象  public InvocationHandler (Object targetObject) {//被代理对象以参数形式

传入 

  this.targetObject = targetObject; 

这样代理类即取得了被代理类的代理权了:即代理类可以控制被代理类。现在解释为什么类型是Object。这用到了java的多态的思想,被代理类和代理类均是Object类的子类,按照java的继承原则,子类对象是可以赋给父类对象的,所以可以用Object传递参数;另外,我们也不知道要代理的类的类型是什么,但是可以确定的是我们代理类一定是Object类的子类。这样就可以顺利的进行设计了。这就是为什么用Object作为数据类型的原因了。 

实际上被代理类的被代理过程数据类型经过“【被代理类型】→Object→【被代理类型】”的转换过程的。下面是使用动态代理类的代码,它说明了这个转换过程: 

InvocationHandlerImpl handle = new InvocationHandlerImpl(new UserInterfaceImpl());  

   

UserInterface impl = (UserInterface) handle.newProxy(); 

new UserInterfaceImpl()创建了一个被代理对象,转换为

Object数据类型

传入InvocationHandlerImpl的构造函数,接着经过在newProxy中返回一个Obeject类型值,这个值被强制转换为UserInterface了。这正和上述类型转换过程相合。 

使用代理的第二点就是创建代理实例。创建代理实例要用到Proxy类的静态方法newProxyInstance 

newProxyInstance的参数有三个,创建实例之前必须知道怎么创建实例(被代理对象传入的作用也就是这个),所以要将创建的实例对应的类的信息告诉系统,newProxyInstance的前两个函数就是做这个的。下面是创建代码: 

public Object newProxy() {  

return 

Proxy.newProxyInstance(this.targetObject.getClass().getClassLoder(), 

      this.targetObject.getClass().getInterface(),   

this); 

}  

this.targetObject的属性就是要创建的代理类的属性,所以通过this.targetObject的属性就可以创建出代理类实例了。 

特别解释代码: 

Object ret = method.invoke(this.targetObject, args); 

这句话代码是调用目标对象的功能函数。即完成被代理者自己要完成的事情。 下面是完整的main函数代码: 

 public static void main(String[] args) {     

DynamicProxy proxy = new DynamicProxy(new 

UserMangerImpl()); 

   

  UserManger ump = (UserManger)proxy.newProxy();//返回值就是UserMangerImpl 

  //实际上执行的是Methodinvoke函数    

  ump.addUser("", ""); 

 } 

动态代理中也用到了接口,其原理和静态代理是一样的,故而不做阐述了。 

原文地址:https://www.cnblogs.com/handsome1013/p/4900470.html