学习笔记-设计模式之代理模式
本文内容源于视频教程,若有侵权,请联系作者删除。
一、概念
代理模式(Porxy Pattern)是指为其他对象提供一种代理,以控制对这个对象的访问。
简言之:房产中介,售票黄牛都属于代理。
二、实现
1.静态代理
需求:实现父亲替儿子找对象。
首先构造一个儿子,并且具有找对象的方法。
1 public class Son { 2 3 public void findLove(){ 4 System.out.println("儿子找对象,要求肤白貌美,大长腿"); 5 } 6 }
构造一个帮儿子找对象的父亲,在找对象之前需要筛选,找到后需要安排后续事情。
1 public class Father { 2 3 private Son son; 4 5 public Father(Son son) { 6 this.son = son; 7 } 8 9 public void findLove(){ 10 System.out.println("父亲帮儿子筛选对象"); 11 this.son.findLove(); 12 System.out.println("准备婚礼。。"); 13 } 14 }
测试类如下。
1 public class StaticProxyTest { 2 3 public static void main(String[] args) { 4 Father father = new Father(new Son()); 5 father.findLove(); 6 } 7 }
运行结果
父亲帮儿子筛选对象
肤白貌美,大长腿
准备婚礼。。
虽然是父亲帮儿子找对象,但是最终调用的还是儿子的findLove方法。这就是代理模式。
从代码中也能看出,父亲在找对象前会帮儿子筛选对象,找到后会帮忙婚礼。在到了方法增强的效果的同时,没有改变原来的方法。可见代理模式遵循了开闭原则。
以上的缺点也很明显,父亲只能帮儿子找对象,不能给其他人找对象,极大限制了应用范围。随着时代发展,找对象的需求越来越多,由此衍生出了一种职业——媒婆。
媒婆可以给任何人找对象,在物色对象时会筛选符合要求的青年,在找对象之后会准备婚礼。下面通过两种动态代理实现。
2.动态代理
2.1 jdk动态代理
首先创建接口Person,所有实现此接口的类都可以找对象。
1 public interface Person { 2 3 void findLove(); 4 5 }
接着创建一个女儿,实现person并且具有找对象方法。
1 public class Girl implements Person { 2 3 @Override 4 public void findLove() { 5 System.out.println("女儿找对象:高富帅"); 6 } 7 }
然后创建媒婆,能帮所有人找对象,并且找对象前和找对象后都有相应工作。
1 public class JdkMeiPo implements InvocationHandler { 2 3 private Person person; 4 5 public Person getInstance(Person person) { 6 this.person = person; 7 Class<? extends Person> clazz = person.getClass(); 8 return (Person) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); 9 } 10 11 @Override 12 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 13 before(); 14 Object invoke = method.invoke(this.person, args); 15 after(); 16 return invoke; 17 } 18 19 private void before(){ 20 System.out.println("找对象前筛选对象"); 21 } 22 23 private void after(){ 24 System.out.println("找对象后举办婚礼"); 25 } 26 }
测试类
1 public class JdkProxyTest { 2 3 public static void main(String[] args) { 4 Person person = new JdkMeiPo().getInstance(new Girl()); 5 person.findLove(); 6 } 7 }
执行结果
找对象前筛选对象
女儿找对象:高富帅
找对象后举办婚礼
这里的媒婆不仅能给女儿找对象,只要是继承了Person的类都可以找。
jdk代理的核心是实现InvocationHandler类并实现了其invoke方法,媒婆类中的getInstance方法返回的实际上是girl的一个代理对象,这个对象由jdk生成,该对象的findLove方法不仅有Girl的findLove方法,同时也有媒婆类中的before和after方法。
jdk动态代理实现原理:
1.拿到被代理类的引用,并且获取它的所有接口
2.JDK Proxy类重新生成一个类,实现了被代理类的所有方法
3.动态生成java代码,把增强逻辑加入到新生成的代码中
4.编译新生成的类
5.加载并运行
2.2 Cglib动态代理
在上面的基础上,写一个cglib的媒婆类
1 public class CglibMeiPo implements MethodInterceptor { 2 3 public Object getInstance(Class<?> clazz){ 4 Enhancer enhancer = new Enhancer(); 5 enhancer.setSuperclass(clazz); 6 enhancer.setCallback(this); 7 return enhancer.create(); 8 } 9 10 @Override 11 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 12 before(); 13 Object invoke = methodProxy.invokeSuper(o, objects); 14 after(); 15 return invoke; 16 } 17 18 private void before(){ 19 System.out.println("找对象前筛选对象"); 20 } 21 22 private void after(){ 23 System.out.println("找对象后举办婚礼"); 24 } 25 }
测试类:
1 public class CglibProxyTest { 2 3 public static void main(String[] args) { 4 Son instance = (Son)new CglibMeiPo().getInstance(Son.class); 5 instance.findLove(); 6 } 7 }
输出结果
找对象前筛选对象
肤白貌美,大长腿
找对象后举办婚礼
与Jdk动态代理一样,Cglib同样动态生成了一个代理类,不同的是Jdk通过实现接口的方式生成,Cglib通过继承目标类实现。
2.3.CGLib 和 JDK 动态代理对比
1.JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象。
2.JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低。
3.JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法,CGLib 执行效率更高。