设计模式之代理模式

92 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情

简介

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

应用场景

代理模式主要用于解决直接访问对象时带来的问题。通过这个意图我们就好判断其应用场景。

按职责来划分,通常有以下使用场景:

  1. 远程代理。
  2. 虚拟代理。
  3. Copy-on-Write 代理。
  4. 保护(Protect or Access)代理。
  5. Cache代理。
  6. 防火墙(Firewall)代理。
  7. 同步化(Synchronization)代理。
  8. 智能引用(Smart Reference)代理。

分类

一. 静态代理

接口:

/**
 * @author miracle
 */
public interface IWorker {

    /**
     * 上班
     */
    void working();
}

实体类:

/**
 * 真正的打工人
 *
 * @author miracle
 */
public class RealWorker implements IWorker {

    @Override
    public void working() {
        System.out.println("摸鱼");
    }
}

代理类-方式1:

/**
 * 代理
 *
 * @author miracle
 */
public class WorkerProxy implements IWorker {
    private IWorker IWorker;


    public WorkerProxy(IWorker IWorker) {
        this.IWorker = IWorker;
    }

    @Override
    public void working() {
        // 代理类可以进行增强和控制
        before();
        IWorker.working();
    }


    public void before(){
        System.out.println("打工是不可能的,这辈子都不可能打工");
    }
}

使用测试:

public static void main(String[] args) {
    Proxy proxy = new Proxy(new RealWorker());

    proxy.working();
}

代理类-方式2:

public class WorkerProxy implements IWorker {
    private RealWorker realWorker;


    public WorkerProxy() {
    }

    @Override
    public void working() {
        if(null == realWorker){
            realWorker = new RealWorker();
        }

        // 代理类可以进行增强和控制
        before();
        realWorker.working();
    }


    public void before(){
        System.out.println("打工是不可能的,这辈子都不可能打工");
    }
}

测试用例:

public static void main(String[] args) {
    WorkerProxy proxy = new WorkerProxy();

    proxy.working();
}

它们之间的关系: image.png

静态代理的特点就是一个服务类对应一个代理类,如果服务类比较多的时候,我们就必须创建很多代理类,维护成本和开发成本太大了,因此才有了动态代理。

二. 动态代理

动态代理允许使用一种方法的单个类(代理类)为具有任意数量方法的任意类(真实类)的多个方法调用提供服务

1. JDK代理(接口代理)

JDK代理涉及到java.lang.reflect包中的InvocationHandler接口和Proxy类

代理类:

public class JDKProxy implements InvocationHandler {
    /**
     * 需要代理的服务对象
     */
    private Object object;

    public JDKProxy(Object object){
        this.object = object;
    }

    /**
     * 获取服务对象的代理
     * @return
     */
    public Object getProxy(){
        Class<?> clazz = object.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(object, args);

        // 增加控制

        return result;
    }
}

这里接着使用上面静态代理的接口和实现类 测试用例:

public static void main(String[] args) {
    JDKProxy proxy = new JDKProxy(new RealWorker());

    RealWorker realWorker = (RealWorker) proxy.getProxy();

    realWorker.working();
}

其核心就是利用反射。

其代理实现的是JDK自带InvocationHandler接口,实现了这个接口的类也叫拦截器类,也叫代理类。

JDK动态代理的前提条件是,要有接口存在,那还有许多场景是没有接口的,这个时候就需要cglib动态代理.

2. Cglib代理

实体类(无实现接口):

public class Family {

    public void reunite(){
        System.out.println("家人团聚");
    }
}

代理类:

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author miracle
 */
public class CglibProxy implements MethodInterceptor {

    /**
     * 需要代理的服务对象
     */
    private Object obj;

    public CglibProxy(Object obj) {
        this.obj = obj;
    }

    /**
     * 获取服务对象的代理
     *
     * @return
     */
    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        // 设置拦截器,回调对象设置为自己
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 调用前控制/扩展行为
        before();
        // 调用服务对象的某个方法
        Object result = methodProxy.invoke(obj, objects);
        // 调用后控制/扩展行为

        return result;
    }


    public void before(){
        System.out.println("明天是中秋了");
    }

}

测试用例:

public static void main(String[] args) {
    CglibProxy proxy = new CglibProxy(new Family());
    Family family = (Family) proxy.getProxy();
    
    family.reunite();

}

可以看出Cglib动态代理和JDK动态代理两者相似,Cglib动态代理类是通过实现MethodInterceptor,重写intercept方法,通过生成被代理类的子类来达到代理增强代码的目的;而Jdk代理是通过实现InvocationHandler,重写invoke方法,通过生成接口的代理类来达到代码增强的目的,所以jdk动态代理的实现需要接口,cglib则不需要。

总结

  1. 静态代理 前提要有接口及实现类,且一个实现类对应一个代理,太繁琐因此产生了动态代理。
  2. JDK代理 前提也是要有接口及实现类型,利用反射来代理,实现InvocationHandler 并重写invoke方法调用服务对象(被代理类)的方法并增加一些控制。
  3. Cglib代理 无需接口,通过实现MethodInterceptor,重写intercept方法来拦截及增加服务对象(被代理类)的方法。

代理模式优缺点

优点:

  1. 职责清晰。
  2. 高扩展性。

缺点:

  1. 因使用了代理,存在中介,造成处理速度变慢。
  2. 实现较为复杂。