美文网首页
代理模式

代理模式

作者: lclandld | 来源:发表于2020-03-10 09:44 被阅读0次

一、代理模式的概念

就是为目标对象提供另外一种访问方式,通过访问代理对象来间接访问目标对象。

优点是:
  • 降低了系统的耦合度(将客户端与目标对象分离,在一定程度上降低了系统的耦合度)
  • 扩展目标对象的功能(在不修改原来方法的情况下,给现有的对象中的方法追加额外的功能)
  • 保护目标对象(在客户端与目标对象之间起到一个中介作用和保护目标对象的作用)
缺点是:
  • 请求处理速度变慢(在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢)
  • 增加了系统的复杂度

二、代理模式的分类

image.png
  • 作用:其实就是在我们目标方法之前和之后实现增强/限制/修改
  • 代理模式分类:静态代理模式(需要自己编写代理类)和动态代理模式(使用反射或者字节码技术生成代理类)
  • 动态代理分类:JDK动态代理(采用反射机制回调、必须依赖于接口)、CGLIB动态代理(采用字节码技术调用、采用重写(覆盖)的形式)

三、静态代理

需要定义三个类(接口、目标对象、代理对象),目标对象和代理对象同时实现接口,代理对象中维护一个目标对象的引用,并追加额外的功能

3.1、静态代理的UML图
image.png
3.2、静态代理的简单DEMO
  • 接口类
/**
 * @author lichunlan
 * @description 接口
 * @since 2020-03-09
 */
public interface Subject {
     void request();
 }

  • 目标对象
/**
 * @author lichunlan
 * @description 目标对象
 * @since 2020-03-09
 */
public class RealSubject implements Subject {

    public void request() {
        System.out.println("这个是真实目标对象要处理的事情.......");
    }
}
  • 代理类
/**
 * @author lichunlan
 * @description 代理对象
 * @since 2020-03-09
 */
public class Proxy implements Subject {
    public RealSubject realSubject;

    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    public void request() {
        preRequest();
        //目标对象的逻辑
        realSubject.request();
        postRequest();
    }

    public void preRequest(){
        System.out.println("目标方法之前追加的功能.......");
    }
    public void postRequest(){
        System.out.println("目标方法之后追加的功能.......");
    }
}
  • Client
public class Main {
    public static void main(String[] args) {
        Proxy proxy = new Proxy(new RealSubject());
        proxy.request();

    }
}
  • 结果


    image.png
优点:可以在不修改目标对象的情况下追加新的功能。
缺点:当接口中添加新的方法之后,目标对象和代理对象都要进行维护。

当代理方法越多,重复逻辑越多,前后执行的逻辑可能大部分都是一样的,于是就有了动态代理

四、动态代理

动态代理就是动态的创建代理类的技术,这样代理类就不需要手动实现接口了。
动态代理又分为JDK动态代理和CGLIB动态代理。

4.1、JDK动态代理
4.1.1 JDK动态代理主要点
  • 通过java.lang.reflect.Proxy类,动态生成代理类
  • 代理类实现的植入的逻辑,需要实现接口InvocationHandler
  • 代理类中维护一个目标对象的引用
  • 只能基于接口进行动态代理
4.1.2 JDK动态代理简单DEMO
  • 接口类
/**
 * @author lichunlan
 * @description 接口
 * @since 2020-03-09
 */
public interface Subject {
     void request();
 }

  • 目标对象
/**
 * @author lichunlan
 * @description 目标对象
 * @since 2020-03-09
 */
public class RealSubject implements Subject {

    public void request() {
        System.out.println("这个是真实目标对象要处理的事情.......");
    }
}
  • JDK代理类
/**
 * @author lichunlan
 * @description JDK动态代理
 * @since 2020-03-09
 */
public class JDKProxySubject implements InvocationHandler {
    private  RealSubject realSubject;

    public JDKProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        preRequest();
        Object result = null;
        //动态反射方法
        result = method.invoke(realSubject, args);
        postRequest();
        return result;
    }

    public void preRequest(){
        System.out.println("JDK动态代理目标方法之前追加的功能.......");
    }
    public void postRequest(){
        System.out.println("JDK动态代理目标方法之后追加的功能.......");
    }
}
  • Client
