• java常用设计模式八:代理模式


    一、概述

    代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。 其特征是代理类与委托类有同样的接口,真正的核心业务逻辑还是在实际对象里面。

    二、为什么要使用代理模式

    • 当客户端不想直接调用实际对象,或是客户端直接调用实际对象有困难。
    • 比如:想在实际对象的业务方法执行前或执行后额外处理一些事情。但是又不能修改原来的实际对象的方法,这时候也可以用代理模式。

    三、代理模式的分类

     1、静态代理(在程序运行前,代理类的.class文件已经存在,所以称为静态代理)

    1)原业务接口

    public interface SourceObject {
        void operation();
    }

    2)原业务接口的实现类

    public class SourceObjectImpl implements SourceObject {
        public void operation() {
            System.out.println("这是原业务核心方法");
        }
    }

    3)静态代理类

    public class StaticProxy implements SourceObject {
        private SourceObjectImpl srcObj;
        public StaticProxy(SourceObjectImpl srcObj){
            this.srcObj = srcObj;
        }
        public void operation() {
            System.out.println("原业务方法执行前:记录处理中日志");
            srcObj.operation();
            System.out.println("原业务方法执行前:记录完成日志");
        }
    }
    public class StaticProxy implements SourceObject {
        private SourceObject srcObj;
        public StaticProxy(SourceObject srcObj){
            this.srcObj = srcObj;
        }
        public void operation() {
            System.out.println("原业务方法执行前:记录处理中日志");
            srcObj.operation();
            System.out.println("原业务方法执行前:记录完成日志");
        }
    }

    4)测试类

    public class Client {
        public static void main(String[] args){
            SourceObject srcObj = new SourceObjectImpl();
            StaticProxy staticProxy = new StaticProxy(srcObj);
            staticProxy.operation();
        }
    }
    原业务方法执行前:记录处理中日志
    这是原业务核心方法
    原业务方法执行前:记录完成日志

    由以上的结构可知:每个接口(SourceObject)有一个与之对应的代理类(StaticProxy),如果再有一个接口,那么就要同时新增一个代理类,这种情况在接口很多的时候,就会产生很多代理类。是否能只有一个代理类呢?当然是可以的,就是动态代理

     2、动态代理(在程序运行前,代理类的.class文件不存在,是运行时序由Java反射机制动态生成)

      动态代理主要是借助 类Proxy、接口InvocationHandler 来实现的。下面以一个具体的例子来说明 动态代理的过程

      1)原业务接口

    public interface SourceObject {
        void operation();
    }

    2)原业务接口的实现类

    public class SourceObjectImpl implements SourceObject {
        public void operation() {
            System.out.println("这是原业务核心方法");
        }
    }

    3)新建动态代理类处理器(注意,该类并不是SourceObject的代理类,它并没有实现于接口SourceObject,因此它不能调用operation()方法,这是与静态代理 最大的不同之处,代理类是程序运行的时候才动态产生的。)

    public class DynamicProxyHandler implements InvocationHandler {
        private Object srcObj;//被代理的实际对象,定义为Object类型表明,可以是任意对象
    
        /**
         * 该方法负责动态创建代理类,并返回代理类的实例,这个实例的类继承于java.lang.reflect.Proxy,还实现了传入进来的srcObject的原业务接口。
         *
         * @param srcObj
         * @return
         */
        public Object getProxyInstance(Object srcObj) {
            this.srcObj = srcObj;
            return Proxy.newProxyInstance(srcObj.getClass().getClassLoader(), srcObj.getClass().getInterfaces(), this);
        }
    
        /**
         * 注意:客户端不会真正的显示调用该方法,当通过getProxyInstance取得的实例执行真正的方法时,该方法会执行。
         *
         * @param proxy  参数传递的是动态生成的代理类的实例,本方法并没有使用它
         * @param method 是调用的方法,即需要执行的方法
         * @param args   是执行方法的参数
         * @return
         * @throws Throwable
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("原业务方法执行前:记录处理中日志");
            System.out.println("invoke 方法的第一个参数proxy 的类是:" + proxy.getClass().toString());
            method.invoke(srcObj, args);
            System.out.println("原业务方法执行前:记录完成日志");
            return null;
        }
    }

    4)测试类

    public class Client {
        public static void main(String[] args){
            SourceObject srcObj = new SourceObjectImpl();
            DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler();
            //为什么这里可以用SourceObject来接收getProxyInstance返回的实例呢?从后面的结果可知道动态产生的代理类实质上是继承于Proxy类,并实现于SourceObject
            SourceObject dynamicProxyInstance = (SourceObject)dynamicProxyHandler.getProxyInstance(srcObj);
            System.out.println("dynamicProxyInstance 的类是:================="+dynamicProxyInstance.getClass().toString());
            System.out.println("dynamicProxyInstance 的父类是:==============="+dynamicProxyInstance.getClass().getSuperclass());
            System.out.println("dynamicProxyInstance 实现的接口如下:===========");
            Class<?>[] interfaces=dynamicProxyInstance.getClass().getInterfaces();
            for(Class<?> i:interfaces){
                System.out.println(i.getName());
            }
            System.out.println("dynamicProxyInstance 的方法有:===============");
            Method[] method=dynamicProxyInstance.getClass().getDeclaredMethods();
            for(Method m:method){
                System.out.println(m.getName());
            }
            //执行真正的核心业务方法
            System.out.println("dynamicProxyInstance 执行原业务方法:===============");
            dynamicProxyInstance.operation();
    
    
            //以下的代码是将动态生成的代理类保存到target的classes里面,便于查看。
            byte[] bts = ProxyGenerator.generateProxyClass("$Proxy0", interfaces);
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(new File("target/classes/proxy/demo/$Proxy0.class"));
                fos.write(bts);
                fos.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    dynamicProxyInstance 的类是:=================class com.sun.proxy.$Proxy0
    dynamicProxyInstance 的父类是:===============class java.lang.reflect.Proxy
    dynamicProxyInstance 实现的接口如下:===========
    proxy.demo.SourceObject
    dynamicProxyInstance 的方法有:===============
    equals
    toString
    hashCode
    operation
    dynamicProxyInstance 执行原业务方法:===============
    原业务方法执行前:记录处理中日志
    invoke 方法的第一个参数proxy 的类是:class com.sun.proxy.$Proxy0
    这是原业务核心方法
    原业务方法执行前:记录完成日志

    由以上结果可知:程序运行时动态生成了一个代理类com.sun.proxy.$Proxy0,它的父类是Proxy,并且还实现了接口SourceObject。这个代理类是由Proxy的 newProxyInstance 方法生成的。生成的代理类的源代码如下:

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    import proxy.demo.SourceObject;
    
    public final class $Proxy0 extends Proxy implements SourceObject {
        private static Method m1;
        private static Method m3;
        private static Method m2;
        private static Method m0;
    
        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 operation() throws  {
            try {
                super.h.invoke(this, m3, (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 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);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m3 = Class.forName("proxy.demo.SourceObject").getMethod("operation");
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }

    当调用  dynamicProxyInstance.operation() 的时候,实质上就是调用了以上生成的代理类中红色标注的方法。

       super.h.invoke(this, m3, (Object[])null);

    super就是指父类Proxy,h就是指InvocationHandler的实例,这里应该是传入的子类DynamicProxyHandler 的实例,所以h.invoke方法,就是执行DynamicProxyHandler的invoke方法。

  • 相关阅读:
    nginx日志、变量
    http相关
    nginx.conf文件的使用
    NA交换①
    第一章 何为网络
    第二章 以太网
    SATA、SCSI、SAS
    第十章 安全
    附录A 思科互联网络操作系统(IOS)
    标准ACL、扩展ACL和命名ACL的配置详解
  • 原文地址:https://www.cnblogs.com/boshen-hzb/p/10251619.html
Copyright © 2020-2023  润新知