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"); } @Override public void sayHello() { preRequest(); this.speaker.sayHello(); postRequest(); } }
package com.demo.dynamicproxy; /** * @author Xiechangming * */ public class HelloSpeaker implements IHello { @Override public void sayHello() { System.out.println("Hello,world"); } }
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(); } }
正如你所见,需要显示地实现一个代理类,这就是静态代理模式。 如果需要被代理的方法很多,且代理的逻辑相同,势必要为每种方法实现一个代理类,在程序规模稍大是就会产生很多代理类。该如何解决呢?
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"); } @Override public 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 proxy HelloProxy proxy = new HelloProxy(new HelloSpeaker()); proxy.sayHello(); //dynamic proxy HelloHandler 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(); } @Override public 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(); // Cglib CglibProxy cglibProxy = new CglibProxy(); IHello helloProxy = (IHello)cglibProxy.newProxyInstance(new HelloSpeaker()); helloProxy.sayHello(); } }
CGLib 本质上是动态生成了一个被代理类的子类,并 Override 了被代理的方法,因此被代理的类中方法不能将方法定义为 final ,否则就类似于直接对被代理类的调用,就如实例中,只会输出“Hello,world"。