public class Main {
    public static void main(String[] args) {
        //目标对象
        RealSubject realSubject = new RealSubject();
        JDKProxySubject jdkProxySubject = new JDKProxySubject(realSubject);
        //给目标对象创建代理对象
        Subject subject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]{Subject.class}, jdkProxySubject);
        subject.request();

    }
}
  • 结果


    image.png
  • 接口类有多个目标方法就很容易了,假设我在接口类中,再添加8个目标方法,这个时候,只需要在RealSubject中实现方法,在Client中直接调用即可

/**
 * @author lichunlan
 * @description 接口
 * @since 2020-03-09
 */
public interface Subject {
    void request();
    void request1();
    void request2();
    void request3();
    void request4();
    void request5();
    void request6();
    void request7();
    void request8();
 }

/**
 * @author lichunlan
 * @description 目标对象
 * @since 2020-03-09
 */
public class RealSubject implements Subject {

    public void request() {
        System.out.println("这个是真实目标对象要处理的事情.......");
    }

    public void request1() {
        System.out.println("这个是真实目标对象1要处理的事情.......");
    }

    public void request2() {
        System.out.println("这个是真实目标对象2要处理的事情.......");
    }

    public void request3() {
        System.out.println("这个是真实目标对象3要处理的事情.......");
    }

    public void request4() {
        System.out.println("这个是真实目标对象4要处理的事情.......");
    }

    public void request5() {
        System.out.println("这个是真实目标对象5要处理的事情.......");
    }

    public void request6() {
        System.out.println("这个是真实目标对象6要处理的事情.......");
    }

    public void request7() {
        System.out.println("这个是真实目标对象7要处理的事情.......");
    }

    public void request8() {
        System.out.println("这个是真实目标对象8要处理的事情.......");
    }
}

/**
 * @author lichunlan
 * @description
 * @since 2020-02-26
 */
public class Main {
    public static void main(String[] args) {
        //目标对象
        RealSubject realSubject = new RealSubject();
        //JDK代理对象
        JDKProxySubject jdkProxySubject = new JDKProxySubject(realSubject);
        //给目标对象创建代理对象
        Subject subject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]{Subject.class}, jdkProxySubject);
        subject.request();
        subject.request1();
        subject.request2();
        subject.request3();
        subject.request4();
        subject.request5();
        subject.request6();
        subject.request7();
        subject.request8();
    }
}
4.2、CGLIB动态代理
4.2.1 CGLIB动态代理主要点
  • 需要实现接口MethodInterceptor
  • 目标对象是没有接口的
  • 采用字节码(其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑)
4.2.2 CGLIB动态代理简单DEMO

这种代理方式是为了解决目标对象并没有实现任何接口的时候,使用这种方式生成一个子类对象从而实现对目标对象功能的扩展

  • 目标对象(在CGLIB中可以称为父类)
/**
 * @author lichunlan
 * @description 目标对象
 * @since 2020-03-09
 */
public class RealSubject{

    public void request() {
        System.out.println("这个是真实目标对象要处理的事情.......");
    }
}
  • CGLIB动态代理

/**
 * @author lichunlan
 * @description CGLIB动态代理(自定义MethodInterceptor)
 * @since 2020-03-09
 */
public class CglibProxySubject implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();

    /**
     * 通过字节码技术生成CGLIB动态代理的代理对象
     * @param clazz  目标对象
     * @return
     */
    public Object getProxy(Class clazz){
        //继承被代理类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        //通过字节码技术动态生成代理对象
        return enhancer.create();
    }

    /**
     *
     * @param o  cglib生成的代理对象
     * @param method  被代理对象方法
     * @param objects 方法入参
     * @param methodProxy 代理方法
     * @return
     * @throws Throwable
     */
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        preRequest();
        Object object = null;
        object = methodProxy.invokeSuper(o, objects);
        postRequest();
        return object;
    }

    public void preRequest(){
        System.out.println("CGLIB动态代理目标方法之前追加的功能.......");
    }
    public void postRequest(){
        System.out.println("CGLIB动态代理目标方法之后追加的功能.......");
    }
}

  • Client
