Java_代理模式

news/2024/7/24 10:47:44

介绍:

要求给某一个对象提供一个代理,并由代理对象控制原有对象的引用

角色:

Subject(抽象主题角色)

声明了真实主题和代理主题接口,这样一来,使用真实主题的地方都可以使用代理主题

Proxy(代理主题角色)

代理主题角色包含对真实主题的引用,从而可以在任何时候操作真实主题对象,代理角色通常

RealSubject(真实主题角色)

定义了代表角色所代表的对象,在真实主题主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的方法

结构图:

优点:

  1. 降低系统耦合度
  2. 远程代理使得客户端可以访问远程机器对象,远程机器可能具有更好的计算性能与处理速度,可以快速的相应并处理客户端需求
  3. 可以减少系统资源消耗,对系统进行优化并提高运行速度
  4. 保护代理可以控制对真实对象的使用权限

缺点:

  1. 因为在客户端和真实对象之间添加了代理对象,可能会造成处理速度更慢
  2. 增加额外工作,并且有些代理是非常复杂的

代理模式适用环境(按职责来划分):

远程代理:为一个不同的地址空间的对象提供一个本地代理对象,不同地址空间可以是在同一台主机上,也可以是另一台主机上

虚拟代理:如果要建立一个资源消耗较大的对象,先创建一个资源消耗较少的对象来表示,真实对象只有在需要时才会被创建

copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,copy-on-Write代理可以让这个操作延迟,只有在对象再用到的时候才会被克隆

保护代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限

缓冲代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果

防火墙代理:保护目标不让恶意用于接近

同步化代理:使几个用户能够同时使用一个对象而没用冲突

智能引用代理:当一个对象被引用时,提供一些额外的操作,比如将次对象调用的次数给记录下来

虚拟代理,远程代理和保护代理是最常见的代理模式

代理分类:

  1. 静态代理
  2. 动态代理(jdk动态代理,cglib动态代理)

静态代理案例:

西门庆想勾搭潘金莲,不直接找潘金莲,而是通过王婆找,

流程分析:

 

代码:

抽象角色类:

public interface Behavior {

    void sayHello();

    void haveIdea();
}

真实角色类:

public class PanJinLian implements Behavior{
    @Override
    public void sayHello() {
        System.out.println("潘金莲在打招呼。。。。");
    }

    @Override
    public void haveIdea() {
        System.out.println("潘金莲在勾搭。。。。。");
    }
}

代理角色类:

public class WangPo implements Behavior {
    private Behavior behavior;

    //这个地方直接定死
    public WangPo(){
        this.behavior = new PanJinLian();
    }

    public WangPo(Behavior behavior) {
        this.behavior = behavior;
    }

    @Override
    public void sayHello() {
        this.behavior.sayHello();
    }

    @Override
    public void haveIdea() {
        this.behavior.haveIdea();
    }
}

测试:(main方法测试)

public class XiMenQing {
    public static void main(String[] args) {
        WangPo wangPo = new WangPo();
        wangPo.sayHello();
        wangPo.haveIdea();
    }
}

结果:

好处,如果西门庆想勾搭林黛玉,直接添加一个林黛玉类

public class LinDaiYu implements Behavior{
    @Override
    public void sayHello() {
        System.out.println("林黛玉打招呼......");
    }

    @Override
    public void haveIdea() {
        System.out.println("林黛玉勾搭人......");
    }
}

 

public class XiMenQing {
    public static void main(String[] args) {
        //WangPo wangPo = new WangPo();
       // wangPo.sayHello();
        //wangPo.haveIdea();
        LinDaiYu linDaiYu = new LinDaiYu();
        WangPo wangPo1 = new WangPo(linDaiYu);
        wangPo1.sayHello();
        wangPo1.haveIdea();

    }
}

 

JDK动态代理和cglib动态代理区别:

(参考博文:Cglib和jdk动态代理的区别 - 橙发 - 博客园)

  • jdk动态代理:利用拦截器(必须实现InvocationHandler)加反射机制生成一个代理接口匿名类,在调用具体方法之前电泳InvocationHandler进行处理
  • cglib动态代理:利用ASM框架,对代理对象生成class文件加载进来,通过修改其字节码生成子类来处理
  • jdk动态代理只能对实现了接口的类生成代理,而不能针对类
  • cglib针对类实现了代理,主要对指定的类生成一个子类,覆盖其中方法,并覆盖其中方法的增强,因为采用的方法是继承,所以该类或方法最好不要出现final

