• Java实现静态代理、动态代理


    一、代理模式

      代理模式是设计模式中的一种结构型模式,在设计模式中算比较好理解的一种模式。具体来说就是使用代理对象来代替对真实对象的访问,当我们需要新增额外功能时,不需要修改目标对象就能达到功能扩展的效果。代理模式的关键点--代理对象与目标对象,代理对象是目标对象的扩展,并会调用目标对象。

      例子:记得几年前微商很火,小明的高中同学也很多在做微商(听说已经提玛莎拉蒂了!!!),每天朋友圈都被大量的广告刷屏。这些微商,大部分都是从厂家拿货,然后自己通过社交平台宣传,走向财富自由。现实生活中我们购买产品时,很少直接自己跑去找厂家购买,一般的销售模式都是厂家委托给代理商进行销售,而顾客直接向代理商购买即可,不需要与厂家直接联系。这里的代理商就类似我们的代理对象,而厂家就类似目标对象。

      优点:1、职责清晰。 2、高扩展性。3、智能化。

      缺点:1、在客户端和目标对象之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

      Java实现代理模式主要有静态代理和动态代理两种方式。

    二、静态代理

      1、基本概念

      静态代理从它的名字我们大概就能猜出来它是什么了,即在编译时就将已经将接口,被代理类,代理类确定下来了,在程序运行之前,代理类的.class文件就已经生成了。

       静态代理的实现步骤:

      a. 定义一个接口及其实现类,即被代理类;

      b. 创建一个代理类同样实现这个接口;

      c. 将目标对象注入这个代理类,然后在代理对象的对应方法调用目标类中的对应方法,在这期间我们可以在目标方法执行前后做一些自己想做的事情。

        2、情景引入

        小明的学校附近新开了一家蛋糕店,作为一名资深舔狗,小明已经迫不及待地想去店里买个大蛋糕送给他的女神了。一进店后,看到各种精美的蛋糕,小明就已经忍不住yy女神拿到蛋糕后感谢他的样子了。最后小明忍痛花了299买了个草莓奶油蛋糕准备送给女神。晚上,在女神宿舍楼下等送蛋糕的时候,小明闲得慌,便在脑袋里复习起最近正在学的设计模式,突然想到--“唉,这个草莓奶油蛋糕的制作过程,如何结合设计模式中的代理模式用Java来实现呢?”。

        3、代码实现 

         1)首先应该有一个做蛋糕的机器,因此我们先定义一个蛋糕机器的接口CakeMachine,同时在接口内定义一个制作蛋糕的方法。

    public interface CakeMachine {
      //制作蛋糕
    void makeCake(); }

        2)接着我们定义一个CakeMachine接口的实现类CakeMachineImpl,也就是我们的被代理类,通过这个实现类我们能做出奶油味的蛋糕胚。

    //制做奶油味蛋糕胚
    public
    class CakeMachineImpl implements CakeMachine { @Override public void makeCake() { System.out.println("奶油蛋糕制作完成!!!"); } }

       此时我们通过CakeMachineImpl制作的只是普通的奶油蛋糕胚,那如果我们要做草莓奶油蛋糕的话怎么办?难道专门再做一个制作草莓奶油蛋糕的机器吗?当然不是,如果这样的话,对于草莓巧克力蛋糕我们又得制作一个机器,就变得格外麻烦。此时我们通过代理模式就能很好的解决问题,通过创建代理类,帮我们实现在蛋糕制作完成之后“铺草莓”这个动作,这个代理类能对所有的CakeMachine的实现类都新增这一功能,这样我们就省去了制作多种机器的繁琐过程。

        3)创建代理类ProxyCakeMachine,并同样实现CakeMachine接口,这时候我们可以在蛋糕制作完成之后,在表面铺上美味的草莓,这样我们的草莓蛋糕就制作完成啦。

    public class ProxyMachine implements CakeMachine{
    
        private CakeMachine cakeMachine;
    
        public ProxyMachine(CakeMachine cakeMachine) {
            this.cakeMachine = cakeMachine;
        }
    
        @Override
        public void makeCake() {
            cakeMachine.makeCake();
            System.out.println("铺上美味的草莓~~~");
            System.out.println("草莓味蛋糕完成啦!!!");
        }
    }

        4)最后,当我们去蛋糕店购买时,老板使用的是代理对象进行操作。

    public class CakeShop {
    
        public static void main(String[] args) {
            CakeMachine cakeMachine = new CakeMachineImpl();
            ProxyMachine proxyMachine = new ProxyMachine(cakeMachine);
            proxyMachine.makeCake();
            
        }
    }

        运行结果:

    蛋糕制作完成!!!
    铺上美味的草莓~~~
    草莓味蛋糕完成啦!!!

    三、动态代理

      相较于静态代理,动态代理更加的灵活,我们不需要针对每个目标类都单独创建一个代理类。上面的例子中,ProxyCakeMachine类是我们自己定义好的,在程序运行前就已经编译完成,然而动态代理,代理类并不是在Java代码中定义好的,而是在运行期间根据我们在Java代码中的“指示”动态生成的。相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一处理,而不用修改每个代理类中方法。

      1、JDK动态代理机制

       在Java动态代理机制中,InvocationHandler接口和Proxy是核心,上述例子用Java动态代理的实现过程如下:

       a. 新建一个类,这个类必须实现InvocationHandler接口,并重写它的invoke方法,在invoke方法中实现我们的铺草莓动作。

    public class StrawberryHandler implements InvocationHandler {
    
        private Object object;
    
        public StrawberryHandler(Object object) {
            this.object = object;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object o = method.invoke(object, args);
            System.out.println("铺上美味的草莓~~~");
            System.out.println("草莓蛋糕完成啦!!!");
            return o;
        }
    }

      b. 在蛋糕店类中,我们使用Proxy类的newProxyInstance静态方法生成我们的代理对象,通过这个代理对象来制作我们的草莓奶油蛋糕。

    public class CakeShop {
    
        public static void main(String[] args) {
            CakeMachine cakeMachine = new CakeMachineImpl();
            StrawberryHandler strawberryHandler = new StrawberryHandler(cakeMachine);
            CakeMachine cakeMachine1 = (CakeMachine)Proxy.newProxyInstance(CakeMachineImpl.class.getClassLoader(),
                    CakeMachineImpl.class.getInterfaces(),
                    strawberryHandler);
            cakeMachine1.makeCake();
        }
    }

      执行结果:

    蛋糕制作完成!!!
    铺上美味的草莓~~~
    草莓蛋糕完成啦!!!

        newProxyInstance()方法的三个参数:

        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            ......
        }

        1)loader:类加载器,用于加载代理对象;

         2)interface:被代理类实现的一些接口;

         3)h:实现了InvocationHandler接口的对象。

      注意:使用动态代理,我们的“铺草莓”动作,即InvocationHandler,不仅能用在蛋糕机器接口CakeMachine的实现类,也能用于其他的接口实现类,如面包机器接口、饼干机器接口实现类。而在静态代理中,我们的静态代理类ProxyMachine只能对蛋糕机器接口的实现类起作用,若要实现在面包上铺草莓的动作,则需要另外创建面包机器的代理类。

      2、CGLIB实现动态代理

      JDK动态代理有一个最致命的问题,就是只能代理实现了接口的类,对于没有实现接口的类,JDK动态代理则无能为力。而CGLIB则可以为我们解决这个问题。(Spring的AOP模块中,如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理)

      在CGLIB动态代理中,MethodInterceptor接口和Enhancer类是核心。具体步骤如下:

      1)定义一个类;

      2)自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;

      3)通过 Enhancer 类的 create()创建代理类;

      CGLIB实现制作草莓奶油蛋糕:

      a. 在pom.xml中加入CGLIB的依赖坐标。

    <dependency>
           <groupId>cglib</groupId>
           <artifactId>cglib</artifactId>
           <version>3.1</version>
     </dependency>

      b. 自定义MethodInterceptor,并重写Interceptor方法。

    public class StrawberryInterceptor implements MethodInterceptor {
    
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            Object o1 = methodProxy.invokeSuper(o, objects);
            System.out.println("铺上美味的草莓~~~");
            System.out.println("草莓蛋糕完成啦!!!");
            return o1;
        }
    }

      MethodInterceptor接口参数解析:

    public interface MethodInterceptor
    extends Callback{
        // 拦截被代理类中的方法
        public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                                   MethodProxy proxy) throws Throwable;
    }

      1)obj :被代理的对象(需要增强的对象)

      2)method :被拦截的方法(需要增强的方法)

      3)args :方法入参

      4)methodProxy :用于调用原始方法

      c. 蛋糕商店类使用enhancer获取代理类

    public class CakeShop {
    
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(CakeMachineImpl.class);
            enhancer.setCallback(new StrawberryInterceptor());
            CakeMachineImpl cakeMachine = (CakeMachineImpl) enhancer.create();
            cakeMachine.makeCake();
        }
    }

      执行结果:

    蛋糕制作完成!!!
    铺上美味的草莓~~~
    草莓蛋糕完成啦!!!

    ----------------

    nice,小明很开心前几天学的设计模式还没有忘记。可女神就是迟迟没下楼,明明是炎炎夏日,小明心里却凉飕飕的。

      

  • 相关阅读:
    iOS 制作view渐变的效果CAGradientLayer
    iOS应用架构谈 view层的组织和调用方案
    xcode8 iOS10 log太多
    iOS 10 UserNotifications 框架解析
    iOS UITableViewCell 左滑删除时,修改删除按钮背景颜色,默认是红色的
    给self.navigationItem.rightBarButtonItem设置字体颜色
    iPhone屏幕尺寸、分辨率及适配
    iOS 判断输入是否全是空格
    ios 修改UItableviewcell点击时的颜色
    ios 播放语音(文字转语音) 播放音频文件 振动
  • 原文地址:https://www.cnblogs.com/wyy11/p/14674996.html
Copyright © 2020-2023  润新知