首页 > 代码库 > 代理模式
代理模式
java 的代理模式可以有静态代理模式和动态代理模式。下面的内容,大体的流程如下:
所谓代理模式,就是代理处理其他类的操作。具体点就是,类似于北京的中介公司一样,自己没有实际的房屋和出租权,而是通过跟真正的房东商谈,代理出租房屋,从中谋取利益。这种模式的好处是,隐藏了真正的处理类,可以使代理类的操作简单化,而代理的操作可以复杂和有更多的操作。就如中介公司如果把房东透漏给你了,你就不会再经过中介去租房屋了,中介也不能再中介抽取钱了。
(I)静态代理模式
首先,看一下简单的静态代理模式。静态代理模式的类图如下:
其中,我定义了接口 PersonDao,实现类 PersonDaoImpl,代理类 PersonProxy,另外两个 After、Before 是我设置的后置方法类和前置方法的封装类。具体的代码如下:
(1)Dao 接口:
1 /** 2 * 0.0.0.1 3 */ 4 package com.gaoqing.common.mode; 5 6 /** 7 * Person 到 接口 8 * @author gaoqing 9 * 2014-6-1710 */11 public interface PersonDao {12 13 public void info();14 15 }
上述 Dao 接口,只是定义了一个简单的 info() 方法,打印出用户信息的操作。
(2)DaoImpl 实现类:
1 /** 2 * 0.0.0.1 3 */ 4 package com.gaoqing.common.mode; 5 6 /** 7 * Person dao 的实现类 8 * @author gaoqing 9 * 2014-6-1710 */11 public class PersonDaoImpl implements PersonDao {12 13 private String name;14 private String address;15 /**16 * 构造方法17 */18 public PersonDaoImpl() {19 20 }21 22 /**23 * 构造方法24 */25 public PersonDaoImpl(String name, String address) {26 super();27 this.name = name;28 this.address = address;29 }30 31 /**32 * @see com.gaoqing.common.mode.PersonDao#info()33 */34 @Override35 public void info() {36 37 System.out.println("姓名:" + name + "," + "地址:" + address);38 39 }40 41 /**42 * @return the name43 */44 public String getName() {45 return name;46 }47 48 /**49 * @param name the name to set50 */51 public void setName(String name) {52 this.name = name;53 }54 55 /**56 * @return the address57 */58 public String getAddress() {59 return address;60 }61 62 /**63 * @param address the address to set64 */65 public void setAddress(String address) {66 this.address = address;67 }68 }
(3)代理类如下:
1 /** 2 * 0.0.0.1 3 */ 4 package com.gaoqing.common.mode; 5 6 /** 7 * 静态代理方法 8 * @author gaoqing 9 * 2014-6-2110 */11 public class StaticProxyTest implements PersonDao{12 13 /** PersonDao 方法 */14 private PersonDao personDao;15 /** 前置方法对象 */16 private Before before;17 /** 后置方法对象 */18 private After after;19 20 /**21 * 构造方法22 */23 public StaticProxyTest() {24 25 }26 27 /**28 * 构造方法29 * @param personDao PersonDao 对象30 * @param before 前置方法对象31 * @param after 后置方法对象32 */33 public StaticProxyTest(PersonDao personDao, Before before, After after) {34 super();35 this.personDao = personDao;36 this.before = before;37 this.after = after;38 }39 40 /**41 * @see com.gaoqing.common.mode.PersonDao#info()42 */43 @Override44 public void info() {45 //执行前置方法46 before.execute();47 48 //执行具体的请求方法49 personDao.info();50 51 //执行后置方法52 after.execute();53 }54 55 /**56 * @return the personDao57 */58 public PersonDao getPersonDao() {59 return personDao;60 }61 62 /**63 * @param personDao the personDao to set64 */65 public void setPersonDao(PersonDao personDao) {66 this.personDao = personDao;67 }68 69 /**70 * @return the before71 */72 public Before getBefore() {73 return before;74 }75 76 /**77 * @param before the before to set78 */79 public void setBefore(Before before) {80 this.before = before;81 }82 83 /**84 * @return the after85 */86 public After getAfter() {87 return after;88 }89 90 /**91 * @param after the after to set92 */93 public void setAfter(After after) {94 this.after = after;95 }96 }
上面的代理类的特点是:和 DaoImpl 的实现类一样,都实现了 PersonDao 接口,只是在自己的 info() 方法,调用实现类的具体方法,而在自己的操作中,添加了更多别的附加的操作。
(II)动态代理模式
如果只是仅仅使用简单的静态代理也可以实现某些面向切面的编程,比如插入日志什么的,可以将插入的内容放到 Before 和 After 类中。但是,如果我需要很多的代理类的时候,就需要像上面一样,写很多的类似的 PersonProxy 类,这样就很类了。随之产生的一种处理方式就出现了--动态代理。这样就可以只编写一个代理类,去代理所有自己想要代理类,更可以把那些相同的代码,分离到某些类中,集中进行管理,方便而且优雅。
(II.1)JDK 实现
我依旧使用上面的 PersonDao 和 PersonDaoImpl,具体的类图如下:
正如,在类图中的注释,这种动态代理的方式,是在 JDK 中,动态的生成一个类,该类和代理类一样,实现了相同的接口,拥有和代理类同样的方法。
(4)动态代理类如下:
1 /** 2 * 0.0.0.1 3 */ 4 package com.gaoqing.common.mode; 5 6 import java.lang.reflect.Proxy; 7 8 /** 9 * 代理类10 * @author gaoqing11 * 2014-6-1712 */13 public class ProxyPerson {14 15 /** 被代理对象 */16 private Object obj;17 18 /** 调用处理类对象 */19 private MyInvocationHandler handler;20 21 /**22 * 构造方法23 */24 public ProxyPerson() {25 26 }27 28 /**29 * 构造方法30 * @param obj 被代理的对象31 * @param handler 具体的调用处理类32 */33 public ProxyPerson(Object obj, MyInvocationHandler handler) {34 super();35 this.obj = obj;36 this.handler = handler;37 }38 39 /**40 * 生成代理类41 * @author 高青42 * 2014-6-1743 * @return obj 代理类44 */45 public Object getProxyPerson(){46 47 return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);48 }49 50 /**51 * @return the obj52 */53 public Object getObj() {54 return obj;55 }56 57 /**58 * @param obj the obj to set59 */60 public void setObj(Object obj) {61 this.obj = obj;62 }63 64 /**65 * @return the handler66 */67 public MyInvocationHandler getHandler() {68 return handler;69 }70 71 /**72 * @param handler the handler to set73 */74 public void setHandler(MyInvocationHandler handler) {75 this.handler = handler;76 }77 }
(4)具体的调用处理类如下:
1 /** 2 * 0.0.0.1 3 */ 4 package com.gaoqing.common.mode; 5 6 import java.lang.reflect.InvocationHandler; 7 import java.lang.reflect.Method; 8 9 /** 10 * 自定义的调用处理器 11 * @author gaoqing 12 * 2014-6-17 13 */ 14 public class MyInvocationHandler implements InvocationHandler { 15 16 /** 被代理的类 */ 17 private Object obj; 18 19 /** 方法前对象 */ 20 private Before before; 21 22 /** 方法后对象 */ 23 private After after; 24 25 /** 26 * 构造方法 27 */ 28 public MyInvocationHandler() { 29 30 } 31 32 /** 33 * 构造方法 34 */ 35 public MyInvocationHandler(Object obj) { 36 super(); 37 this.obj = obj; 38 } 39 40 /** 41 * 构造方法 42 */ 43 public MyInvocationHandler(Object obj, Before before, After after) { 44 super(); 45 this.obj = obj; 46 this.before = before; 47 this.after = after; 48 } 49 50 /** 51 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) 52 */ 53 @Override 54 public Object invoke(Object proxy, Method method, Object[] args) 55 throws Throwable { 56 /* 57 * 代理的具体处理方式 58 */ 59 60 //方法前操作 61 before.execute(); 62 63 Object invokeValue =http://www.mamicode.com/ method.invoke(obj, args); 64 65 //方法后操作 66 after.execute(); 67 68 return invokeValue; 69 } 70 71 /** 72 * @return the obj 73 */ 74 public Object getObj() { 75 return obj; 76 } 77 78 /** 79 * @param obj the obj to set 80 */ 81 public void setObj(Object obj) { 82 this.obj = obj; 83 } 84 85 /** 86 * @return the before 87 */ 88 public Before getBefore() { 89 return before; 90 } 91 92 /** 93 * @param before the before to set 94 */ 95 public void setBefore(Before before) { 96 this.before = before; 97 } 98 99 /**100 * @return the after101 */102 public After getAfter() {103 return after;104 }105 106 /**107 * @param after the after to set108 */109 public void setAfter(After after) {110 this.after = after;111 }112 }
(II.2)Cglib 实现
从上面可以看到,使用 JDK 自带的接口实现动态代理的话,需要被代理的对象必须实现一个接口。这样,就存在了一种限制,如果我需要代理那些普通的类那该怎么办呢?这个时候可以使用 cglib 类库,它可以实现代理那些没有实现接口的类。Cglib 是一个优秀的动态代理框架,它的底层使用 ASM 在内存中动态的生成被代理类的一个子类。具体的 ASM 的使用,目前还没用过啊,现在只是简单的使用以下 cglib 。
使用 cglib 框架实现动态代理时,需要导入两个包,一个是其依赖的 asm.jar,一个是 cglib.jar。注意:需要导入合适的包,如果包的版本不对,是会报错的,如果报错了,可以试着换下 cglib 的 jar 包。在这个代理的过程中,主要用到了 cglib 的:
(1)Enhancer – 主要的增强类,类似于 JDK 的 Proxy 类,产生具体的代理类
(2)MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现,就是将代理类请求的方法,进行拦截到被代理类对象进行处理,类似于 JDK 的 InvocationHandler 接口的 invoke()方法
(3)MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用
具体的实现如下:
1 /** 2 * 0.0.0.1 3 */ 4 package com.gaoqing.common.mode; 5 6 import java.lang.reflect.Method; 7 8 import net.sf.cglib.proxy.Enhancer; 9 import net.sf.cglib.proxy.MethodInterceptor;10 import net.sf.cglib.proxy.MethodProxy;11 12 /**13 * 使用 Cglib 框架实现的代理类14 * @author gaoqing15 * 2014-6-1916 */17 public class DynamicProxyByCGLIBTest {18 19 /**20 * 构造方法21 */22 public DynamicProxyByCGLIBTest() {23 24 }25 26 /**27 * 主线程方法28 * @author 高青29 * 2014-6-1930 * @param args 参数字符串集31 * @return void 空32 */33 public static void main(String[] args) {34 35 //代理类36 final PersonDaoImpl personDaoImpl = new PersonDaoImpl("gaoqing", "beijing");37 38 //得到增强类39 Enhancer enhancer = new Enhancer();40 41 //设置增强类的父类42 enhancer.setSuperclass(PersonDaoImpl.class);43 44 enhancer.setCallback(new MethodInterceptor() {45 46 @Override47 public Object intercept(Object target, Method method, Object[] args,48 MethodProxy proxy) throws Throwable {49 50 Object invoke = method.invoke(personDaoImpl, args); //也可以使用 proxy 的 invokeSupre(target, args)51 52 return invoke;53 }54 });55 PersonDaoImpl personDaoImplProxy = (PersonDaoImpl) enhancer.create();56 57 personDaoImplProxy.info();58 59 }60 }
(III)代理模式的使用场景
上述就是简单的代理模式的实现。动态代理的方式,可以实现面向 AOP 的编程,具体的使用是大家知道的动态的插入日志,添加事物,记录用户的操作记录等。具体的框架应用就是 Spring 的 AOP 实现,为它们提供为方法的Interception(拦截),在 hibernate 中,使用 Cglib 来代理单端single-ended(多对一和一对一)关联等等。