Java设计模式——代理模式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java设计模式——代理模式相关的知识,希望对你有一定的参考价值。

有高手云:了解设计模式才算是入门级的程序员

所以为了入门我打算把我学习到的设计模式逐条总结下来。和别人的文章不同,我几乎只提供了测试源码与细节分类。原因是,我相信对于设计来说,你永远无法给出终极答案。不同的人看到会有不同的理解,所以大家一起讨论吧。

一、静态代理

设计测试接口,提供request()方法

技术分享
package proxy.staticproxy;

public interface Service {
    void request();
}
Service 接口

创建两个实现类

技术分享
package proxy.staticproxy;

public class UserServiceImpl implements Service {

    @Override
    public void request() {
        System.out.println("UserServiceImpl...");
    }

}
UserServiceImpl
技术分享
package proxy.staticproxy;

public class AdminServiceImpl implements Service {

    @Override
    public void request() {
        System.out.println("AdminServiceImpl...");
    }

}
AdminServiceImpl

创建可以实现代理的抽象类,此抽象类同样实现了Service接口。创建抽象类的目的是为了设计一种可以为指定接口提供代理的模板,用户只要继承此抽象类即可。

技术分享
package proxy.staticproxy;

public abstract class ProxyService implements Service {
    private Service serviceImpl;

    public ProxyService(Service service) {
        this.serviceImpl = service;
    }

    @Override
    public void request() {
        preRequest();
        serviceImpl.request();
        postRequest();
    }

    abstract void preRequest();

    abstract void postRequest();

}
ProxyService

创建两个代理类

技术分享
package proxy.staticproxy;

public class CheckProxyServiceImpl extends ProxyService {

    public CheckProxyServiceImpl(Service service) {
        super(service);
    }

    @Override
    void preRequest() {
        System.out.println("Check preRequest...");
    }

    @Override
    void postRequest() {
        System.out.println("Check postRequest...");
    }

    public static void main(String[] args) {

    }
}
CheckProxyServiceImpl
技术分享
package proxy.staticproxy;

public class ReferProxyServiceImpl extends ProxyService {

    public ReferProxyServiceImpl(Service service) {
        super(service);
    }

    @Override
    void preRequest() {
        System.out.println("Reference preRequest...");
    }

    @Override
    void postRequest() {
        System.out.println("Reference postRequest...");
    }

    public static void main(String[] args) {
        Service user = new UserServiceImpl();
        Service userReferProxy = new ReferProxyServiceImpl(user);
        Service userCheckProxy = new CheckProxyServiceImpl(userReferProxy);
        userCheckProxy.request();
    }
}
ReferProxyServiceImpl

输出结果:

Check preRequest...
Reference preRequest...
UserServiceImpl...
Reference postRequest...
Check postRequest...

二、动态代理

动态代理可以通过JDK或者CGLIB来实现,它是AOP思想的主要具体表现形式之一。

设计测试接口

技术分享
package proxy.dynamicproxy;

public interface Hypnosis {
    void hypnosis();
    void hypnosis(String s);
}
Hypnosis

变现实现类

技术分享
package proxy.dynamicproxy;

import java.util.Random;

public class HypnosisImpl implements Hypnosis {

