JAVA设计模式之代理模式
1、什么是代理模式
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
2、应用场景
代理模式是一种比较常见的结构型设计模式,按照不同的场景,又可以分为保护代理,远程代理,虚拟代理,缓冲代理和智能引用代理。
- 保护代理,就是为原对象设置不同的访问权限,处于被保护的状态,不可直接访问,用户在访问时根据自己不同的权限来访问。
- 远程代理,在本地创建一个远程对象的本地代理,通过访问该本地代理实现访问远程对象的目的,也叫大使(Ambassador)
- 虚拟代理,虚拟的意思是假的,当要创建一个复杂巨大的对象时,先创建一个小的代理对象,当具体使用时再创建真实对象,有点“懒加载”的意思。
- 缓冲代理,为针对原对象的某种操作提供一个临时的缓冲代理,用来分担访问原对象的压力,以便多客户端快速共享这些资源。
- 智能引用代理,相当于对原对象的一种功能扩展,在访问原对象时,加入了新功能,例如统计访问次数等。
3、与装饰模式的区别
https://blog.csdn.net/zhshulin/article/details/38665187
装饰者模式UML类图
区别:
- 从语意上讲,代理模式是为控制对被代理对象的访问,而装饰模式是为了增加被装饰对象的功能
- 代理类所能代理的类完全由代理类确定,装饰类装饰的对象需要根据实际使用时客户端的组合来确定
- 被代理对象由代理对象创建,客户端甚至不需要知道被代理类的存在;被装饰对象由客户端创建并传给装饰对象
4、静态代理
所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
抽象角色
/**
* 抽象角色
*/
public interface Subject {
/**
* 处理任务
* @param taskName 任务名称
* @return 处理任务是否成功
*/
boolean dealTask(String taskName);
/**
* 销毁任务
* @param taskName 任务名称
* @return 销毁任务是否成功
*/
boolean destroyTask(String taskName);
}
真实角色
/**
* 真实角色
*/
public class RealSubject implements Subject {
public boolean dealTask(String taskName) {
System.out.println("正在处理任务:" + taskName);
return true;
}
public boolean destroyTask(String taskName) {
System.out.println("正在销毁任务:" + taskName);
return true;
}
}
代理角色
/**
* 代理角色
*/
public class ProxySubject implements Subject {
/**
* 真实角色
*/
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
public boolean dealTask(String taskName) {
System.out.println("method:dealTask is invoked before");
boolean dealTask = realSubject.dealTask(taskName);
System.out.println("method:dealTask is invoked after");
return dealTask;
}
public boolean destroyTask(String taskName) {
System.out.println("method:destroyTask is invoked before");
boolean dealTask = realSubject.destroyTask(taskName);
System.out.println("method:destroyTask is invoked after");
return dealTask;
}
}
客户端
public class Client {
public static void main(String[] args) {
Subject subject = new ProxySubject(new RealSubject());
if (subject.dealTask("技术分享")) {
System.out.println("处理任务成功!");
}
if (subject.destroyTask("技术分享")) {
System.out.println("销毁任务成功!");
}
}
}
输出结果
method:dealTask is invoked before
正在处理任务:技术分享
method:dealTask is invoked after
处理任务成功!
method:destroyTask is invoked before
正在销毁任务:技术分享
method:destroyTask is invoked after
销毁任务成功!
5、JDK动态代理
5.1、如何使用
/**
* 抽象角色
*/
public interface Subject {
/**
* 处理任务
* @param taskName 任务名称
*/
void dealTask(String taskName);
/**
* 销毁任务
* @param taskName 任务名称
*/
void destroyTask(String taskName);
}
/**
* 真实角色
*/
public class RealSubject implements Subject {
public void dealTask(String taskName) {
System.out.println("正在处理任务:" + taskName);
}
public void destroyTask(String taskName) {
System.out.println("正在销毁任务:" + taskName);
}
}
/**
* 自定义调用处理器
*/
public class MyInvocationHandler implements InvocationHandler {
/**
* 真实角色
*/
private Object realSubject;
public MyInvocationHandler(Object realSubject) {
this.realSubject = realSubject;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method:" + method.getName() + " is invoked before");
Object invoke = method.invoke(realSubject, args);
System.out.println("method:" + method.getName() + " is invoked after");
return invoke;
}
}
public class Client {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//加上这句将会产生一个$Proxy0.class文件,这个文件即为动态生成的代理类文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
RealSubject realSubject = new RealSubject();
ClassLoader classLoader = Client.class.getClassLoader();
InvocationHandler handler = new MyInvocationHandler(realSubject);
// 第一种写法
// Class<?> proxyClass = Proxy.getProxyClass(classLoader, Subject.class);
// Object proxyInstance = proxyClass.getConstructor(InvocationHandler.class).newInstance(handler);
// Subject subject = (Subject) proxyInstance;
// Subject2 subject2 = (Subject2) proxyInstance;
// 第二种写法
Class<?>[] interfaces = realSubject.getClass().getInterfaces();
Object proxyInstance = Proxy.newProxyInstance(classLoader, interfaces, handler);
Subject subject = (Subject) proxyInstance;
Subject2 subject2 = (Subject2) proxyInstance;
subject.dealTask("技术分享");
subject.destroyTask("技术分享");
subject2.sayHello("World");
}
}
5.2、关键源码解读
UML类图
概述
Proxy是用二级缓存WeakCache来实现的,一级Key是ClassLoader,二级Key由KeyFactory工厂生成,Value由ProxyClassFactory生成,key和value存的都是弱引用对象,JVM每次GC都会清理弱引用所引用的对象(非弱引用对象本身,请注意,指的是Reference对象中的referent属性),并将弱引用对象存到弱引用与之关联的引用队列中,这是JVM的内部实现机制,JVM为什么要这么做呢,我的理解是方便程序主动去清理这些弱引用对象。
源码分析
最重要的代码就是这一段,WeakCache的get()方法
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
// 根据引用队列清理缓存,这些引用的referent已经被JVM GC时回收了,所以要将这些引用从map中删除,否则从缓存中拿到的值也是空的,没意义,谁进行引用队列的入队操作的呢,概述中讲过,JVM内部实现机制,GC时做的操作。
expungeStaleEntries();
// key是ClassLoader,为什么Proxy的一级Key要用ClassLoader我还不太清楚,还没怎么去研究过类加载机制,这里是用classLoader创建一个弱引用作为一级Key
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
// 为指定的一级Key懒加载二级valuesMap
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
// 用subKeyFactory生成二级key
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
// 真正取值的地方,如果是Factory实例,说明还没有缓存,factory的get方法会调用valueFactory(Proxy是ProxyClassFactory)生成value,并用CacheValue<V>替换Factory,也就是生成缓存;如果是CacheValue实例,说明已有缓存,直接取值;不管是Factory还是CacheValue实例,一般来说这里都会拿到值
V value = supplier.get();
// 拿到值了就跳出循环(好像大佬们称为自旋)
if (value != null) {
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// 上面三句话解释得很清楚,不再翻译,看源码时留意一下
// lazily construct a Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
// 走到这里是Factory的get()方法为null的情况,旧的Factory无法正常生成value,所以用新的factory替换
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// 不太确定什么时候会走到这里,同步引起的问题?还没想的太明白
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
6、总结
通过这次对代理模式的学习,自我感觉提升不少,特别是看JDK源码的过程中,了解到了很多新的知识点,比如强引用、软引用、弱引用、虚引用以及与之相关的引用队列等知识点,当然还有这次的主题,JDK动态代理的实现原理,不过还是有一些没完全整明白的地方,还得多看几遍才行,温故而知新,到时候再来完善;文中可能有说的不恰当的地方,欢迎大家指正,收到第一时间回复,以免误导其他人,谢谢。