Java设计模式之四——代理模式

来源:互联网 发布:淘宝上的车秒贷靠谱吗 编辑:程序博客网 时间:2024/04/28 04:19

人的理想志向往往和他的能力成正比。——约翰逊

一、前言

大家有没有这样一种经历,大学毕业后从学校来到一个默生的城市找工作,住宿的问题是我们必须要解决的问题;有些人是自己去找房主进行租房,而肯定也有些人是先找到中介,然后由中介按照你的要求去联系房主帮你找到房子;当然,中介是要收中介费的。转换到我们Java设计模式中,这个中介其实就是一个代理。

二、什么是代理模式。

1、在某些特定的情况下,一个客户端对象不想或者不能直接去引用另外一个对象,但是代理对象可以在客户端对象和目标对象中起到一个中介(桥梁)的作用;此时,我们就可以使用代理对象来间接的去引用目标对象,这就是代理模式的基本思想。

2、代理模式的作用:为其他对象(即上面所说的目标对象)提供一个代理用于控制对这个目标对象的访问权。

  3、根据惯例,每个设计模式都会涉及相应的角色,来为这种模式提供实际的操作。代理模式一般涉及到的角色如下:

1)抽象角色:用于声明代理对象和真实对象的一组共同接口;该角色通常由抽象类或者接口来充当。

2)代理角色:代理对象角色其内部拥有真实对象的引用,从而代理对象就可以操纵真实对象;同时,代理对象也会拥有和真实对象一样的接口(或者父类)以便在任何时候代理对象都可以代替真实对象完成真实对象所需要完成的业务逻辑;再者,像前言部分说的中介,代理对象在完成真实对象的业务逻辑时,可以附加上自己的一些操作。

3)真实角色:代理角色所代表的真实对象,也就是客户端最终需要引用的对象。

三、代理模式的分类和实现

1、代理对象可以分为静态代理和动态代理(Dynamic Proxy),其中静态代理相对简单,而动态代理则涉及到Java中的反射机制。

2、静态代理的实现:

2.1 抽象角色,例子代理如下:

/** *  * @ClassName: AbstractSubject * @Description: 抽象角色 * @author admin * @date 2016年12月18日 下午1:35:36 * @version V1.0 */public abstract class AbstractSubject {/** *  * @Title: doRentHouse * @Description: 租房子 * @param  * @return void  * @throws */public abstract void doRentHouse();}
2.2 真实角色,例子代码如下:

/** *  * @ClassName: RealSubject * @Description: 具体角色 * @author admin * @date 2016年12月18日 下午1:36:18 * @version V1.0 */public class RealSubject extends AbstractSubject {@Overridepublic void doRentHouse() {System.out.println("房主出租房子!");}}
2.3 代理角色,例子代码如下:

/** *  * @ClassName: ProxySubject * @Description: 代理角色 * @author admin * @date 2016年12月18日 下午1:37:08 * @version V1.0 */public class ProxySubject extends AbstractSubject {//代理角色内部引用了真实角色private RealSubject realSubject;@Overridepublic void doRentHouse() {//在真实角色操作之前代理角色所附加的操作preDoRentHouse();if(null == realSubject){realSubject = new RealSubject();}//真实角色所完成的realSubject.doRentHouse();//在真实角色操作之后代理角色所附加的操作postDoRentHouse();}private void preDoRentHouse(){System.out.println("中介跟房客介绍房子信息");}private void postDoRentHouse(){System.out.println("中介收取房客的中介费");}}
2.4 客户端调用,例子代码如下:

/** *  * @ClassName: Client * @Description: 客户端 * @author admin * @date 2016年12月18日 下午4:35:14 * @version V1.0 */public class Client {public static void main(String[] args) {AbstractSubject subject = new ProxySubject();subject.doRentHouse();}}
2.5 执行结果如下:


2.6 分析:

1)由以上例子和执行结果可以知道,客户端实际需要去调用的是RealSubject类的doRentHouse()方法,而现在使用ProxySubject类来代理RealSubject类也同样实现可效果,同时,这个代理类还封装了其他的一些方法(preDoRentHouse()和postDoRentHouse()方法)来实现一些额外的操作。

