在静态代理中,真实对象和代理角色是“一对一”的关系。而在动态代理中,真实对象和代理角色是“多对一”的关系。动态代理可以用一个“万能”的代理者来代理任何类型的真实对象,而不用像静态代理那样给每个真实角色都设置一个代理,这也是动态代理的最大优势。
静态代理需要实现和真实对象相同的接口。例如,房屋中介和真正房屋的主人,都需要“出租房”,因此“出租房”就是两者需要共同实现的接口。但是,动态代理既然是一个“万能”的代理者,那么动态代理应该实现什么接口呢?例如,房屋主人和汽车主人,都需要动态代理,那么这个动态代理应该实现“出租房”还是“出租车”的接口呢?为了解决这个问题,JDK 提供了一个“万能”的动态代理接口——InvocationHandler,及使用动态代理时可用 InvocationHandler 接口中的 invoke() 方法代理出租房、出租车等各种业务方法。
二 代码 1 租房接口package dynamicproxy;
// 租房接口
public interface Subject {
boolean rent(int money);
}
2 租车接口
package dynamicproxy;
// 出租车接口
public interface Subject2 {
boolean rentCar(String type);
}
3 租房真实角色
package dynamicproxy;
// 租房真实角色
public class RealSubject implements Subject {
@Override
public boolean rent(int money) {
System.out.println("租房" + money + "元");
return true;
}
}
4 租车真实角色
package dynamicproxy;
// 租车真实接口
public class RealSubject2 implements Subject2 {
@Override
public boolean rentCar(String type) {
System.out.println("租用的车类型:" + type);
return true;
}
}
5 动态代理角色
package dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxy implements InvocationHandler {
private Object obj; // 可以代理任意 类型的角色
public DynamicProxy(Object obj) {
this.obj = obj;
}
public void before() {
System.out.println("before rent()...");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
this.before();
// obj.method(args)
boolean result = (boolean) (method.invoke(obj, args)); // 通过反射调用rent()方法
this.after();
return result;
}
public void after() {
System.out.println("after...");
}
}
6 租房测试
package dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
// 真实对象
Subject realSubject = new RealSubject();
// 初步的代理对象:handler 就是真实角色 realSubject 的初步代理对象
InvocationHandler handler = new DynamicProxy(realSubject);
// 最终的代理对象
Subject subProxy = (Subject) Proxy.newProxyInstance(
handler.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
handler);
boolean result = subProxy.rent(3000); // 会调用动态代理对象的 invoke()
System.out.println(subProxy.getClass().getName());
System.out.println(result ? "ok" : "error");
}
}
7 租车测试
package dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test2 {
public static void main(String[] args) {
Subject2 realSubject2 = new RealSubject2();
InvocationHandler handler2 = new DynamicProxy(realSubject2);
Subject2 subProxy2 = (Subject2) Proxy.newProxyInstance(
handler2.getClass().getClassLoader(),
realSubject2.getClass().getInterfaces(),
handler2);
subProxy2.rentCar("宝马");
}
}
三 测试
1 租房测试
2 租车测试before rent()...
租房3000元
after...
com.sun.proxy.$Proxy0
ok
四 动态代理的编写步骤before rent()...
租用的车类型:宝马
after...
1 编写自定义接口。接口本身包含了真实角色中的方法(称为方法A)。
2 编写真实角色类。真实角色类需要实现自定义接口,并重写接口中的方法A。
3 编写动态代理角色类。动态代理角色类实现 InvocationHandler 接口,并重写接口中的 invoke() 方法,再通过 method.invoke(obj,args)调用真实角色中的方法A。
4 使用时,先根据真实角色,产生一个初步代理角色 handler,即 handler = new DynamicProxy(真实角色);再通过 subProxy = (自定义接口) Proxy.newProxyInstance(类加载器,真正角色接口数组,初步代理角色)产生一个最终的代理对象 subProxy;最后通过 subProxy 调用真实角色方法 A,但实际上,在调用代理角色方法的 invoke() 方法。



