代理模式
假如现在有个
厂家
生产了一台生产冰淇淋的机器,支持蛋筒&杯装两种形式,支持选择口味、大小
投放硬币,就可以根据选择生成冰淇淋
(这台机器就相当于一个类,蛋筒和杯装是两个不同的方法,选择口味和规格按钮相当于参数,冰淇淋是返回值)。
代码模拟
接口:
public interface IceCreamMachine {
/**
* 模拟生产杯装冰淇淋
* @param taste 草莓/原味/巧克力
* @param size 大/中/小
* @return 冰淇淋
*/
String cup(String taste, String size);
/**
* 模拟生产蛋筒冰淇淋
* @param taste 草莓/原味/巧克力
* @param size 大/中/小
* @return 冰淇淋
*/
String eggCone(String taste, String size);
}
实现
/**
* @Author wmf
* @Date 2022/1/18 15:37
* @Description 模拟一个冰淇淋机
*/
public class IceCreamMachine1 implements IceCreamMachine {
/**
* 模拟生产杯装冰淇淋
* @param taste 草莓/原味/巧克力
* @param size 大/中/小
* @return 冰淇淋
*/
@Override
public String cup(String taste, String size) {
System.out.println("开始生产杯装冰淇淋");
return taste + " 杯装冰淇淋("+size+")";
}
/**
* 模拟生产蛋筒冰淇淋
* @param taste 草莓/原味/巧克力
* @param size 大/中/小
* @return 冰淇淋
*/
@Override
public String eggCone(String taste, String size) {
System.out.println("开始生产蛋筒冰淇淋");
return taste + " 蛋筒冰淇淋("+size+")";
}
}
写个测试类来跟进整个过程
/**
* @Author wmf
* @Date 2022/1/18 15:45
* @Description
*/
public class ChainApplication {
public static void main(String[] args) {
IceCreamMachine machine = new IceCreamMachine1();
String iceCream = machine.cup("草莓", "大");
System.out.println(iceCream); // 正常输出: 草莓 杯装冰淇淋(大)
}
}
输出:
1.开始生产杯装冰淇淋
2.草莓 杯装冰淇淋(大)
过了一段时间
厂家
领导要做市场调研,想收集每天大家都选择了什么口味、什么规格,做市场分析。
为了应付这个工作,厂家雇了一个售货员看管机器,以后客户从售货员
处购买,这个售货员
先把需求记录下来,然后再去按需操作机器,再把冰淇淋给用户(这个售货员就是这台机器的代理)。
这种实现起来也很简单,最简单的方式就是代理模式
/**
* @Author wmf
* @Date 2022/1/18 15:58
* @Description 机器的代理
*/
public class IceCreamMachineProxy implements IceCreamMachine {
public IceCreamMachineProxy(IceCreamMachine iceCreamMachine) {
this.iceCreamMachine = iceCreamMachine;
}
private IceCreamMachine iceCreamMachine;
@Override
public String cup(String taste, String size) {
System.out.println("模拟记录需求"+taste+size);
return iceCreamMachine.cup(taste, size);
}
@Override
public String eggCone(String taste, String size) {
System.out.println("模拟记录需求"+taste+size);
return iceCreamMachine.eggCone(taste, size);
}
}
测试一下
public class ChainApplication {
public static void main(String[] args) {
IceCreamMachine machine = new IceCreamMachine1();
IceCreamMachine machineProxy = new IceCreamMachineProxy(machine);
String iceCream = machineProxy.cup("草莓", "大");
System.out.println(iceCream);
}
}
输出:
1.模拟记录需求(草莓 大)
2.开始生产杯装冰淇淋
3.草莓 杯装冰淇淋(大)
动态代理
通过
售货员
方式(代理模式),很轻松实现需求记录。
后来厂家
越做越大,冰淇淋机越来越多,引进了很多新机器,领导对新的机器质量不放心,于是规定说新机器不光要采集用户需求,又要检查产出的冰淇淋是否达标。
这样导致每个机器不光要配一个售货员
(代理),每个售货员干的活还不一样,人力成本和培训成本急速提高。
这时候来了个代理公司
说可以帮解决问题,只要厂家每次约定好某个冰淇淋机,计划好额外的工作(拦截器),我们就培养出一个售货员 ,而厂家只要专心生产机器并做好拦截计划就好(代理公司根据约定培养出销售员的过程就是动态代理)。
关于拦截计划,双方约定了一个规范,否则随便写会造成阅读障碍。
拦截计划的上下文
机器:机器的信息
产品:蛋筒或草莓
需求:客户的需求
开始:实际开始生产冰淇淋
代码模拟就是
/**
* @Author wmf
* @Date 2022/1/19 10:05
* @Description 信息的格式
*/
public interface MethodInvocation {
/**
* 机器的信息
* @return
*/
Object getThis();
/**
* 方法的信息(杯装还是蛋筒)
* @return
*/
Method getMethod();
/**
* 客户需求的信息(草莓/原味/巧克力 大/中/小)
* @return
*/
Object[] getArguments();
/**
* 开始生产标识
* @return
* @throws Throwable
*/
Object proceed() throws Throwable;
}
拦截计划的格式即为针对上下文如何处理
@FunctionalInterface
public interface MethodInterceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
两方规定好了这些规范,接下来就是
厂家
定制计划的时候,比如为了食品监督他们定制的计划可能是这样的
Object invoke(MethodInvocation invocation) {
//1.从打包信息invocation获取需求(invocation.getArguments())记在小本上
//2.开始生产冰淇淋(invocation.proceed())
//3.把生产出的冰淇淋拍个照发给厂家微信
}
对应的代码模拟
MethodInterceptor interceptor = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("记录需求:"+invocation.getArguments());
Object proceed = invocation.proceed();
System.out.println("对生产出的冰淇淋拍照"+proceed);
return proceed;
}
};
厂家
的冰淇淋机和拦截计划
都准备好了,代理公司
需要根据你的机器和拦截计划
给你定制培训一个售货员
(生成动态代理)
我们以jdk动态代理的方式模拟代理公司
的这个过程(不懂jdk动态代理自己补吧)
/**
* @Author wmf
* @Date 2022/1/12 18:23
* @Description 代理公司
*/
public class ProxyCompany implements AopProxy, InvocationHandler { // AopProxy相当于国家给所有代理公司下发的一个标准
public ProxyCompany() {
}
/**
* 设置拦截计划
* @param interceptor
*/
public void setInterceptor(MethodInterceptor interceptor) {
this.interceptor = interceptor;
}
/**
* 绑定冰淇淋机
* @param target
*/
public void setTarget(Object target) {
this.target = target;
}
/**
* 附加工作
*/
MethodInterceptor interceptor;
/**
* 冰淇淋机
*/
Object target;
/**
* 生成售货员(代理)
* @return
*/
@Override
public Object getProxy() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object getProxy(ClassLoader classLoader) {
return null;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 准备上下文
MethodInvocation invocation = new MethodInvocation() {
@Override
public Method getMethod() {
return method;
}
@Override
public Object[] getArguments() {
return args;
}
@Override
public Object proceed() throws Throwable {
return method.invoke(target, args);
}
@Override
public Object getThis() {
return target;
}
@Override
public AccessibleObject getStaticPart() {
return null;
}
};
// 需求来了之后按拦截计划去执行
return interceptor.invoke(invocation);
}
}
测试整个过程:
public class ChainApplication {
public static void main(String[] args) {
// 厂家的冰淇淋机
IceCreamMachine machine = new IceCreamMachine1();
// 厂家定制拦截计划
MethodInterceptor interceptor = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("记录需求:"+invocation.getArguments()[0]);
Object proceed = invocation.proceed();
System.out.println("对生产出的冰淇淋拍照"+proceed);
return proceed;
}
};
// 代理公司
ProxyCompany proxyCompany = new ProxyCompany();
// 绑定冰淇淋机到代理公司
proxyCompany.setTarget(machine);
// 绑定拦截计划到代理公司
proxyCompany.setInterceptor(interceptor);
// 生成售货员(机器的代理)
IceCreamMachine saler = (IceCreamMachine) proxyCompany.getProxy();
String iceCream = saler.eggCone("原味", "中");
}
}
输出:
1.记录需求:原味
2.开始生产蛋筒冰淇淋
3.对生产出的冰淇淋拍照 原味 蛋筒冰淇淋(中)
就这样过了一段时间,
厂家
和代理公司
配合很好,厂家
专注维护机器制定拦截计划
,代理公司
专注培养代理售货员
,分工明确,单一职责。
责任链模式
又过了一段时间,出现新需求,一台机器要加多个
拦截计划
:既要市场调研又要食品监督,而且拒绝修改原有的拦截计划,只想新加入拦截计划让新旧拦截计划一起生效,这样一来代理公司
又要做变动了
首先第一步,一个机器对应多个拦截计划
/** * 附加工作列表 */
List<MethodInterceptor> interceptors;
现在有多个计划,每个计划都包含了开始生产的标识,那怎么执行呐,循环执行肯定不行,人家客户要一个冰淇淋你可能给人家生成多个(点了多次开始按钮)。
代理公司想出了解决方案,给每个售货员配一个调度员
,这个调度员
一个个执行拦截计划
,当某个拦截计划
写着点击开始按钮时,不是实际的点击开始,而是进行下一个拦截计划
(相当于把开始按钮调包了),最后再没有拦截计划
时再实际点击开始按钮,这里有点绕,看下面的例子
比厂家
一台机器有两个计划
食品监督计划:
Object invoke(MethodInvocation invocation) {
//1.口味/规格记在食品监督小本上
//2.开始生产冰淇淋(invocation.proceed())
//3.生产出的冰淇淋拍个照发给厂家微信
}
市场调研计划:
Object invoke(MethodInvocation invocation) {
//1.口味/规格记在市场调研小本上
//2.开始生产冰淇淋(invocation.proceed())
}
实际来客户购买冰淇淋时,售货员拿到两个计划交给召唤出的调度员,调度员
是这么调度的
按第一个计划(食品监督计划)先走
1.1 口味/规格记在食品监督小本上
1.2 开始生产冰淇淋(拿到这个指令时,调度员实际开始进行下一个计划)
├── 2.1 口味/规格记在市场调研小本上
├── 2.2 开始生产冰淇淋 ***此时没有下一个拦截计划,真正点击开始按钮***
1.3 生产出的冰淇淋拍个照发给厂家微信
所以
调度员
的任务就是调包开始按钮,记录好拦截计划
列表执行到第几个,一个个执行,形成一条链
示意图如下:
代码模拟:
/**
* @Author wmf
* @Date 2022/1/19 13:59
* @Description 调度员
*/
public class Dispatcher {
/**
* 原打包信息
*/
private MethodInvocation methodInvocation;
/**
* 调包后的打包的信息
*/
private MethodInvocation changelingMethodInvocation;
/**
* 拦截计划列表
*/
private List<MethodInterceptor> chain;
/**
* 执行拦截计划的游标
*/
private Integer index = -1;
public Dispatcher(MethodInvocation methodInvocation, List<MethodInterceptor> chain) throws Throwable {
this.chain = chain;
Dispatcher that = this;
// 存储调度员原打包的信息
this.methodInvocation = methodInvocation;
// 调包打包信息里面的开始按钮
this.changelingMethodInvocation = new MethodInvocation() {
@Override
public Object getThis() {
return methodInvocation.getThis();
}
// 不管
@Override
public AccessibleObject getStaticPart() {
return null;
}
@Override
public Method getMethod() {
return methodInvocation.getMethod();
}
@Override
public Object[] getArguments() {
return methodInvocation.getArguments();
}
/** 调包开始按钮,工作执行计划的开始按钮实际上指向调度员的proceed**/
@Override
public Object proceed() throws Throwable {
return that.proceed();
}
};
}
/**
* 调度员的工作
* @return
* @throws Throwable
*/
public Object proceed() throws Throwable {
Object re;
// 最后一次,没有拦截计划了,开始生产冰淇淋
if (index == chain.size()-1) {
re = methodInvocation.proceed();
}else{ // 还有下一个拦截计划,继续按照下一个拦截计划执行
re = chain.get(++index).invoke(changelingMethodInvocation);
}
return re;
}
}
修改代理公司培养售货员逻辑,改动的地方标注了(改)
/**
* @Author wmf
* @Date 2022/1/12 18:23
* @Description 代理公司
*/
public class ProxyCompany implements AopProxy, InvocationHandler { // AopProxy相当于国家给所有代理公司下发的一个标准
public ProxyCompany() {
}
/**
* (改)添加拦截计划
* @param interceptor
*/
public void addInterceptor(MethodInterceptor interceptor) {
this.interceptors.add(interceptor);
}
/**
* 绑定冰淇淋机
* @param target
*/
public void setTarget(Object target) {
this.target = target;
}
/**
* (改)附加工作列表
*/
List<MethodInterceptor> interceptors = new ArrayList<>();
/**
* 冰淇淋机
*/
Object target;
/**
* 生成售货员(代理)
* @return
*/
@Override
public Object getProxy() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object getProxy(ClassLoader classLoader) {
return null;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 打包的信息 上面提到过
MethodInvocation invocation = new MethodInvocation() {
@Override
public Method getMethod() {
return method;
}
@Override
public Object[] getArguments() {
return args;
}
@Override
public Object proceed() throws Throwable {
return method.invoke(target, args);
}
@Override
public Object getThis() {
return target;
}
@Override
public AccessibleObject getStaticPart() {
return null;
}
};
// (改)召唤一个调度员
Dispatcher dispatcher = new Dispatcher(invocation, interceptors);
// (改)需求来了之后让调度员去执行
return dispatcher.proceed();
}
}
测试一下
public class ChainApplication {
public static void main(String[] args) {
// 厂家的冰淇淋机
IceCreamMachine machine = new IceCreamMachine1();
// 厂家定制食品监督计划
MethodInterceptor interceptor1 = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("记录需求至食品监督本:"+invocation.getArguments()[0]);
Object proceed = invocation.proceed();
System.out.println("拍照传给厂家微信:"+proceed);
return proceed;
}
};
// 厂家定制市场调研计划
MethodInterceptor interceptor2 = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("记录需求至市场调研本:"+invocation.getArguments()[0]);
return invocation.proceed();
}
};
// 代理公司
ProxyCompany proxyCompany = new ProxyCompany();
// 绑定冰淇淋机
proxyCompany.setTarget(machine);
// 绑定两个拦截计划
proxyCompany.addInterceptor(interceptor1);
proxyCompany.addInterceptor(interceptor2);
// 生成售货员(机器的代理)
IceCreamMachine saler = (IceCreamMachine) proxyCompany.getProxy();
String iceCream = saler.eggCone("原味", "中");
}
}
输出:
记录需求至食品监督本:原味
记录需求至市场调研本:原味
开始生产蛋筒冰淇淋
拍照传给厂家微信:原味 蛋筒冰淇淋(中)
完全满足了厂家
需求,调度员使用这种方式完成了拦截计划
一个接一个的执行,就是一条责任链
如上Dispatcher调包的方式还是使用的类似代理模式,写法有点丑,售货员完全可以把打包信息的工作也交给调度员,而调度员实现调包的方式也可以同过继承覆盖来实现,于是优化下代码,(按spring的命名 Dispatcher以下修改为ReflectiveMethodInvocation),优化后的代码如下
ProxyCompany:
/**
* @Author wmf
* @Date 2022/1/12 18:23
* @Description 代理公司
*/
public class ProxyCompany implements AopProxy, InvocationHandler { // AopProxy相当于国家给所有代理公司下发的一个标准
public ProxyCompany() {
}
/**
* 设置拦截计划
* @param interceptor
*/
public void addInterceptor(MethodInterceptor interceptor) {
this.interceptors.add(interceptor);
}
/**
* 绑定冰淇淋机
* @param target
*/
public void setTarget(Object target) {
this.target = target;
}
/**
* 附加工作列表
*/
List<MethodInterceptor> interceptors = new ArrayList<>();
/**
* 冰淇淋机
*/
Object target;
/**
* 生成售货员(代理)
* @return
*/
@Override
public Object getProxy() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object getProxy(ClassLoader classLoader) {
return null;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 召唤一个调度员,并把打包的工作也交给调度员
ReflectiveMethodInvocation dispatcher = new ReflectiveMethodInvocation(target, method, args, interceptors);
// 需求来了之后按拦截计划去执行
return dispatcher.proceed();
}
}
ReflectiveMethodInvocation(原Dispatcher):
/**
* @Author wmf
* @Date 2022/1/17 18:05
* @Description 调度员本身就是一个打包信息,所以继承了MethodInvocation,自己重新实现了proceed
*/
public class ReflectiveMethodInvocation implements MethodInvocation {
private Object target;
private Method method;
private Object[] args;
/**
* 拦截计划列表
*/
List<MethodInterceptor> chain;
private int index = -1;
public ReflectiveMethodInvocation(Object target, Method method, Object[] args, List<MethodInterceptor> chain) {
this.target = target;
this.method = method;
this.args = args;
this.chain = chain;
}
@Override
public Method getMethod() {
return this.method;
}
@Override
public Object[] getArguments() {
return args;
}
@Override
public Object proceed() throws Throwable {
Object re;
if (index == chain.size()-1) {
re = method.invoke(target, args);
}else{
re = chain.get(++index).invoke(this);
}
return re;
}
@Override
public Object getThis() {
return target;
}
@Override
public AccessibleObject getStaticPart() {
return null;
}
}
测试方法还是不变,输出结果页一样(写法干净了很多)
以上就是整个spring aop动态代理+方法拦截+责任链模式的整个思路,下面对比下源码
对比spring
ReflectiveMethodInvocation对比spring ReflectiveMethodInvocation:
ProxyCompany(使用的是jdk动态代理)对比spring的JdkDynamicAopProxy:
MethodInterceptor和MethodInvocation就是直接用的spring的,所以不用对比了。
over~
后期会再扩展一下,把整个aop都加上