java 代理模式 3/23

2018-12-17  本文已影响11人  jellyb

代理模式

1. 代理模式介绍

代理模式也称为委托模式,它是一项基本技巧。许多其他的模式(状态模式、策略模式、访问者模式)本质实在更特殊的场合采用了代理模式。代理模式可以提供非常好的访问控制

1.1 代理模式结构

Subject抽象主题类

public interface Subject{
    public void request();
}

RealSubject具体主题角色,正常的业务实现类

public class RealSubject implement Subject{
    //实现方法
    public void request(){
        
    }
}

Proxy代理类,代理模式的核心

public class Proxy implements Subject{
    //要代理哪个实现类
    private Subject subject = null;
    //默认被代理者
    pubilc Proxy(){
        this.subject = new Proxy();
    }
    //使用构造方法传递代理者
    pulbic Proxy(Object... object){
        
    }
    //实现
    public void request(){
        this.before();
        this.subject.request();
        this.after();
    }
    //预处理
    private void before(){
        //do something;
    }
    //善后处理
    private void after(){
        //do something;
    }

一个代理类可以代理多个被委托者(被代理者),这是由具体场景来决定的。最简单的就是一个主题类一个代理类。通常情况下一个主题类一个代理类也足够了,具体代理哪个实现类由搞成模块来决定,也就是在构造方法中指派传递;我们上述代理类Proxy中可以添加如下构造函数:

public  Proxy(Subject subject){
    this.subject = subject;
}
1.2 静态代理&动态代理
1.2.1 静态代理

由程序员创建或者工具生成代理类的源码,在编译代理类。亦就是在程序运行前已经存在了代理累的字节码文件了,代理类和委托类的关系在运行前就确定了,如下:

  1. 抽象主题角色
/**
 * 描述:代理接口,处理给定名字的任务
 *
 * @author biguodong
 * Create time 2018-12-16 下午1:08
 **/
public interface Subject {

    /**
     * 执行给定任务的名字
     * @param taskName
     */
    void dealTask(String taskName);
}
  1. 具体处理业务的委托类
/**
 * 描述:真正执行任务的类,实现了代理接口
 * @author biguodong
 * Create time 2018-12-16 下午4:34
 **/
public class RealSubject implements Subject{

    /**
     * 执行给定任务的名字
     * @param taskName
     */
    @Override
    public void dealTask(String taskName) {
        System.out.println("正在执行任务:" + taskName);
        try{
            TimeUnit.SECONDS.sleep(5);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
  1. 代理类
/**
 * 描述:静态代理类的演示代码
 * @author biguodong
 * Create time 2018-12-16 下午4:38
 **/
public class ProxySubject implements Subject{

    /**
     * 代理类持有一个委托类的对象引用
     */
    private Subject delegate;

    public ProxySubject(Subject delegate) {
        this.delegate = delegate;
    }

    /**
     * 将请求分派给委托类执行,记录任务执行时间
     * @param taskName
     */
    @Override
    public void dealTask(String taskName) {
        long beginTime = System.currentTimeMillis();
        delegate.dealTask(taskName);
        long finishTime = System.currentTimeMillis();
        System.out.println("执行任务耗时:" + (finishTime - beginTime) + "毫秒");
    }
}
  1. 代理类工厂
/**
 * 描述:静态代理工厂
 * @author biguodong
 * Create time 2018-12-16 下午4:42
 **/
public class SubjectStaticFactory {
    /**
     * 客户端调用此方法获取代理对象
     * 客户端并不知道获取到的是代理类对象还是为拖类对象
     * @return
     */
    public static Subject getInstance(){
        return new ProxySubject(new RealSubject());
    }
}
  1. 调用客户端
/**
 * 描述:调用客户端
 * @author biguodong
 * Create time 2018-12-16 下午4:45
 **/
public class Client {
    public static void main(String[] args) {
        Subject subject = SubjectStaticFactory.getInstance();
        subject.dealTask("some task");
    }
}
  1. 执行结果
正在执行任务:some task
执行任务耗时:5008毫秒

静态代理优缺点

优点:

缺点:

1.2.2 动态代理

动态代理类的源码在JVM runtime时期根据反射等机制动态地生成,代理类和委托类的关系也是在程序运行时确定的;

1.java.lang.relect.Proxy:java生成所有动态代理类的父类,提供一组静态方法来为一组接口动态的生成代理类及其对象。

//获取指定代理对象所关联的调用处理器
public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException
//指定接口列表和类装载器的代理类
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
//是否为动态代理类
public static boolean isProxyClass(Class<?> cl)
//指定类加载器、一组接口以及调用处理器生成动态代理类实例
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

2. java.lang.reflect.InvocationHandler: 调用处理器接口,自定义了invoke方法,用来集中处理动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理类对象时都要指定一个对应的调用处理器对象。

/**
* proxy 代理类实例
* method 被调用的方法对象
* args 调用方法参数
* 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
*/
public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

3. java.lang.ClassLoader:类加载器,负责把类的字节码装在到JVM 并为其定义类对象,然后才能被使用,它与普通类的唯一区别就是其字节码是由JVM在运行时动态生成的,而不是预存在任何一个.class文件中

4. 实现动态代理的步骤

Proxy类的静态方法newInstance 对上述步骤作了封装,简化了动态代理对象的获取过程

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);
        final Class<?>[] intfs = interfaces.clone();
        /*
         * 缓存中查找或生成代理类
         */
        Class<?> cl = getProxyClass0(loader, intfs);
        /*
         * 使用特定的调用处理器invoke构造方法
         */
        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            //生成代理类实例
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

5. 创建自己的调用处理器实现动态代理

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

/**
 * 描述:自定义调用处理器
 * @author biguodong
 * Create time 2018-12-16 下午6:05
 **/
public class SubjectInvocationHandler implements InvocationHandler {
    private Object delegate;

    public SubjectInvocationHandler(Object delegate) {
        this.delegate = delegate;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long beginTime = System.currentTimeMillis();
        //利用反射将请求分派给为拖类对象
        method.invoke(delegate, args);
        long finishTime = System.currentTimeMillis();
        System.out.println("动态代理-执行任务耗时:" + (finishTime - beginTime) + "毫秒");
        return null;
    }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * 描述:动态代理对象的工厂
 *
 * @author biguodong
 * Create time 2018-12-16 下午6:08
 **/
public class DynProxyFactory {

    public static Subject getInstance(){
        Subject delegate = new RealSubject();
        InvocationHandler handler = new SubjectInvocationHandler(delegate);
        Subject proxy;
        proxy = (Subject) Proxy.newProxyInstance(delegate.getClass().getClassLoader(),
                delegate.getClass().getInterfaces(),
                handler);
        return proxy;
    }
}
/**
 * 描述:动态代理调用客户端
 * @author biguodong
 * Create time 2018-12-16 下午6:10
 **/
public class Client {

    public static void main(String[] args) {
        Subject proxy = DynProxyFactory.getInstance();
        proxy.dealTask("动态代理任务");
    }
}

结果

正在执行任务:动态代理任务
动态代理-执行任务耗时:5008毫秒

6.动态代理特点

6.1动态生成的代理类本身特点:

6.2动态代理类实例的特点:

6.3被代理的接口特点:

6.4异常处理方面的特点:

7. 动态代理的有点和缺点

优点:

缺点:

2. 代理模式总结

优点:

上一篇 下一篇

猜你喜欢

热点阅读