读书人

Java的动态署理实现

发布时间: 2012-12-23 11:28:15 作者: rapoo

Java的动态代理实现

1. 问题

在许多情况下,我们需要使用代理模式来解决问题。如下为代理模式的类图。


Java的动态署理实现

?

?

?

如下为使用Java实现代理模式的一个例子:

?

package com.demo.dynamicproxy;public interface IHello {public void sayHello() ;}

?

package com.demo.dynamicproxy;public class HelloProxy implements IHello {private IHello speaker = new HelloSpeaker();public HelloProxy(IHello hello) {this.speaker = hello;}private void preRequest() {System.out.println("prepare");}private void postRequest() {System.out.println("applaud");}@Overridepublic void sayHello() {preRequest();this.speaker.sayHello();postRequest();}}

?

package com.demo.dynamicproxy;/** * @author Xiechangming *  */public class HelloSpeaker implements IHello {@Overridepublic void sayHello() {System.out.println("Hello,world");}}

?

package com.demo.dynamicproxy;public class ProxyTest {public static void main(String...args){//static proxyHelloProxy proxy = new HelloProxy(new HelloSpeaker());proxy.sayHello();//dynamic proxyHelloHandler handler = new HelloHandler();IHello dynamicProxy = (IHello)handler.bind(new HelloSpeaker());dynamicProxy.sayHello();}}

?

?

正如你所见,需要显示地实现一个代理类,这就是静态代理模式。 如果需要被代理的方法很多,且代理的逻辑相同,势必要为每种方法实现一个代理类,在程序规模稍大是就会产生很多代理类。该如何解决呢?

2. 解决方案

在JDK1.3中,引入了动态代理类,用来解决相同的代理逻辑可以为多种被代理类重复使用。JDK动态代理中包含一个类和一个接口:?
InvocationHandler接口:?
public interface InvocationHandler {?
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;?
}?
参数说明:?
Object proxy:指被代理的对象。?
Method method:要调用的方法?
Object[] args:方法调用时所需要的参数?

该方法就是实现代理逻辑的地方,类似于ProxySubject。?

Proxy类:?
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:?
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,?
InvocationHandler h)?
?????????????????????????????? throws IllegalArgumentException?
参数说明:?
ClassLoader loader:类加载器?
Class<?>[] interfaces:得到全部的接口?
InvocationHandler h:得到InvocationHandler接口的子类实例?

该方法返回真实的代理类的一个对象,该代理类是JDK动态生成的。如下是一个动态代理模式的实例:

package com.demo.dynamicproxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class HelloHandler implements InvocationHandler {private Object delegate;public Object bind(Object delegate) {this.delegate = delegate;return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this);}private void preRequest() {System.out.println("prepare");}private void postRequest() {System.out.println("applaud");}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {preRequest();method.invoke(this.delegate, args);postRequest();return null;}}

?

?

?

?

package com.demo.dynamicproxy;public class ProxyTest {public static void main(String...args){//static proxyHelloProxy proxy = new HelloProxy(new HelloSpeaker());proxy.sayHello();//dynamic proxyHelloHandler handler = new HelloHandler();IHello dynamicProxy = (IHello)handler.bind(new HelloSpeaker());dynamicProxy.sayHello();}}

?

?

3.? 实现原理

通过查看Proxy的源代码,可以对动态代理模式的实现原理有一个大致的了解:

下面是newProxyInstance的实现,从中可以看出关键是调用getProxyClass方法获得最终的代理类,该代理类是动态生成

public static Object newProxyInstance(ClassLoader loader,  Class<?>[] interfaces,  InvocationHandler h)throws IllegalArgumentException    {if (h == null) {    throw new NullPointerException();}/* * Look up or generate the designated proxy class. */Class cl = getProxyClass(loader, interfaces);/* * Invoke its constructor with the designated invocation handler. */try {    Constructor cons = cl.getConstructor(constructorParams);    return (Object) cons.newInstance(new Object[] { h });} catch (NoSuchMethodException e) {    throw new InternalError(e.toString());} catch (IllegalAccessException e) {    throw new InternalError(e.toString());} catch (InstantiationException e) {    throw new InternalError(e.toString());} catch (InvocationTargetException e) {    throw new InternalError(e.toString());}}

?

?

?

?

?下面是getProxyClass的主要实现

public static Class<?> getProxyClass(ClassLoader loader,                                          Class<?>... interfaces)throws IllegalArgumentException    {…Class proxyClass = null;//声明要动态生成的类…String proxyName = proxyPkg + proxyClassNamePrefix + num; //定义动态生成的类的名字/* * Verify that the class loader hasn't already * defined a class with the chosen name. *//* * Generate the specified proxy class. */byte[] proxyClassFile =ProxyGenerator.generateProxyClass(    proxyName, interfaces);//调用generateProxyClass方法生成动态类的.class文件的二进制,Sun的内部实现try {    proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);//.class文件的二进制导入到JVM,获得类的实例。这是一个本地方法。} catch (ClassFormatError e) {    /*     * A ClassFormatError here means that (barring bugs in the     * proxy class generation code) there was some other     * invalid aspect of the arguments supplied to the proxy     * class creation (such as virtual machine limitations     * exceeded).     */    throw new IllegalArgumentException(e.toString());}    }    // add to set of all generated proxy classes, for isProxyClass    proxyClasses.put(proxyClass, null);} …return proxyClass;//返回生成的类。    }

?

?

?

?

4. 不足

该代理模式要求被代理的类必须要实现接口,如果被代理的类没有实现接口,则不能使用动态代理模式。那么有什么更好的方式来代理没有实现接口的类的方法呢?这就要用到开源软件CBLib了。下面是是使用CGLib实现的例子:

package com.demo.dynamicproxy;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;public class CglibProxy implements MethodInterceptor {private void preRequest() {System.out.println("prepare");}private void postRequest() {System.out.println("applaud");}public Object newProxyInstance(Object target) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass());enhancer.setCallback(this);return enhancer.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy) throws Throwable {preRequest();proxy.invokeSuper(obj, args);postRequest();return null;}}
?
package com.demo.dynamicproxy;public class ProxyTest {public static void main(String... args) {//// static proxy//HelloProxy proxy = new HelloProxy(new HelloSpeaker());////proxy.sayHello();////// dynamic proxy//HelloHandler handler = new HelloHandler();////IHello dynamicProxy = (IHello) handler.bind(new HelloSpeaker());////dynamicProxy.sayHello();// CglibCglibProxy cglibProxy = new CglibProxy();IHello helloProxy = (IHello)cglibProxy.newProxyInstance(new HelloSpeaker());helloProxy.sayHello();}}
?

CGLib本质上是动态生成了一个被代理类的子类,并Override了被代理的方法,因此被代理的类中方法不能将方法定义为final,否则就类似于直接对被代理类的调用,就如实例中,只会输出“Hello,world"。

?

读书人网 >编程

热点推荐