对代理模式的理解,模拟静态代理和动态代理

什么是代理模式

先说什么是代理,就是我能代表你,行使你的权力,同时,我也可以在不改变你的权力的情况下,行使自己的权力,而在编程中代理就是为其他对象提供一种代理以控制对这个对象的访问。代理可以在不改动目标对象的基础上,增加其他额外的功能(扩展功能)。

代理模式角色分为 3 种:

  • Subject(抽象主题角色):定义代理类和真实主题的公共对外方法,也是代理类代理真实主题的方法;
  • RealSubject(真实主题角色):真正实现业务逻辑的类;
  • Proxy(代理主题角色):用来代理和封装真实主题;

如果根据字节码的创建时机来分类,可以分为静态代理和动态代理:

  • 所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运行前就确定了。
  • 而动态代理的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件

静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象(目标对象)与代理对象(Proxy)一起实现相同的接口或者是继承相同父类。

动态代理

使用动态代理我们可以在不改变源码的情况下,直接在方法中插入自定义逻辑。这种动态编程思想也叫AOP(Aspect Oriented Programming ) 面向切面编程。总所周知,解耦是程序员编码开发过程中所一直追求的。而AOP正能解决此类问题,其具体思想就是:定义一个切面,在切面的纵向定义处理方法,处理完成之后,回到横向业务流。

模拟静态代理

通过比喻代理班长和班长帮助大家理解

  1. 创建Power接口
    package ProxyDemo;
    
    public interface Power {
        //定义权力的接口
        void manage();//管理权
        void supervise();//监督权
    }
    
  2. 创建班长实现类
    package ProxyDemo;
    //定义班长类,实现权力接口
    public class Monitor implements Power {
        @Override
        public void manage() {
            System.out.println("班长管理班级...");
        }
    
        @Override
        public void supervise() {
            System.out.println("班长监督班级成员学习...");
        }
    }
    
  3. 创建代理班长类
    package ProxyDemo;
    //代理班长,同样能形式权力,但在代理班长行使权力之前需要班长将班级事务转交给他
    public class ProxyMonitor implements Power{
        private Monitor monitor;
    
        public ProxyMonitor(Monitor monitor) {
            this.monitor = monitor;
        }
    
        @Override
        public void manage() {
            System.out.println("代理班长管理班级...");
        }
    
        @Override
        public void supervise() {
            System.out.println("代理班长监督班级成员学习...");
        }
        //代理班长自己的事务谈恋爱,因为他有女朋友,而班长没有
        public void tanlianai(){
            System.out.println("代理班长正在谈恋爱...");
        }
    }
    
  4. 静态代理测试类
    package ProxyDemo;
    
    public class Test1 {
        //静态代理演示
        public static void main(String[] args) {
            //新建班长
            Monitor monitor = new Monitor();
            //班长正在管理监督班级
            monitor.manage();
            monitor.supervise();
            //班长开会去了,找代理班长暂代
            ProxyMonitor proxyMonitor = new ProxyMonitor(monitor);
            proxyMonitor.manage();
            proxyMonitor.supervise();
            proxyMonitor.tanlianai();
        }
    }
    
  5. 模拟静态代理运行结果

image20211015224301166.png

Jdk动态代理

JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数:

方法是在Proxy类中是静态方法,且接收的三个参数依次为:

  • ClassLoader loader //指定当前目标对象使用类加载器
  • Class<?>[] interfaces //目标对象实现的接口的类型,使用泛型方式确认类型
  • InvocationHandler h //事件处理器

主要是完成InvocationHandler h的编写工作。

public interface InvocationHandler

InvocationHandler是由代理实例的调用处理程序实现的接口

每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。

invoke(Object proxy, 方法 method, Object[] args) 
处理代理实例上的方法调用并返回结果。 
  • 参数

    proxy - 调用该方法的代理实例

    method -所述方法对应于调用代理实例上的接口方法的实例。 方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。

    args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。 原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integerjava.lang.Boolean

  • 结果

    从代理实例上的方法调用返回的值。 如果接口方法的声明返回类型是原始类型,则此方法返回的值必须是对应的基本包装类的实例; 否则,它必须是可声明返回类型的类型。 如果此方法返回的值是null和接口方法的返回类型是基本类型,那么NullPointerException将由代理实例的方法调用抛出。 如上所述,如果此方法返回的值,否则不会与接口方法的声明的返回类型兼容,一个ClassCastException将代理实例的方法调用将抛出。

    模拟jdk动态代理

    package ProxyDemo;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class Test2 {
        //动态代理演示
        public static void main(String[] args) {
            //实现类的实例保存在接口的引用中
            Power monitor = new Monitor();
            //班长指定一个人说,现在你是代理班长,你想干啥干啥啊
            Power proxyMonitor = (Power) Proxy.newProxyInstance(monitor.getClass().getClassLoader(), monitor.getClass().getInterfaces(), new InvocationHandler() {
    
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    //行使原班长的权力(调用被代理对象的方法)
                    playBasketball();
                    Object o = method.invoke(monitor, args);
                    listenToMusic();
                    return o;
                }
                //写代理对象自己的方法
                public void playBasketball(){
                    System.out.println("代理班长上课之前出去打篮球");
                }
                public void listenToMusic(){
                    System.out.println("代理班长下课后听歌");
                }
            });
            //调用代理对象的方法
            proxyMonitor.supervise();
            proxyMonitor.manage();
        }
    }
    
模拟动态代理测试结果

image20211015224832894.png

L.X.Q.


温柔赠于四方,自由灵魂独享