代理模式
# 概念
为其他对象提供一种代理以控制对这个对象的访问。
静态代理代理类: 在编译期就生成。
动态代理代理类: 则是在 Java 运行时动态生成,可分为:
- JDK 代理
- CGLib 代理
# 结构
Subject 抽象主题角色: 抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。
Real Subject 具体主题角色: 也叫做被委托角色、被代理角色。它才是冤大头,是业务逻辑的具体执行者。
Proxy 代理主题角色: 也叫做委托类、代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法、限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。
# 静态代理
火车站卖票
//卖票接口
public interface SellTickets {
void sell();
}
//火车站 火车站具有卖票功能,所以需要实现SellTickets接口
public class TrainStation implements SellTickets {
public void sell() {
System.out.println("火车站卖票");
}
}
//代售点
public class ProxyPoint implements SellTickets {
private TrainStation station = new TrainStation();
public void sell() {
System.out.println("代理点收取一些服务费用");
station.sell();
}
}
//测试类
public class Client {
public static void main(String[] args) {
ProxyPoint pp = new ProxyPoint();
pp.sell();
}
}
代理类和被代理类实现了相同的接口,客户端调用代理类方法,代理类又调用被代理类方法,从而达到代理效果。
# JDK 动态代理
Java 中提供了一个动态代理类 Proxy,Proxy 并不是我们上述所说的代理对象的类,而是提供了一个创建代理对象的静态方法(newProxyInstance 方法)来获取代理对象。
//卖票接口
public interface SellTickets {
void sell();
}
//火车站 火车站具有卖票功能,所以需要实现SellTickets接口
public class TrainStation implements SellTickets {
public void sell() {
System.out.println("火车站卖票");
}
}
//代理工厂,用来创建代理对象
public class ProxyFactory {
private TrainStation station = new TrainStation();
public SellTickets getProxyObject() {
//使用Proxy获取代理对象
/*
newProxyInstance()方法参数说明:
ClassLoader loader : 类加载器,用于加载代理类,使用真实对象的类加载器即可
Class<?>[] interfaces : 真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口
InvocationHandler h : 代理对象的调用处理程序
*/
SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(
station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler() {
/*
InvocationHandler中invoke方法参数说明:
proxy : 代理对象
method : 对应于在代理对象上调用的接口方法的 Method 实例
args : 代理对象调用接口方法时传递的实际参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理点收取一些服务费用(JDK动态代理方式)");
//执行真实对象
Object result = method.invoke(station, args);
return result;
}
});
return sellTickets;
}
}
//测试类
public class Client {
public static void main(String[] args) {
//获取代理对象
ProxyFactory factory = new ProxyFactory();
SellTickets proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}
ProxyFactory 是代理类吗?
ProxyFactory 不是代理模式中所说的代理类,而代理类是程序在运行过程中动态的在内存中生成的类。
通过阿里巴巴开源的 Java 诊断工具(Arthas)查看代理类的结构,重点代码:
//程序运行过程中动态生成的代理类
public final class $Proxy0 extends Proxy implements SellTickets {
private static Method m3;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
m3 = Class.forName("com.itheima.proxy.dynamic.jdk.SellTickets").getMethod("sell", new Class[0]);
}
public final void sell() {
this.h.invoke(this, m3, null);
}
}
//Java提供的动态代理相关类
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
this.h = h;
}
}
//代理工厂类
public class ProxyFactory {
private TrainStation station = new TrainStation();
public SellTickets getProxyObject() {
SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理点收取一些服务费用(JDK动态代理方式)");
Object result = method.invoke(station, args);
return result;
}
});
return sellTickets;
}
}
//测试访问类
public class Client {
public static void main(String[] args) {
//获取代理对象
ProxyFactory factory = new ProxyFactory();
SellTickets proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}
执行流程如下:
- 在测试类中通过代理对象调用 sell () 方法
- 根据多态的特性,执行的是代理类($Proxy0)中的 sell () 方法
- 代理类($Proxy0)中的 sell () 方法中又调用了 InvocationHandler 接口的子实现类对象的 invoke 方法
- invoke 方法通过反射执行了真实对象所属类 (TrainStation) 中的 sell () 方法
# 分类
普通代理: 在该模式下,调用者只知代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色想怎么修改就怎么修改,对高层次的模块没有任何的影响,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求较高的场合。
强制代理: 强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色。高层模块只要调用 getProxy 就可以访问真实角色的所有方法,它根本就不需要产生一个代理出来,代理的管理已经由真实角色自己完成。
- 区别:普通代理就是我们要知道代理的存在,然后才能访问;强制代理则是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定的。
动态代理: 根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理会宣称 “我已经实现该接口下的所有方法了”。两条独立发展的线路。动态代理实现代理的职责,业务逻辑实现相关的逻辑功能,两者之间没有必然的相互耦合的关系。通知从另一个切面切入,最终在高层模块进行耦合,完成逻辑的封装任务。
- 意图:横切面编程(AOP),在不改变我们已有代码结构的情况下增强或控制对象的行为。
- 首要条件:被代理的类必须要实现一个接口。
# 代码示例
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}