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

?
?
?
如下为使用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"。
?