什么时候用JDK动态代理什么时候用cglib动态代理:

  • 目标对象生成接口,使用jdk动态代理
  • 如果目标对象使用了接口,可以强制使用cglib代理
  • 如果目标对象没有生成接口,必须采用cglib库,Spring会自动太jdk代理和cglib代理之间转化

jdk和cglib哪个更快?

cglib地城是ASM字节码生成框架,在jdk1.6以前比反射更高,后面对jdk动态代理不断优化,在1.8以后,jdk动态代理效率高于cglib动态代理.

Spring如何选择使用JDK还是cglib?

  • 实现bean接口时,会使用jdk代理
  • 没有实现bean接口时,会使用cglib代理
  • 也可以强制使用cglib代理,(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class=”true”/>)

JDK动态代理:

主要涉及类:

InvocationHandler接口.它是代理实例的调用处理程序接口

接口方法:public Object invoke(Object proxy, Method method, Object[] args)

  1. 第一个参数proxy表示代理类
  2. 第二个参数method表示要代理的方法
  3. 第三个参数args表示代理方法参数数组

Proxy类:该类即为动态代理

方法:public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

newProxyInstance用于根据传入的接口类型interface返回一个动态创建的代理类实例

  1. 第一个参数loader表示代理类的类加载器
  2. 第二个参数interfaces表示代理类实现的接口列表(与真实主题类的接口列表表示一致)
  3. 第三个参数h表示所指派的调用处理程序类

JDK动态代理案例

介绍:

DK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的,但是JDK中所有要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中有一定的局限性,而且使用反射的效率也不高

抽象主题类:

public interface AbstractSubject {
    public void method();

}

真实主题类:

public class RealSubjectA implements AbstractSubject{
    @Override
    public void method() {
        System.out.println("真实主题A!");
    }
}
public class RealSubjectB implements AbstractSubject {
    @Override
    public void method() {
        System.out.println("真实主题B!");
    }
}

代理类:

public class LoggerProxy implements AbstractSubject{
    private BusinessClass businessClass = new BusinessClass();

    @Override
    public void method() {
        System.out.println("method方法被调用,当前时间为:"+ LocalDateTime.now());
        try {
            businessClass.method();
            System.out.println("方法method()调用成功!");
        } catch (Exception e) {
            System.out.println("方法method调用失败!");
        }

    }
}

测试:

 public static void main(String[] args) {
        InvocationHandler handler = new DynamicProxy(new RealSubjectA());
        AbstractSubject subject = (AbstractSubject) Proxy.newProxyInstance(AbstractSubject.class.getClassLoader(),new Class[]{AbstractSubject.class},handler);
        subject.method();
        System.out.println("=================================");
        InvocationHandler handler1 = new DynamicProxy(new RealSubjectB());
        AbstractSubject subject1 = (AbstractSubject) Proxy.newProxyInstance(AbstractSubject.class.getClassLoader(),new Class[]{AbstractSubject.class},handler1);
        subject1.method();
    }

结果:

cilib动态代理:

(参考博文:设计模式-代理模式(jdk代理和cglib代理详解)_几人憔悴几人泪-CSDN博客_cglib代理模式)

介绍:

JDK动态代理机制只能代理实现接口的类,一般没有实现接口的类不能进行代理。cglib就是针对类来实现代理的,它

的原理是对指定目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进

行代理。

使用cglib实现动态代理,完全不受代理类必须实现接口的限制,而且cglib底层采用ASM字节码生成框架,使用字节码

技术生成代理类.

主要方法:public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable ;

  1. 第一个参数proxy为cglib动态生成的代理实例
  2. 第二个参数method为上文中实体类所调用的被代理的方法调用
  3. 第三个参数args为method参数数值列表
  4. 第四个参数methodProxy为生成代理类对方法的代理引用
  5. 返回:从代理实例方法调用返回值

案例1:

需要装备的jar:


        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>
    
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm</artifactId>
            <version>5.0.4</version>
        </dependency>

抽象主题角色:

public interface AbstractSubject {
    void delete();

}

真实主题角色:

public class RealSubject implements AbstractSubject {
    @Override
    public void delete() {
        System.out.println("对数据进行删除");
    }
}

代理主题角色:

public class CglibProxy {
    /**
     * 被代理对象
     */
    private Object object;

