2011年12月30日金曜日

[设计模式] 代理模式 (Proxy)

1. 场景
给某一对象提供代理对象, 并由代理对象控制此具体对象的访问
在某些情况下, 一个客户不想或者不能直接引用另一个对象, 而代理对象可以在客户端和目标对象之间起到中介的作用
例如授权机制, 通过代理能拦截真实主题的能力, 来控制真实主题的访问权限

2. 角色
2-1. 抽象角色: 声明真实对象和代理对象的共同接口
2-2. 代理角色: 代理对象角色内部含有对真实对象的引用, 从而可以操作真实对象, 同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象. 同时, 代理对象可以在执行真实对象操作时, 附加其他的操作, 相当于对真实对象进行封装
2-3. 真实角色: 代理角色所代表的真实对象, 是最终要引用的对象

3. 种类
3-1. 静态代理
3-2. 动态代理

3-3. 实例
3-3-1. 静态代理
// 抽象角色 (抽象类或接口)
public abstract class Subject {
    abstract public void request();
}

// 真实角色
public class RealSubject extends Subject {
    public void request() {
        System.out.println("真实角色的请求方法 RealSubject.request() 被调用!");
    }
}

// 代理角色
public class ProxySubject extends Subject {
    private RealSubject realSubject; // 以真实角色作为代理角色的属性

    // 该方法封装了真实对象的 request 方法
    public void request() {
        preRequest(); //代理前执行的操作
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        realSubject.request(); // 此处执行真实对象的request方法
        postRequest(); //代理后执行的操作
    }

    private void preRequest() {
        System.out.println("代理前执行的操作 ProxySubject.preRequest()!");
    }

    private void postRequest() {
        System.out.println("代理后执行的操作 ProxySubject.postRequest()!");
    }
}

// 客户端
public class Test {
    public static void main(String[] args) {
        Subject sub = new ProxySubject();
        sub.request();
    }
}

3-3-2. 动态代理

// 抽象角色
public interface Subject {
    public void request();
}

// 真实角色
public class RealSubject implements Subject {
    public void request() {
        System.out.println("具体角色的请求方法RealSubject.request()被调用!");
    }
}

/**
 * 代理处理器
 *
 * 该代理类的内部属性为 Object 类, 实际使用时通过该类的构造函数 DynamicSubject(Object obj) 对其赋值
 * 在该类还实现了 invoke 方法, 该方法中的 method.invoke(sub,args), 其实就是调用被代理对象的将要被执行的方法
 * 方法参数 sub 是实际的被代理对象, args 为执行被代理对象相应操作所需的参数
 */

public class DynamicSubject implements InvocationHandler {
    private Object sub; //被代理对象

    /**
     * 构造方法, 并初始化被代理对象
     * @param obj 是被代理的对象
     */
    public DynamicSubject(Object obj) {
        sub = obj;
    }

    /**
     * @param proxy  指代理类
     * @param method 被代理的方法
     * @param args   被代理的方法所需要的参数数组
     * @return  在代理实例上处理方法调用并返回结果。
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理前执行的操作!");
        method.invoke(sub, args);
        System.out.println("代理后执行的操作!");
        return null;
    }
}

// 客户端
public class Test {
    static public void main(String[] args) throws Throwable {
        RealSubject rs = new RealSubject(); // 在这里指定被代理类

        //通过被代理类对象声明一个代理类对象
        InvocationHandler dynamicSubject = new DynamicSubject(rs);
        Class<?> cls = rs.getClass();

        // 生成代理类实例
        // 返回一个指定接口的代理类实例
        // 该接口可以将方法调用指派到指定的调用处理程序
        Subject subject = (Subject) Proxy.newProxyInstance(
                cls.getClassLoader(), //定义代理类的类加载器
                cls.getInterfaces(), //代理类要实现的接口列表
                dynamicSubject); //指派方法调用的调用处理程序: InvocationHandler 对象

        subject.request(); //调用代理处理器上的方法
    }
}

3-3-3. 动态代理的实现 AOP

// 切面接口, 通过实现这个接口可以对指定函数在调用前后进行处理
public interface AopInterface {
    public void before(Object obj); //调用的处理
    public void end(Object obj); //调用后的处理
}

// 切面接口实现类
public class AopInterfaceImp implements AopInterface{ 
    public void before(Object obj) {
        System.out.println("调用前拦截");
    }

    public void end(Object obj) {
        System.out.println("调用调用后处理");
    }
}

// 代理类
public class PeoxyObject implements InvocationHandler {
    private AopInterface aop; //定义了切入时调用的方法
    private Object proxy_obj;
    private String methodName; //指定要切入的方法名

    public Object factory(Object obj){
        proxy_obj = obj;
        Class cls = obj.getClass();
        return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(this.aop == null)throw new NullPointerException("aop is null");
        if(method == null)throw new NullPointerException("method is null");

        Object o;

        // 如果指定了要拦截方法名, 并且调用的方法和指定的方法名相同, 则进行拦截处理
        // 否则当正常方法处理
        if(methodName != null && method.toString().indexOf(methodName) != -1){
            aop.before(proxy_obj); //指定方法调用前的处理
            o = method.invoke(proxy_obj, args); // 调用真实方法
            aop.end(proxy_obj);//指定方法调用后的处理
        } else {
            //没有指定的方法, 以正常方法调用
            o = method.invoke(proxy_obj, args);
        }

        return o;
    }

    public AopInterface getAop() {
        return aop;
    }

    public void setAop(AopInterface aop) {
        this.aop = aop;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }
}

// 抽象角色
public interface SubInterface {
    public void add(String value1,String value2);
    public void acc(String value1);
}

// 真实角色
public class ImpObject implements SubInterface{
    public void add(String value1,String value2) {
        System.out.println("ImpObject add(String value1,String value2)");
    }

    public void acc(String value1){
        System.out.println("ImpObject acc(String value1)");
    }
}

// 测试类
public static void main(String agr[]){
    PeoxyObject po = new PeoxyObject();
    po.setAop(new AopInterfaceImp()); // 拦截处理对象
    po.setMethodName("acc");// 指定要拦截的函数
    SubInterface si = (SubInterface)po.factory(new ImpObject());

    // 因为 add 方法不是我们指定的拦截函数, AopInterfaceImp 是不会被执行
    si.add("tt","dd");

    // acc 是我们指定的拦截方法, 所以调用 acc 的前后会先执行 AopInterfaceImp
    si.acc("tt");
}

0 件のコメント:

コメントを投稿