天天看点

动态代理的使用以及其实现机制

  一、动态代理的使用

 

 动态代理可以提供对另一个对象的访问,同时隐藏实际对象的具体事实。代理一般会实现它所表示的实际对象的接口。代理可以访问实际对象,但是延迟实现实际

对象的部分功能,实际对象实现系统的实际功能,代理对象对客户隐藏了实际对象。客户不知道它是与代理打交道还是与实际对象打交道。

  动态代理主要包含以下角色:

  动态代理类(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类,该类具有下面描述的行为。

  代理接口 是代理类实现的一个接口。

  代理实例 是代理类的一个实例。

<a></a>

  目前java开发包中包含了对动态代理的支持,但是其实现只支持对接口的的实现。 其实现主要通过java.lang.reflect.proxy类和java.lang.reflect.invocationhandler接口。 proxy类主要用来获取动态代理对象,invocationhandler接口用来约束调用者实现

  下面看一个例子:

  1.代理接口:icomputer.java

  2.被代理对象:laptop.java

  3.调用处理类:timehander.java

  4.测试程序:

程序运行结果:

start:1369118281186

电脑正在执行中……

end:1369118282849

total:1663

  二、动态代理运行机制

  proxy.<code>&lt;span style="text-decoration: underline;"&gt;**[newproxyinstance](http://www.cnblogs.com/java/lang/reflect/proxy.html#newproxyinstance(java.lang.classloader, java.lang.class[], java.lang.reflect.invocationhandler))**&lt;/span&gt;([classloader](http://www.cnblogs.com/java/lang/classloader.html "java.lang 中的类") loader, [class](http://www.cnblogs.com/java/lang/class.html "java.lang 中的类")&lt;?&gt;[] interfaces, [invocationhandler](http://www.cnblogs.com/java/lang/reflect/invocationhandler.html "java.lang.reflect 中的接口") h)</code> 方法会返回一个代理对象类的实例。如上面例子中icomputer

computer =

(icomputer)proxy.newproxyinstance(laptop.getclass().getclassloader(),

laptop.getclass().getinterfaces(),

hander);执行这一句话的时候会通过反射机制动态的生成一个代理类,该类实现了icomputer接口,并且重写了接口里面的方法(也就是说代理类

与被代理类有相同的接口),在该代理类里面有一个invocationhandler类型的成员变量,也就是调用处理程序,通过调用处理程序来给被代理类

增强功能。创建好代理类后就调用类加载器将该类加载到类存,然后再通过反射创建一个该代理类的实例对象。

  为了能够更加的理解动态代理的运行机制,我自己来实现了一个动态代理:

  1.代理接口:moveable.java

  2.被代理对象:tank.java

  3.下面写一个proxy类来为被代理对象产生一个代理类对象,来实现增加记录运行时间的功能。

  4.tankproxy.java

  5.测试程序:

执行该程序的结果为:

start:1369121253400

tank moving…

end:1369121260078

time:6678

动态生成的代理类的内容如下:

  看了这个例子,对动态代理的实现机制应该会有一定的了解了!

   小结:动态代理在运行期通过接口动态生成代理类,这为其带来了一定的灵活性,但这个灵活性却带来了两个问题,第一代理类必须实现一个接口,如果没实现接口会抛出一个异常。第二性能影响,因为动态代理使用反射的机制实现的,首先反射肯定比直接调用要慢,其次使用反射大量生成类文件可能引起full gc造成性能影响,因为字节码文件加载后会存放在jvm运行时区的方法区(或者叫持久代)中,当方法区满的时候,会引起full gc,所以当你大量使用动态代理时,可以将持久代设置大一些,减少full gc次数。