美文网首页
回顾一下基础之静态代理&动态代理

回顾一下基础之静态代理&动态代理

作者: 未扬帆的小船 | 来源:发表于2019-12-11 10:55 被阅读0次

    项目中常用到代理模式,本篇文章带大家回顾一下基本的操作以及动态代理生成的类。

    1. 定义

    代理模式: 为其它对象提供代理,带你对象挟持原对象类的引用,也称委托模式。
    作用:可以在不修改原对象的功能前提下,对原对象在功能进行扩展。(通俗讲就是在当你原对象封装完毕或者你没办法修改,但是有一些增加的新的功能的时候,就可以在代理类上增加 符合“开闭原则”)

    2. 代理的方式

    • 静态代理

    使用:手动创建源代码,在对其编译。
    缺点:代理类跟原对象类实现一样的接口,所以会有很多代理类。并且,当接口增加方法,目标对象与代理对象都要对应修改。(怎么解决?改用动态代理。)

    • 动态代理

    使用:在程序运行时,利用反射机制动态创建。

    3. 基本方式

    • 静态代理的使用方式
      (下面以高低端电脑的例子展示一下基本使用方式)
      image.png
    1. 定义接口
    /**
     * Create by ldr
     * on 2019/12/10 15:06.
     * 定义接口,电脑接口
     */
    public interface Computer {
        void screen();//显示器
        void memoryBank();//内存条
        void mainBoard();//主板
        void graphicsCard();//显卡
    }
    
    1. 分别定义两个被代理对象
    ------------------------------------------高配版电脑---------------------------------------------------
    /**
     * Create by ldr
     * on 2019/12/10 15:08.
     * 高配电脑
     */
    public class HeightConfigurationComputer implements Computer{
        private static final String TAG = "HeightComp";
        @Override
        public void screen() {
            System.out.println("高配置电脑 -- 屏幕");
        }
    
        @Override
        public void memoryBank() {
            System.out.println("高配置电脑 -- 内存条");
        }
    
        @Override
        public void mainBoard() {
            System.out.println("高配置电脑 -- 主板");
        }
    
        @Override
        public void graphicsCard() {
            System.out.println("高配置电脑 -- 显卡");
        }
    }
    
    ------------------------------------------低配版电脑---------------------------------------------------
    /**
     * Create by ldr
     * on 2019/12/10 15:08.
     * 低配电脑
     */
    public class LowConfigurationComputer implements Computer {
        private static final String TAG = "LowComputer";
        @Override
        public void screen() {
            System.out.println("低配置电脑 -- 屏幕");
        }
    
        @Override
        public void memoryBank() {
            System.out.println("低配置电脑 -- 内存条");
        }
    
        @Override
        public void mainBoard() {
            System.out.println("低配置电脑 -- 主板");
        }
    
        @Override
        public void graphicsCard() {
            System.out.println("低配置电脑 -- 显卡");
        }
    }
    
    1. 创建静态代理类
    /**
     * Create by ldr
     * on 2019/12/10 15:14.
     * 静态代理
     */
    public class ComputerStaticPro implements Computer {
    
        private Computer mComputer;
    
        public void setProx(Computer computer){
            mComputer = computer;
        }
    
        @Override
        public void screen() {
            System.out.println("代理类可先做共用一些逻辑~");
            mComputer.screen();
        }
    
        @Override
        public void memoryBank() {
            System.out.println("代理类可先做共用一些逻辑~");
            mComputer.memoryBank();
        }
    
        @Override
        public void mainBoard() {
            System.out.println("代理类可先做共用一些逻辑~");
            mComputer.mainBoard();
        }
    
        @Override
        public void graphicsCard() {
            System.out.println("代理类可先做共用一些逻辑~");
            mComputer.graphicsCard();
        }
    }
    
    
    1. 使用方式
           HeightConfigurationComputer heightConfigurationComputer = new HeightConfigurationComputer();
            ComputerStaticPro staticPro = new ComputerStaticPro();
            staticPro.setProx(heightConfigurationComputer);
            staticPro.graphicsCard();
            staticPro.mainBoard();
            staticPro.screen();
            staticPro.memoryBank();
    
            //低配电脑
            staticPro.setProx(new LowConfigurationComputer());
            staticPro.graphicsCard();
            staticPro.mainBoard();
            staticPro.screen();
            staticPro.memoryBank();
    -----------------------打印出来的Log
    代理类可先做共用一些逻辑~
    高配置电脑 -- 显卡
    代理类可先做共用一些逻辑~
    高配置电脑 -- 主板
    代理类可先做共用一些逻辑~
    高配置电脑 -- 屏幕
    代理类可先做共用一些逻辑~
    高配置电脑 -- 内存条
    代理类可先做共用一些逻辑~
    低配置电脑 -- 显卡
    代理类可先做共用一些逻辑~
    低配置电脑 -- 主板
    代理类可先做共用一些逻辑~
    低配置电脑 -- 屏幕
    代理类可先做共用一些逻辑~
    低配置电脑 -- 内存条
    
    • 动态代理的使用方式
    1. 创建动态代理类
    /**
     * Create by ldr
     * on 2019/12/10 15:55.
     * 动态代理
     */
    public class ComputerDynamicPro implements InvocationHandler {
        //动态代理类需要被代理的实例
        private Computer mComputer;
    
        public ComputerDynamicPro(Computer computer){
            mComputer = computer;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //关键点1
            //执行被代理的方法的类  方法被调用时就会自动调用
            Object invoke =  method.invoke(mComputer,args);
            //关键点2 
            String name = method.getName();
            if (name.equals("screen")){
                System.out.println(mComputer.getClass().getSimpleName()+"  screen()" );
            }else if (name.equals("memoryBank")){
                System.out.println(mComputer.getClass().getSimpleName()+"  memoryBank()" );
            }else if (name.equals("mainBoard")){
                System.out.println(mComputer.getClass().getSimpleName()+"  mainBoard()" );
            }else if(name.equals("graphicsCard")){
                System.out.println(mComputer.getClass().getSimpleName()+"  graphicsCard()" );
            }
            return invoke;
        }
    }
    

    动态代理需要实现的是InvocationHandler接口,并重写invoke方法,同样也是要挟持被代理类的对象。
    invoke方法中可以看到,使用的method.invoke(mComputer,args);传入对象跟args便可以实现方法被调用时自动调用。

    在这个地方,其实你可以做的事情很多,比如你想知道方式的执行时间,你可以在method.invoke()关键点1关键点2,加上时间,根据两个时间差来计算一下这个方法的大致运行时间。(实现AOP(面向程序切面编程))

    1. 使用
    //关键代码1
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    //        //使用动态代理
    //        //高配电脑
    HeightConfigurationComputer heightConfigurationComputer = new HeightConfigurationComputer(); 
    ClassLoader classLoader = heightConfigurationComputer.getClass().getClassLoader();
    Class[] interfaces = heightConfigurationComputer.getClass().getInterfaces();
    ComputerDynamicPro computerDynamicPro = new ComputerDynamicPro(heightConfigurationComputer);
    
    //通过Proxy.newProxyInstance生成代理类对象
    // 关键代码2
    Object newProxyInstance = Proxy.newProxyInstance(classLoader,interfaces,computerDynamicPro);
    Computer computer = (Computer) newProxyInstance;
    computer.graphicsCard();
    computer.mainBoard();
    computer.memoryBank();
    computer.screen();
    

    关键代码1:是看人家网上说的可以将代理类生成的一句代码,等下再贴一下代理类的代码
    关键代码2:这里可以看到Proxy.newProxyInstance传入了三个参数。

    第一个参数ClassLoader loader:被代理的类的类加载器,通过它生成代理的class文件。

    第二个参数Class<?>[] interfaces:被代理类实现的所有接口字节码,代理类需要知道并实现

    第三个参数InvocationHandler h:调用处理程序调度方法调用,也就是第一步我们定义好的那个ComputerDynamicPro 实现了InvocationHandler接口的动态代理类。

    我们来看一下关键代码1中为我们生成的代理类的代码吧

    
    package com.sun.proxy;
    
    import com.mzs.aptpro.Computer;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class $Proxy0 extends Proxy implements Computer {
        private static Method m1;
        private static Method m5;
        private static Method m2;
        private static Method m3;
        private static Method m6;
        private static Method m0;
        private static Method m4;
    
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
        public final boolean equals(Object var1) throws  {
            try {
                return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final void graphicsCard() throws  {
            try {
                super.h.invoke(this, m5, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final String toString() throws  {
            try {
                return (String)super.h.invoke(this, m2, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final void mainBoard() throws  {
            try {
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final void screen() throws  {
            try {
                super.h.invoke(this, m6, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final int hashCode() throws  {
            try {
                return (Integer)super.h.invoke(this, m0, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final void memoryBank() throws  {
            try {
                super.h.invoke(this, m4, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m5 = Class.forName("com.mzs.aptpro.Computer").getMethod("graphicsCard");
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m3 = Class.forName("com.mzs.aptpro.Computer").getMethod("mainBoard");
                m6 = Class.forName("com.mzs.aptpro.Computer").getMethod("screen");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
                m4 = Class.forName("com.mzs.aptpro.Computer").getMethod("memoryBank");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    
    

    可以看到上面class $Proxy0 extends Proxy implements Computer也是要实现我们的接口。在代码最下面可以看到静态代码块中,使用了反射将所有方法获取到。
    每个方法中都调用super.h.invoke()这样的一个代码 并且传入三个参数this,反射获取的方法,参数数组
    super.h实际上父类的h也是$Proxy0中的InvocationHandler,在构造函数的时候传递上去的public $Proxy0(InvocationHandler var1) throws { super(var1); }
    这里你就可以明白了为什么我们要动态代理类要实现InvocationHandler并实现invoke方法,并且inovke方法的三个参数是什么时候传递进来的。

    总结一下:

    静态代理跟动态代理的区别:
    静态代理:需要自己手动生成类文件,并且被代理类跟代理类都需要实现同样的接口,当接口改变时代理类跟被代理类都需要修改。
    动态代理:只需实现InvocationHandler,并重写invoke方法。不需要知道也用不着实现接口,当接口改变的时候也只需要改变invoke中的逻辑便可以。

    相关文章

      网友评论

          本文标题:回顾一下基础之静态代理&动态代理

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