标签:
一、关于代理模式的几个概念
1.比如A要去租房,通过中介B,租到C家的房子,这里B就是A的代理。
代理可以认为是之前介绍的组合和继承的折中模式,它有继承的部分也有组合的部分。
2、代理的结构
1)抽象主题角色: 声明了真实主题和代理主题的共同的接口。
2)代理主题角色:内部含有真实主题的引用, 从而可以再任何时候操作真实主体对象;提供了一个与真实主题相同的接口,以便可以替代真实主题。
3)真实主题角色:抽象主题的实现,是具体被代理的对象。
二、三种常见的代理模式
1.普通代理模式
1 package com.test; 2 3 /** 4 * 抽象主题角色 5 */ 6 public abstract class Subject { 7 public abstract void request(); 8 } 9 10 //-------------分割线-------------------------- 11 package com.test; 12 13 /** 14 * 真实主题对象 15 */ 16 public class RealSubject extends Subject{ 17 @Override 18 public void request() { 19 System.out.println("真实主题对象"); 20 } 21 } 22 23 //-------------分割线-------------------------- 24 package com.test; 25 26 /** 27 * 代理对象 28 */ 29 public class ProxySubject extends Subject{ 30 31 private RealSubject realSubject;//代理对象里面放真实对象 32 33 public ProxySubject() { 34 35 } 36 37 @Override 38 public void request() { 39 pre(); 40 if (null == realSubject) { 41 realSubject = new RealSubject(); 42 } 43 realSubject.request(); 44 after(); 45 } 46 47 private void pre() { 48 System.out.println("前置"); 49 } 50 51 private void after() { 52 System.out.println("后置"); 53 } 54 55 public static void main(String[] args) { 56 Subject sub = new ProxySubject();//父类声明,指向子类代理对象 57 sub.request(); 58 } 59 }
结果如下:
使用代理模式的优点,是可以在真实请求的前后,加一些控制。使用这种方式,要求知道接口和实现类的信息。
2.使用JDK自带的Proxy,InvocationHandler,Mehtod来实现代理模式(这种是动态代理,对比来讲前一种是静态代理)
1 package com.test; 2 3 /** 4 * 抽象主题角色 5 */ 6 public interface Subject { //这里需要是接口,跟上面的例子相比,上面的例子可以写成抽象类,也可以写成接 //口,但是这个例子里面将只能够写成接口 7 public abstract void request(); 8 } 9 10 //-------------------------------分割线---------------------------- 11 package com.test; 12 13 /** 14 * 真实主题对象 15 */ 16 public class RealSubject implements Subject{ 17 @Override 18 public void request() { 19 System.out.println("真实主题对象"); 20 } 21 } 22 23 //-------------------------------分割线---------------------------- 24 package com.test; 25 26 import java.lang.reflect.InvocationHandler; 27 import java.lang.reflect.Method; 28 import java.lang.reflect.Proxy; 29 30 /** 31 * 创建代理类 32 */ 33 public class JDKProxySubject implements InvocationHandler{ 34 private Object targetObject;//目标对象, 我们这个工厂就是生产出这个对象的代理对象 35 36 //创建代理对象的具体方法实现 37 public Object createProxyInstance(Object targetObject) { 38 39 this.targetObject = targetObject; 40 /*这个Proxy对象就是jdk自带的一个类,用其newProxyInstance方法 41 来进行创建代理对象, 第一个参数是类加载器, 第二个参数是实现的接口数组 42 第个参数记住用this即可 ,这么理解,他就是一个句柄, 43 给别人握的, 这个句柄对象需要实现InvocationHandler接口*/ 44 return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), 45 this.targetObject.getClass().getInterfaces(),this); 46 } 47 48 49 @Override 50 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 51 52 pre();//前置处理 53 Object result = null; 54 result = method.invoke(this.targetObject,args); 55 after();//后置处理 56 return result; 57 } 58 59 private void pre() { 60 System.out.println("前置"); 61 } 62 63 private void after() { 64 System.out.println("后置"); 65 } 66 67 public static void main(String[] args) { 68 JDKProxySubject factory = new JDKProxySubject();//创建工厂 69 Subject proxySubject = (Subject)factory.createProxyInstance(new RealSubject()); 70 proxySubject.request(); 71 } 72 }
结果同1
这个例子中,首先请关注下Subject处, 上一个例子中Subject是抽象类(换成接口也可以),但是这个例子中,只能使用接口。因为由createProxyInstance
方法生成的代理对象,实际上是Proxy类的一个子类,所以这里向上转型时Subject为接口才适合。使用这种方式,要求提前知道接口信息即可。
3.使用工具CGlib实现动态代理,需要导入cglib的jar
1 package com.test; 2 3 import java.lang.reflect.Method; 4 5 import net.sf.cglib.proxy.Enhancer; 6 import net.sf.cglib.proxy.MethodInterceptor; 7 import net.sf.cglib.proxy.MethodProxy; 8 9 public class CGlibProxyFactory implements MethodInterceptor{ 10 private Object targetObject; 11 12 public Object createProxyInstance(Object targetObject) { 13 pre(); 14 this.targetObject = targetObject; 15 Enhancer enhance = new Enhancer();//创建一个类 16 enhance.setSuperclass(this.targetObject.getClass());//非final方法进行覆盖 17 enhance.setCallback(this);//设置回调为本身 18 after(); 19 return enhance.create(); 20 } 21 22 private void pre() { 23 System.out.println("前置"); 24 } 25 26 private void after() { 27 System.out.println("后置"); 28 } 29 30 31 @Override 32 public Object intercept(Object proxy, Method method, Object[] args, 33 MethodProxy arg3) throws Throwable { 34 35 Object result = null; 36 result = method.invoke(this.targetObject, args); 37 return result; 38 } 39 40 public static void main(String[] args) { 41 CGlibProxyFactory factory = new CGlibProxyFactory(); 42 Subject subject = (Subject)factory.createProxyInstance(new RealSubject()); 43 subject.request(); 44 } 45 }
结果同1
使用CGlib的方式,跟上一种Proxy的方式相比,甚至连接口都不需要提前知道。(性能差点)
三、关于代理模式和另几个模式之前的关系
1.代理模式和适配器模式:粗看上去它们都可以视为一个对象提供一种前置的接口,但是适配器模式的用意在于要改变所使用的接口,而代理模式并不能改变对象的接口。
2.代理模式和装饰模式:装饰模式是要为所装饰的对象增强功能,而代理模式是对对象的使用施加控制。
标签:
原文地址:http://www.cnblogs.com/kaiguoguo/p/4678425.html