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"。