要想了解Java动态代理,首先要了解什么叫做代理,熟悉设计模式的朋友一定知道在Gof总结的23种设计模式中,有一种叫做代理(Proxy)的对象结构型模式,动态代理中的代理,指的就是这种设计模式。
在我看来所谓的代理模式,和23种设计模式中的“装饰模式”是一个东西。23种设计模式中将它们作为两种模式,网上也有些文章讲这两种模式的异同,从细节来看,确实可以人为地区分这两种模式,但是抽象到一定高度后,我认为这两种模式是完全一样的。因此学会了代理模式,也就同时掌握了装饰模式。
代理模式
代理模式简单来说,就是对一个对象进行包装,包装后生成的对象具有和原对象一样的方法列表,但是每个方法都可以是被包装过的。
静态代理
让我们先来看一段代码:
package common;
public class Test {
static interface Subject{
void sayHi();
void sayHello();
}
static class SubjectImpl implements Subject{
@Override
public void sayHi() {
System.out.println("hi");
}
@Override
public void sayHello() {
System.out.println("hello");
}
}
static class SubjectImplProxy implements Subject{
private Subject target;
public SubjectImplProxy(Subject target) {
this.target=target;
}
@Override
public void sayHi() {
System.out.print("say:");
target.sayHi();
}
@Override
public void sayHello() {
System.out.print("say:");
target.sayHello();
}
}
public static void main(String[] args) {
Subject subject=new SubjectImpl();
Subject subjectProxy=new SubjectImplProxy(subject);
subjectProxy.sayHi();
subjectProxy.sayHello();
}
}
</div>
这段代码中首先定义了一个Subject接口,接口中有两个方法。
然后定义了SubjectImpl类实现Subject接口并实现其中的两个方法,到这里肯定是没问题的。
现在再定义一个SubjuectImplProxy类,也实现Subject接口。这个SubjectImplProxy类的作用是包装SubjectImpl类的实例,它的内部定义一个变量target来保存一个SubjectImpl的实例。SubjectImplProxy也实现了接口规定的两个方法,并且在它的实现版本中,都调用了SubjectImpl的实现,但是又添加了自己的处理逻辑。
相信这段代码不难理解,它通过对SubjectImpl进行包装,达到了给输出内容添加前缀的功能。这种代理方式叫做静态代理。
动态代理
从上面的演示中我们不难看出静态代理的缺点:我们对SubjectImpl的两个方法,是进行的相同的包装,但是却要在SubjectImplProxy里把相同的包装逻辑写两次,而且以后如果Subject接口再添加新的方法,SubjectImplProxy也必须要添加新的实现,尽管SubjectImplProxy对所有方法的包装可能都是一样的。
下面我把上面例子的静态代理改成动态代理,我们来看一下区别:
package common;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
static interface Subject{
void sayHi();
void sayHello();
}
static class SubjectImpl implements Subject{
@Override
public void sayHi() {
System.out.println("hi");
}
@Override
public void sayHello() {
System.out.println("hello");
}
}
static class ProxyInvocationHandler implements InvocationHandler{
private Subject target;
public ProxyInvocationHandler(Subject target) {
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.print("say:");
return method.invoke(target, args);
}
}
public static void main(String[] args) {
Subject subject=new SubjectImpl();
Subject subjectProxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject));
subjectProxy.sayHi();
subjectProxy.sayHello();
}
}
</div>
只看main方法的话,只有第二行和之前的静态代理不同,同样是生成一个subjectProxy代理对象,只是生成的代码不同了。静态代理是直接new 一个SubjectImplProxy的实例,而动态代理则调用了java.lang.reflect.Proxy.newProxyInstance()方法,我们来看一下这个方法的源码:
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); //获取代理类的Class
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
Constructor cons = cl.getConstructor(constructorParams); //constructorParams是写死的:{ InvocationHandler.class },上边返回的代理类Class一定是extends Proxy的,而Proxy有一个参数为InvocationHandler的构造函数
return cons.newInstance(new Object[] { h }); //这里通过构造函数将我们自己定义的InvocationHandler的子类传到代理类的实例里,当我们调用代理类的任何方法时,实际上都会调用我们定义的InvocationHandler子类重写的invoke()函数
} 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());
}
}
</div>
上面的 Class<?> cl = getProxyClass(loader, interfaces); 调用的getProxyClass方法:
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException
{
if (interfaces.length > 65535) { //因为在class文件中,一个类保存的接口数量是用2个字节来表示的,因此java中一个类最多可以实现65535个接口
throw new IllegalArgumentException("interface limit exceeded");
}
Class<?> proxyClass = null;
/* collect interface names to use as key for proxy class cache */
String[] interfaceNames = new String[interfaces.length];
// for detecting duplicates
Set<Class<?>> interfaceSet = new HashSet<>();
//验证interfaces里的接口是否能被类加载器加载,是否是接口,是否有重复的
for (int i = 0; i < interfaces.length; i++) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
String interfaceName = interfaces[i].getName();
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != interfaces[i]) {
throw new IllegalArgumentException(
interfaces[i] + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.contains(interfaceClass)) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
interfaceSet.add(interfaceClass);
interfaceNames[i] = interfaceName;
}
/*
* Using string representations of the proxy interfaces as
* keys in the proxy class cache (instead of their Class
* objects) is sufficient because we require the proxy
* interfaces to

