第二梦 设计模式
Posted yrml
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第二梦 设计模式相关的知识,希望对你有一定的参考价值。
什么是代理
具体的做某一件事情的时候,不通过具体做事的人来完成,而是通过某一个中间人来完成,但实际上做事的人还是具体的人。这就是代理。比如有A和B两个人,B能购买亚瑟王手办,但是想买手办的C并不能联系到B。但是A可以联系到B,C又可以联系到A,这个时候C对A说我想买亚瑟王手办,A说我可以找B帮你,A这个时候就起到代理的作用
代理的应用场景
在实际生活中的代理有好多种应用场景,在不同场景下,我们使用的代理方式及代理过程也不尽相同,具体如下:
- 远程代理:为一个对象在不同的地址空间提供局部代表
-
虚拟代理:当需要创建开销非常大的对象时,通过使用一个小的对象代理一个大对象
-
保护代理:控制对目标对象的访问,给不同的用户提供不同的访问权限
-
智能引用代理:当需要在访问对象时附加额外的操作时(耗时操作、计算访问次数)
-
防火墙代理:保护目标不让恶意的用户靠近
-
Cache代理:为结果提供临时的存储空间,以便其他客户端调用
代码实战
- 静态代理
可以做到在不修改目标对象的功能前提下,对目标功能进行扩展
代理类和委托类实现了相同的接口,同时要实现相同的方法,重复代码。接口新增方法,所有的实现类和代理类都需要实现此方法,增加了代码维护的复杂度
1、首先定义一个商店性质的接口<IStore>
1 /** 2 * 卖书 3 */ 4 void saleBook(); 5 6 /** 7 * 卖吃的 8 */ 9 void saleFood(); 10 11 /** 12 * 开发票 13 */ 14 void invocing();
2、完成具体干事情的接口实现类<TaoBao、JingDong>
public class TaoBao implements IStore {
@Override
public void saleBook() {
System.out.println("【掏宝】Java书籍应有尽有...");
}
@Override
public void saleFood() {
System.out.println("【掏宝】Java造型食物应有尽有...");
}
@Override
public void invocing() {
System.out.println("【掏宝】我的发票带Java字样哟...");
}
}
public class JingDong implements IStore {
@Override
public void saleBook() {
System.out.println("【竞东】C++书籍走过路过不要错过...");
}
@Override
public void saleFood() {
System.out.println("【竞东】C++食物走过路过不要错过...");
}
@Override
public void invocing() {
System.out.println("【竞东】我们的发票自带C++字样哟...");
}
}
3、完成一个干代理工作的代理类,这里也实现商店性质接口<ProxyStore>,这里通过批人构造函数实现默认代理,通过提供带参构造函数实现指定代理。具体请查看测试。
1
2 3 /** 4 * 默认代理出售掏宝的东西 5 */ 6 public ProxyStore() { 7 this.store = new TaoBao(); 8 } 9 10 /** 11 * 扩展出售指定渠道的货物 12 * @param store 渠道 13 */ 14 public ProxyStore(IStore store) { 15 this.store = store; 16 } 17 18 @Override 19 public void saleBook() { 20 store.saleBook(); 21 } 22 23 @Override 24 public void saleFood() { 25 store.saleFood(); 26 } 27 28 @Override 29 public void invocing() { 30 store.invocing(); 31 }
4、消费者购买东西<SomeOne>,请注意上一步3提到的指定代理
1 ProxyStore store; 2 store = new ProxyStore(); 3 System.out.println("------买代理者默认代理的------"); 4 store.saleBook(); 5 store.saleFood(); 6 store.invocing(); 7 System.out.println("\\n"); 8 9 store = new ProxyStore(new JingDong()); 10 System.out.println("------指定代理者某渠道的货物------"); 11 store.saleBook(); 12 store.saleFood(); 13 store.invocing();
5、输出结果
1 ------买代理者默认代理的------ 2 【掏宝】Java书籍应有尽有... 3 【掏宝】Java造型食物应有尽有... 4 【掏宝】我的发票带Java字样哟... 5 6 7 ------指定代理者某渠道的货物------ 8 【竞东】C++书籍走过路过不要错过... 9 【竞东】C++食物走过路过不要错过... 10 【竞东】我们的发票自带C++字样哟...
- 动态代理
前面的静态代理,是在代码编译的时候就写好代理类,而动态代理则是在程序运行过程中动态的生成代理类。而且比静态类好的是不用写一个与具体动作完全相同的封装类。使用动态代理还可以在不改变原有实现的情况下,对原有实现进行增强。
- JDK动态代理
1、定义具体实现类
1 public class BianLiDian { 2 3 public void sale(){ 4 System.out.println("【商店】这是一瓶原生牛奶..."); 5 } 6 }
2、定义代理类
1 public class ProxyFactory { 2 3 /** 4 * 维护目标对象 5 */ 6 private Object target; 7 8 public ProxyFactory(Object target) { 9 this.target = target; 10 } 11 12 /** 13 * 给目标对象生成代理对象 14 */ 15 public Object getProxyInstance(){ 16 return Proxy.newProxyInstance( 17 //获取目标对象的类加载器 18 target.getClass().getClassLoader(), 19 //获取目标对象的接口信息 20 target.getClass().getInterfaces(), 21 /* 22 * InvocationHandler is the interface implemented by the 23 * invocation handler of a proxy instance. 24 * Each proxy instance has an associated invocation handler. 25 * When a method is invoked on a proxy instance, 26 * the method invocation is encoded and dispatched to the 27 * invoke method of its invocation handler. 28 */ 29 new InvocationHandler() { 30 @Override 31 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 32 //这里可以通过获取方法的name,实现对不同方法不同的增强处理 33 System.out.println("买东西前确认银行卡余额..."); 34 method.invoke(target, args); 35 System.out.println("确认收货"); 36 return null; 37 } 38 } 39 ); 40 } 41 }
3、消费者测试
1 public static void main(String[] args) { 2 IStore target = new TaoBao(); 3 //对象的原始类型 4 System.out.println(target.getClass()); 5 6 IStore proxy = (IStore) new ProxyFactory(target).getProxyInstance(); 7 //内存中动态代理生成的对象类型 8 System.out.println(proxy.getClass()); 9 10 //指定具体需要代理的对象 11 proxy.saleFood(); 12 13 }
4、执行结果
1 class com.yrml.proxy.store.impl.TaoBao 2 class com.sun.proxy.$Proxy0 3 买东西前确认银行卡余额... 4 【掏宝】Java造型食物应有尽有... 5 确认收货
- CGLIB动态代理
采用字节码技术,通过目标类的字节码为目标类创建子类,在子类中采用方法拦截技术对父类方法的调用进行拦截,顺势织入横切逻辑
底层采用自己处理框架ASM,来转换字节码并生成新类
1、代理类变更
package com.yrml.proxy.cglib; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * @des * @author John Tan * @date 2018/5/22 */ public class ProxyCglib implements MethodInterceptor { private Object target; public ProxyCglib(Object target) { this.target = target; } /** * 获取目标对象的代理对象 */ public Object getProxyInstance(){ //字节码增强器工具类 Enhancer enhancer = new Enhancer(); //设置父类,继承代理类 enhancer.setSuperclass(target.getClass()); //设置回调函数 enhancer.setCallback(this); //创建子类(代理对象) return enhancer.create(); } /** * 回调方法 * @param obj 代理对象 * @param method 委托方法 * @param args 方法参数 * @param proxy 代理方法的MethodProxy对象 * @return 方法执行结果 * @throws Throwable 异常 */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("【代理】我们将为您送上一杯热牛奶..."); System.out.println("【代理】首先从商店购买..."); method.invoke(target, args); System.out.println("【代理】加热..."); System.out.println("【代理】这是您的热牛奶..."); return null; } }
2、消费者测试
1 public class ProxyTest3 { 2 3 public static void main(String[] args) { 4 5 BianLiDian bianLiDian = new BianLiDian(); 6 System.out.println(bianLiDian.getClass()); 7 8 BianLiDian proxy = (BianLiDian) new ProxyCglib(bianLiDian).getProxyInstance(); 9 proxy.sale(); 10 } 11 }
3、执行结果
1 class com.yrml.proxy.store.impl.BianLiDian 2 【代理】我们将为您送上一杯热牛奶... 3 【代理】首先从商店购买... 4 【商店】这是一瓶原生牛奶... 5 【代理】加热... 6 【代理】这是您的热牛奶...
代码托管
该实例托管在我的 GitHub上,为gradle项目,小伙伴可以直接检出构建运行测试
参考文章
以上是关于第二梦 设计模式的主要内容,如果未能解决你的问题,请参考以下文章