    public CglibProxy(Object object) {
        super();
        this.object = object;
    }
    public Object getCglibProxy(){
        //Enhancer类是cglib中的一个字节码增强器,它可以方便的为你所要处理的类进行扩展
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(object.getClass());//将目标对象所在的类作为Enhancer类的父类
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("开始处理.....时间:"+System.currentTimeMillis());
                method.invoke(object,objects);
                System.out.println("结束......");
                return proxy;
            }
        });
        return enhancer.create();//生成对象并返回

    }
}

测试:

 public static void main(String[] args) {
        //真实对象
        RealSubject realSubject = new RealSubject();
        //代理对象
        RealSubject proxySubject= (RealSubject) new CglibProxy(realSubject).getCglibProxy();
        proxySubject.delete();
    }

结果:

代理主题角色(另一种写法):

public class CglibProxy2 implements MethodInterceptor {

    /**
     * 被代理对象
     */
   private Object object;

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

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("开始处理......时间:"+System.currentTimeMillis());
        Object result = methodProxy.invokeSuper(o,objects);
        System.out.println("结束处理......时间:"+System.currentTimeMillis());
        return result;
    }
}

测试:

public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        CglibProxy2 cglibProxy2 = new CglibProxy2(realSubject);
        //创建加强器,用来创建动态代理类
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(realSubject.getClass());
        //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
        enhancer.setCallback(cglibProxy2);
        RealSubject realSubject1= (RealSubject) enhancer.create();
        //调用
        realSubject1.delete();
    }

结果:


http://www.niftyadmin.cn/n/1728631.html

相关文章

vue中方法体内未严格按照执行顺序执行(自上而下,依次执行 ,有用)

注意&#xff1a; VUE中的方法调用顺序是依次进行的&#xff0c;方法体内部也是依次执行的&#xff0c; 但是&#xff0c;两个方法体的执行顺序并不能严格控制&#xff0c;不一定一个方法执行完再执行另一个 下面来说一下涉及到axios请求时的执行顺序我们要求的执行顺序是&…

python分数约分_C++、Java、Python中都是怎么精确表达分数的?

你要的东西&#xff0c;STL 库已经有了&#xff0c;叫 std::ratio &#xff0c;是 C11 标准的模板类。在 C11 标准之后&#xff0c;C委员会加入了大量高等数学相关的函数&#xff0c;beta函数、gamma函数、黎曼Zeta函数、球面贝塞尔函数等等&#xff0c;少不了优秀的先驱库 boo…

lambda表达式filter使用

filter经常使用到,作为一个积累,直接贴代码 实体类 public class User implements Serializable {private String name;private Integer age;private String address;private BigDecimal balance;private Date createTime;public String getName() {return name;}public void…

es6中的箭头函数和this关键字的说明(详细)

1.箭头函数 2.js的this指向1.箭头函数 >等价于return 它不能作为构造函数&#xff0c;不能new let xx () > 555; //xx为一个函数,调用时需带&#xff08;&#xff09; console.log(xx()); //555 //等同于 function xx2() {return 555; } console.log(xx2()); //555//…

兴趣点推荐代码_[深度模型] 阿里MIND网络:天猫首页是怎么给用户做多兴趣embedding的...

本人微信公众号为“推荐算法学习笔记”&#xff0c;定期推出经典推荐算法文章&#xff0c;欢迎关注。一. 概述我们知道推荐系统一般有两个重要阶段&#xff0c;召回和精排阶段。召回阶段负责从海量的商品中选出用户感兴趣的候选集&#xff0c;精排阶段再对这些候选集进行排序取…

Vue.config.productionTip = false

Vue.config.productionTip false&#xff0c;阻止启动生产消息&#xff1b; 没有Vue.config.productionTip false这句代码&#xff0c;它会显示你生产模式的消息&#xff1b; 开发环境下&#xff0c;Vue 会提供很多警告来帮你对付常见的错误与陷阱。 而在生产环境下&#x…

java_责任链接模式

介绍: 避免请求发送者和接受者耦合在一起,让多个对象都有可能接收请求将这些对象连接成一条链,并沿着这条链传递请求,直到有对象处理它为止. 主要角色: 抽象处理者(Handler) 抽象处理者定义了一个处理请求的接口,它一般设计为抽象类,由于不同的具体处理者处理请求的方式不同…

python单元测试框架对比_前端测试框架对比(js单元测试框架对比)

前端测试框架对比(js单元测试框架对比)本文主要目的在于横评业界主流的几款前端框架&#xff0c;顺带说下相关的一些内容。测试分类通常应用会有 单元测试(Unit tests) 和 功能测试(Functional tests)&#xff0c;复杂大型应用可能会有整合测试(Integration tests)。其中&#…