Java设计模式学习记录-代理模式

代理模式

代理模式是常见设计模式的一种,代理模式的定义是:为其他对象提供一种代理以控制对这个对象的访问

在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

静态代理

理解设计模式是比较枯燥的,所以还是以举例子的方式来进行理解,

例如:公司开年会想找个明星来表演,那么并不会直接联系明星(主要还是联系不上),而是会联系明星的经纪人,明星就是被代理的对象,而经纪人就是代理对象。明星只需要准备来参加年会时应该表演什么节目就可以,其他的出场费之类的事情就交给经纪人来处理就好了。代理对象可以理解为被代理对象的扩展,能做被代理对象不能做的事情,也可以调用代理对象做事情。

那么用代码实现这个场景是什么样子的呢?

执行合作方法的接口

/**
 * @Description: 经纪公司接口,代理对象和被代理对象都需要实现的接口
 */
public interface Company {
     合作 */
    void cooperation();
}

被代理对象


 * @Description: 目标对象-明星(被代理对象)
 class Start implements Company {

    @Override
     cooperation() {
        System.out.println("is show time");
    }
}

代理对象


 * @Description: 经纪人(代理对象)
 class Agent  Company {

    private Company company;

    public Agent(Company company)
    {
        this.company = company;
    }

    @Override
     cooperation()
    {
        System.out.println("收出场费,化妆等等");
        company.cooperation();
        System.out.println("收拾行李,打道回府");
    }
}

测试类

import org.junit.Test;


 * @Description: 测试类
 class ProxyTest {

    @Test
     AnnualMeeting()
    {
        //目标对象
        Start start = new Start();
        构建代理对象,生成代理关系
        Agent agent =  Agent(start);
        用代理对象执行被代理对象的动作
        agent.cooperation();
        
    }

}

输出结果:

收出场费,化妆等等
is show time
收拾行李,打道回府

静态代理的特点是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。有时候不方便修改别人的代码或者是引入的一个功能,需要进行功能扩展一下才能适用于自己的业务实现,可以使用代理模式来进行设计。

但是静态代理的实现基础是一个目标对象对应一个代理对象,并且在编译时就已经维护好了代理关系,如果目标对象是多个那么就会需要多个代理对象,这样在更新目标的对象的时候还需要更新代理对象,当代理对象持续增加时维护成本就变得非常困难。

针对于这种情况,动态代理应运而生。

动态代理

JDK代理

动态代理的代理对象不需要和目标对象共同实现接口,而是利用JDK的API,动态的在内存中构建代理对象。

动态生成代理对象需要调用JDK中的java.lang.reflect.Proxy类的newProxyInstance方法,这个方法需要三个参数:

@CallerSensitive
static Object newProxyInstance(ClassLoader loader,]Class<?>[] interfaces,InvocationHandler h)
ClassLoader loader:类加载器,用来加载目标对象类,因为是在运行时获得目标对象,所以肯定需要用到反射。
Class<?>[] interfaces:目标对象类实现的接口集合,这些接口中定义目标对象可以执行的方法。
InvocationHandler h:这个参数代表的是动态代理对象在调用方法的时候,会将方法转发到哪一个invocationHandler对象身上,InvocationHandler是个接口,
需要自己实现它,然后定义自己的动态代理执行方法。
创建包含动态代理对象具体执行方法的实现类。
 java.lang.reflect.InvocationHandler;
 java.lang.reflect.Method;


 * @Description: 包含动态代理对象具体执行方法的实现类
 class MyInvocationHandler  InvocationHandler {

     MyInvocationHandler(Company company)
    {
        public Object invoke(Object proxy,Method method,Object[] args) throws Throwable
    {

        System.out.println("收出厂费,化妆等");
        具体执行方法
        Object result = method.invoke(company,args);

        System.out.println("收拾现场,卸妆,打道回府");

        return result;
    }
}

测试类

 org.junit.Test;
 java.lang.reflect.Proxy;

 DynamicProxyTest {

    @Test
    创建目标对象
        Company start = 创建代理对象需要执行的方法处理对象
        MyInvocationHandler myInvocationHandler =  MyInvocationHandler(start);
        获得目标对象的类加载器
        ClassLoader classLoader = start.getClass().getClassLoader();
        创建动态代理对象
        Company proxy = (Company) Proxy.newProxyInstance(classLoader,start.getClass().getInterfaces(),myInvocationHandler);
        用动态代理对象执行目标对象的方法
        proxy.cooperation();
    }
}

输出结果:

收出厂费,化妆等
is show time
收拾现场,卸妆,打道回府

JDK动态代理的特点:代理对象不需要实现接口,但是目标对象必须实现接口。

那么如果在实际的业务中目标对象确实没有实现接口,怎么办呢?

遇到这种情况的时候就需要时cglib动态代理了。

Cglib代理

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现。
  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)。
  • Cglib包的底层是通过使用一个小块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

在实现cglib代理时需要引入cglib的jar包,但是spring核心功能已经包含了cglib的功能,所以引入spring-core的jar包就可以了。

需要注意的是:代理的类不能为final,否则报错,目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法。

没有实现接口的目标对象类


 * @Description: 没有经纪公司的明星,就行像最近以个人练习生出道的蔡徐坤
  AloneStart {
    );
    }

}

生成Cglib代理对象的类

 org.mockito.cglib.proxy.Enhancer;
 org.mockito.cglib.proxy.MethodInterceptor;
 org.mockito.cglib.proxy.MethodProxy;

 * @Description: 生成代理对象的类
 class CglibProxy  MethodInterceptor {


     AloneStart aloneStart;

     CglibProxy(AloneStart aloneStart)
    {
        this.aloneStart = aloneStart;
    }

    
     * 创建代理对象
     * @return
      Object getProxyInstance()
    {
        动态代理工具类
        Enhancer enhancer =  Enhancer();
        设置父类
        enhancer.setSuperclass(aloneStart.getClass());
        设置回调函数调用对象
        enhancer.setCallback(this返回代理对象
         enhancer.create();

    }

    @Override
    public Object intercept(Object obj,Object[] objects,MethodProxy methodProxy)  Throwable
    {
        System.out.println("收出厂费,化妆等"执行代理方法
        methodProxy.invokeSuper(obj,objects);
        System.out.println("卸妆,回家"return null;
    }
}

测试类

 CglibProxyTest {

    @Test
     cglibTest()
    {
        创建目标对象
        AloneStart aloneStart =  AloneStart();
        创建代理对象
        AloneStart startProxy = (AloneStart)  CglibProxy(aloneStart).getProxyInstance();
        用代理对象执行目标对象的方法
        startProxy.cooperation();
    }
}

输出结果:

收出厂费,化妆等
is show time
卸妆,回家

jdk采用反射机制调用委托类的方法,而cglib采用类似索引的方式直接调用委托类方法;

还有需要注意的是:

在Spring的AOP中

如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理

 参考:

Java的三种代理模式: https://www.cnblogs.com/cenyu/p/6289209.html

说说代理模式:http://www.importnew.com/26116.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结