    @Override
    public void hypnosis() {
        try {
            Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void hypnosis(String s) {
        try {
            System.out.println(s);
            Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
HypnosisImpl

动态代理最令人费解的地方在于如何使用它的InvocationHandler。因为采用了动态编译和反射等技术,对于方法的实现往往不太直观。

技术分享
package proxy.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class InvocationHandlerDemo implements InvocationHandler {
    private Object target;

    public InvocationHandlerDemo(Object o) {
        target = o;
    }
    
    /*
     * 实现方法的代理逻辑
     * 第1个参数表示目标对象的代理对象,通常不调用
     * 第2个参数代表接口中的方法,用户通过反射调用,proxy提供实参
     * 第3个参数代表传递给方法的实参,用户在定义proxy传递,proxy调用handler时传递
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long startTimer = System.currentTimeMillis();
        Object o = method.invoke(target, args);
        long endTimer = System.currentTimeMillis();
        System.out.println("Run Time:" + (endTimer - startTimer));
        return o;
    }

}
InvocationHandlerDemo

其实对方法Proxy.newProxyInstance()的调用也不太直观,为了方便使用通常我会对它进行包装。

技术分享
package proxy.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class ProxyDemo {
    private InvocationHandler h;

    // 也可以通过setter方法注入handler
    public ProxyDemo(InvocationHandler h) {
        this.h = h;
    }

    public Object getProxy(Class<?> clazz, Class<?>... interfaceClasses) throws Exception {
        if (interfaceClasses.length > 0) {
            /*
             * 指定InvocationHandler实现代理
             * 第1个参数定义代理类的类加载器
             * 第2个参数为接口数组
             * 第3个参数定义方法处理类
             */
            return Proxy.newProxyInstance(clazz.getClassLoader(), interfaceClasses, h);
        } else {
            throw new Exception("Have to set Interface...");
        }
    }
    
    // 重新包装了代理方法
    public Object getProxy(Object o) throws Exception {
        return Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), h);
    }

    public static void main(String[] args) {
        Hypnosis hypnosis = new HypnosisImpl();
        InvocationHandler h = new InvocationHandlerDemo(hypnosis);
        ProxyDemo demo = new ProxyDemo(h);
        try {
            Hypnosis hy = (Hypnosis) demo.getProxy(hypnosis);
            hy.hypnosis();
            hy.hypnosis("i was be hypnosis");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
ProxyDemo

如果需要代理的原型对象没有实现任何接口,就必须使用CGLIB。Spring提供了综合解决方案。

三、虚拟代理

虚拟代理是针对后台响应需要时间,而前台又不希望让用户无聊等待而设计的。通过多线程实现,前台加载能快速响应的动作,加载缓慢的动作丢给另外一个线程处理。

设计测试接口

技术分享
package proxy.virtualproxy;

public interface Service {
    void response();
}
Service

编写实现类

技术分享
package proxy.virtualproxy;


public class ServiceImpl implements Service {
    @Override
    public void response() {
        System.out.println("服务器正在准备应答...");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("服务器应答完成!");
    }

}
ServiceImpl

还是通过抽象类来封装代理的模板,与静态代理不同的是需要把原型方法和代理方法独自封装在不同的内部类中。可见,如果原型类的方法比较多,代码就会显得比较臃肿。

技术分享
package proxy.virtualproxy;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public abstract class ProxyService implements Service {
    private Service s;

    public ProxyService(Service s) {
        this.s = s;
    }

    @Override
    public void response() {
        ExecutorService exec = Executors.newCachedThreadPool();
        for (Runnable r : new Runnable[] { new Proxy(), new Prototype() }) {
            exec.execute(r);
        }
        exec.shutdown();
    }

    abstract void preResponse();

    class Proxy implements Runnable {
        @Override
        public void run() {
            preResponse();
        }
    }

    class Prototype implements Runnable {
        @Override
        public void run() {
            s.response();
        }
    }
}
ProxyService

编写代理类

技术分享
package proxy.virtualproxy;

public class ProxyServiceImpl extends ProxyService {

    public ProxyServiceImpl(Service s) {
        super(s);
    }

    @Override
    void preResponse() {
        for (int i = 1; i <= 5; i++) {
            try {
                Thread.sleep(1900);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("代理进度: " + 20*i + "%");
        }
    }

    public static void main(String[] args) {
        Service prototype = new ServiceImpl();
        Service proxy = new ProxyServiceImpl(prototype);
        proxy.response();
    }
}
ProxyServiceImpl

输出结果:

服务器正在准备应答...
代理进度: 20%
代理进度: 40%
代理进度: 60%
代理进度: 80%
代理进度: 100%
服务器应答完成!

 

今天是2016年2月3日,明天一早5点的车出发去海南过春节了。祝我的两个朋友:应卓、李浩节日快乐,阖家幸福。

以上是关于Java设计模式——代理模式的主要内容,如果未能解决你的问题,请参考以下文章

Java设计模式-代理模式之动态代理(附源代码分析)

java代理模式

JAVA设计模式 -- 代理模式

Java 代理模式讲解

Java设计模式——代理模式

Spring之代理模式