代理模式
代理模式(Proxy Pattern):为其他对象提供一种代理以控制对这个对象的访问。属于结构型模式。
在客户和目标对象间起中介作用,保护、增强目标对象,解耦,易扩展;但增加了系统复杂度,延长了处理时间。
在面向对象的编程之中,如果我们想要代理类和目标类可以实现相同的功能,有两种方式:
- 一个比较直观的方式,就是定义一个功能接口,然后让代理类和目标类来实现这个接口。
- 还有比较隐晦的方式,就是通过继承。因为如果代理类继承自目标类,这样代理类则拥有了目标类的功能,代理类还可以通过重写目标类中的方法,来实现多态。
一.JDK静态代理
方式:目标类和代理类同时实现一个目标接口,在代理类对象中持有一个目标接口类型的对象的引用,在代理类的方法中调用该目标接口的方法。
目标接口 Persion.java
public interface Person {
void findHouse();
}
目标类 Myself.java
public class Myself implements Person {
public void findHouse() {
System.out.println("想租房");
}
}
代理类 Friend.java
public class Friend implements Person {
private Person person;
public Friend(Person person) {
this.person = person;
}
public void findHouse() {
System.out.println("我是朋友");
this.person.findHouse();
System.out.println("帮忙找到了");
}
}
测试类 FriendProxyTest.java
public class FriendProxyTest {
public static void main(String[] args) {
Friend friend = new Friend(new Myself());
friend.findHouse();
}
}
缺点:采用硬编码的方式,一个代理类只能为一个目标类服务,造成系统臃肿,且不符合开闭原则。
二.JDK动态代理
机制:利用反射在运行时创建代理类。
1. 目标类实现一个目标接口(必需)。
目标类 租客 Tenant.java
public class Tenant implements Person {
public void findHouse() {
System.out.println("租房要求");
}
}
2. 实现调用处理器接口InvocationHandler,声明目标接口类型的成员变量,重写invoke方法,在invoke方法中,反射调用成员变量的方法。
调用处理器类 房屋中介 JDKHouseAgent.java
public class JDKHouseAgent implements InvocationHandler {
private Person target;
public JDKHouseAgent (Person person) {
this.target = person;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object obj = method.invoke(this.target, args);
after();
return obj;
}
private void before() {
System.out.println("我是中介");
}
private void after() {
System.out.println("这是找到的房子,中介费X元");
}
}
3. 运行时,调用Proxy.newProxyInstance方法,指定目标类、目标接口数组、调用处理器,动态地创建一个代理类$proxy0的对象,它继承了Proxy类,实现了目标接口。
在调用$Proxy0代理类对象中的每一个方法时,在代码内部,都是直接调用了Proxy类持有的InvocationHandler成员的invoke方法,(此处InvocationHandler成员实际被指定为JDKHouseAgent类型),而invoke方法被我们在JDKHouseAgent类中重写了,它会根据传入的方法参数,反射调用目标类的目标方法。具体逻辑参见更下方的$Proxy0.class。?
测试类 JdkProxyTest.java
public class JdkProxyTest {
public static void main(String[] args) {
try {
Person target = new Tenant();
InvocationHandler handler = new JDKHouseAgent(target);
//生成代理类的实例,这一步骤也可写在JDKHouseAgent类中,包装为一个getInstance方法
Person obj = (Person) Proxy.newProxyInstance(
target.getClass().getClassLoader(), //指定目标类加载器
target.getClass().getInterfaces(), //指定目标接口数组
handler); //指派调用处理器
System.out.println("obj的类型 :" + obj.getClass());
obj.findHouse(); //类型强转,才能调用到findHouse方法
//将运行时生成的代理类字节码输出到磁盘
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
FileOutputStream fos = new FileOutputStream("$Proxy0.class");
fos.write(bytes);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行时生成的字节码文件 $Proxy.class
public final class $Proxy0 extends Proxy implements Person {
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{v