2)另外,如果按照静态的方式来实现代理模式,那么真实角色必须是事先就已经存在且作为代理角色的一个内部属性(成员变量)。但是在实际使用时,每一个真实角色必须需要有对应的一个代理角色,这样就会导致两个很明显的问题:第一、我们项目中类会急剧膨胀;第二、假如实现不知道真实角色,那么将怎么进行代理呢?

这就需要下面讲到的动态代理。

3、动态代理的实现

3.1 抽象角色,例子代码如下:

/** *  * @ClassName: AbstractSubject * @Description: 抽象角色 * @author admin * @date 2016年12月18日 下午2:02:24 * @version V1.0 */public interface AbstractSubject {public void doRentHouse();}
3.2 真实角色,例子代码如下:

/** *  * @ClassName: RealSubject * @Description: 真实角色 * @author admin * @date 2016年12月18日 下午2:03:12 * @version V1.0 */public class RealSubject implements AbstractSubject {@Overridepublic void doRentHouse() {System.out.println("房主出租房子!");}}
3.3 代理角色,例子代码如下:

package com.dynamicproxy.pattern.test;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;/** *  * @ClassName: DynamicSubject * @Description: 代理角色 * 代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传递进来一个对象 * 此外,该类还实现了invoke方法,该方法中的method.invoke其实就是调用被代理类对象的将 * 要执行的方法,方法参数是sub,表示该方法从属于sub,通过动态代理类,我们可以在 * 执行真实对象的方法前后加入自己的一些额外方法。 * @author admin * @date 2016年12月18日 下午2:03:49 * @version V1.0 */public class DynamicSubject implements InvocationHandler {// 真实对象private Object object;// 构造方法里面为真实对象赋值public DynamicSubject(Object object) {this.object = object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("before doRentHouse: " + method);method.invoke(object, args);System.out.println("after doRentHouse: " + method);return null;}}
3.4 客户端调用,例子代码如下:

package com.dynamicproxy.pattern.test;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;/** *  * @ClassName: Client * @Description: 客户端 * @author admin * @date 2016年12月18日 下午4:51:02 * @version V1.0 */public class Client {public static void main(String[] args) {RealSubject realSubject = new RealSubject();InvocationHandler handler = new DynamicSubject(realSubject);// 下面的代码生成代理AbstractSubject subject = (AbstractSubject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),realSubject.getClass().getInterfaces(), handler);subject.doRentHouse();System.out.println(subject.getClass());}}
3.5 执行结果如下:


3.6 分析:

通过查看JDK的API可以知道,Java动态代理类位于java.lang.reflect包下,一般主要涉及到一个接口和一个类:
1)Interface InvocationHandler:该接口中只定义了一个方法,即

public object invoke(Object obj, Method method, Object[] args)
我们在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的doRentHouse(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。
2)Proxy该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容:
protected Proxy(InvocationHandler handler):构造函数,用于给内部的成员变量handler赋值。
static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler handler)返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法) 。
3.7 总结

所谓动态代理是这样一种class:它是在我们的程序运行时生成的class,在生成它时你必须提供一组interface给它,然后该class

宣称它实现了我们提供的这些interface。当然我们可以把该class的实例当作这些interface中的任何一个来用。

这个DynamicProxy其实就是一个Proxy,它不会替我们作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管

我们需要完成的实际的工作

四、代理模式在实际项目开发过程中,我们通常不会真正涉及去编写很多的代码,但是假如要编写框架,那么代理模式将是必不可少的模式。假如我们项目常用的spring框架,它里边aop(Aspect Oriented Programming)面向切面编程的实现就是使用了代理模式。所以,我们理解了Java的动态代理,那么将为我们理解框架的运行原理有很大帮助。






1 0