/**
 * @author lichunlan
 * @description
 * @since 2020-02-26
 */
public class Main {
    public static void main(String[] args) {
        CglibProxySubject proxy = new CglibProxySubject();
        //通过生成子类的方式创建代理类
        RealSubject realSubject = (RealSubject)proxy.getProxy(RealSubject.class);
        realSubject .request();
    }
}
  • 结果


    image.png

五、CGLIB动态代理和JDK动态代理的比较

  • JDK采用反射机制回调 ,CGLIB采用字节码技术调用
  • JDK反射和CGLIB字节码生成的区别?
    1)JDK必须依赖于接口,不能针对类
    2)CGLIB是针对类,主要是为目标类生成一个子类,覆盖其中的方法实现增强,目标类不能声明成final,因为final类不能被继承,无法生成代理。
    目标方法也不能声明成final,final方法不能被重写,无法得到处理。
  • 在Spring中到底是采用JDK还是采用CGLIB代理呢???
    1)目标类(被代理类)实现过接口默认情况下用JDK动态代理
    2)目标类(被代理类)实现了接口,可以强制使用CGLIB动态代理
    3)目标类(被代理类)没有实现接口就必须采用CGLIB动态代理
  • JDK和CGLIB哪个应用得广泛些
    相比来说,JDK比CGLIB广泛,但是CGLIB效率比JDK高
  • 为什么CGLIB效率比JDK效率高,还用JDK呢???
    1)JDK支持原生
    2)项目中大多数都采用面向接口编程
    所以JDK使用得比CGLIB多

六 、应用场景

  • Aop
  • mybats mapper对象
  • RPC远程调用
  • 事务
  • 日志
  • 权限控制

既然动态代理模式是采用的反射或者字节码动态生成代理类,那下篇文章我将把反射的原理和字节码的原理学习一遍,并进行记录。因为反射在源码中是一个非常重要的概率

参考

慕课网课程"探秘Spring AOP"

相关文章

  • 设计模式

    单例模式 模板方法模式 工厂模式 代理模式 静态代理 JDK动态代理

  • 设计模式

    单例模式 代理模式 静态代理 jdk动态代理 cglib动态代理 工厂模式 适配器模式 建造者模式 观察者模式

  • kube-proxy的3种模式

    userspace代理模式 iptables代理模式 IPVS代理模式 https://kubernetes.io...

  • 第4章 结构型模式-代理模式

    一、代理模式简介 二、代理模式3个角色 三、代理模式的优点 四、代理模式的实例(游戏代练)

  • 理解代理模式

    原创博客地址 简介 代理模式,也叫做委托模式,分为:静态代理动态代理 代理模式也是平时比较常用的设计模式之一,代理...

  • 结构型 代理模式(文末有项目连接)

    1:什么是代理模式 2:没用代理模式时的实例 3:使用代理模式将其解耦(静态代理) 3:使用代理模式将其解耦(动态...

  • 设计模式-动态代理模式

    之前介绍了代理模式,大家也都了解了代理模式,不过之前介绍的代理模式是静态代理,静态代理什么意思?静态代理指的是代理...

  • 代理模式

    一、什么是代理模式 代理模式(Proxy pattern):代理模式又叫委托模式,是为某个对象提供一个代理对象,并...

  • 设计模式之代理模式(Proxy模式)

    代理模式的引入 代理模式的实例程序 代理模式的分析 代理模式的引入 Proxy是代理人的意思,指的是代替别人进行工...

  • Java设计模式之代理模式

    Java设计模式之代理模式 代理模式 静态代理 动态代理 为什么需要代理 通过代理,我们能够不用知道委托人是谁,而...

网友评论

      本文标题:代理模式

      本文链接:https://www.haomeiwen.com/subject/guzqlhtx.html