Java允许我们在运行时创建某个接口或类的动态代理。什么是动态代理?首先了解一下静态代理(代理设计模式):代理设计模式能够让我们无需修改源代码,进而对源接口功能进行增强(开闭原则,对修改封闭,对扩展开放)。
package com.lz.proxy;
interface Behaviour {
int action();
}
// 被代理类,实现了Behaviour接口
class Target implements Behaviour {
@Override
public int action() {
System.out.println("action...");
return 1;
}
}
// 代理类,也实现了Behaviour接口
class TargetProxy implements Behaviour {
private Behaviour target; // 内部持有被代理类的对象
TargetProxy(Behaviour target) {
this.target = target;
}
@Override
public int action() { // 增强的action方法,在被代理对象的action行为基础上进行扩展
before();
int result = target.action();
after();
return result;
}
private void before() {
System.out.println("before action...");
}
private void after() {
System.out.println("after action...");
}
}
public class StaticProxyDemo {
private static void consume(Behaviour item) { // 消费实现了Behaviour接口的类的对象
int result = item.action();
System.out.println("get result: " + result);
}
public static void main(String[] args) {
Behaviour target = new Target();
consume(target);
System.out.println("\n*** proxy ***");
Behaviour proxy = new TargetProxy(target);
consume(proxy);
}
}
Target类实现了Behaviour接口,能够供consume方法“消费”。而TargetProxy类也实现了Behaviour接口,并内部持有一个Behaviour“类型”的引用,由此能够对接口功能进行扩展。
静态代理是在编译之前,我们编写好的Java代码(.java文件),编译器把它编译成字节码文件(.class文件),进而加载到JVM中,以便使用。而动态代理的思想是:编译期间并不知道代理类的存在,而是在运行时生成代理类字节码加载到虚拟机并创建代理对象,通过反射技术,动态地处理对所代理方法的调用。JDK对此提供了Proxy类。
package com.lz.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
class DynamicProxyHandler implements InvocationHandler {
private Object target;
DynamicProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("action")) {
System.out.println("*** dynamic proxy ***");
System.out.println("method: " + method.getName());
Object result = method.invoke(target, args); // 对原方法的调用
System.out.println("*** dynamic proxy ***");
return result;
} else {
return method.invoke(target, args);
}
}
}
public class DynamicProxyDemo {
private static void consume(Behaviour item) {
int result = item.action();
System.out.println("get result: " + result);
}
public static void main(String[] args) {
Behaviour target = new Target();
// 通过Proxy的newProxyInstance方法在运行时创建代理对象
Behaviour proxy = (Behaviour) Proxy.newProxyInstance(target.getClass().getClassLoader(),
new Class[]{Behaviour.class}, new DynamicProxyHandler(target));
consume(proxy);
/* output:
* *** dynamic proxy ***
* method: action
* action...
* *** dynamic proxy ***
* get result: 1
*/
}
}
注意到DynamicProxyHandler类实现了InvocationHandler接口,此接口只有一个invoke方法。该类中每还持有一个被代理对象的引用。每一个代理对象都会关联一个实现了InvocationHandler接口的对象,通过invoke方法用来对方法进行增强。当通过代理对象调用方法时,都会被“转发”到invoke方法。invoke参数中,proxy就是代理对象,method是被代理的方法,args是传入方法的参数。可以通过method、args等信息各个方法作出相应的“动作”。
Proxy.newInstance()方法接受三个参数:生成代理类的类加载器、代理类要实现(代理)的接口列表和调用处理器。JDK动态代理的弊端就是只能对接口进行代理,原因是生成的代理类继承了Proxy类,而Java有只允许单继承,所以代理对象只能通过实现接口进行代理了。