• 【设计模式】结构型01代理模式(Proxy Pattern)


    代理模式(Proxy Pattern)


    定义:顾名思义,增加中间层,为其他对象提供一种代理以控制对这个对象的访问。核心在于代理二字。

    1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。

    2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。

    静态代理与动态代理:

    静态代理类:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
    动态代理类:在程序运行时,运用反射机制动态创建而成。
    静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
    静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。

    动态代理是实现JDK里的InvocationHandler接口的invoke方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过Proxy里的newProxyInstance得到代理对象。

    代码:静态代理:

    这里笔者通过一个替考的例子,演示了小学生王小虎找人替考被骗子骗的故事:

    package com.pat.proxy.staticproxy;
    /**
     * 统一接口
     * @author ZX
     *
     */
    public interface Student {
    	void exam();
    }
    /**
     * 被代理类-学生王小虎
     * @author ZX
     *
     */
    class BadStudent implements Student{
    	private String name="王小虎";
    
    	@Override
    	public void exam() {
    		System.out.println(name+"参加考试");
    		
    	}
    	public BadStudent(){
    	}
    	public BadStudent(String name){
    		this.name=name;
    	}
    }
    /**
     * 代理类-替考的骗子
     * 静态代理只代理固定的类
     * @author ZX
     *
     */
    class Swindler implements Student{
    	private String name;
    	private BadStudent badStudent;
    	
    	@Override
    	public void exam() {
    		//非重点,前后可以添加某些操作
    		System.out.println(name+"进入考场");
    		badStudent= new BadStudent(name);
    		badStudent.exam();
    		System.out.println(name+"没交试卷");
    		
    	}
    	public Swindler(String name){
    		this.name=name;
    	}
    }

    测试类:

    package com.pat.proxy.staticproxy;
    
    public class Test {
    	public static void main(String[] args) {
    		Student s = new Swindler("老司机");
    		s.exam();
    	}
    }
    

    结果:

    老司机进入考场
    老司机参加考试
    老司机没交试卷
    这就说明了一个问题,做生意得讲诚信,你好好搞替考业务,发展起来说不定还能开个连锁。这样子做生意是不会走的长远的!但是考虑到这个替考人没上过小学,也只能怪小虎人品不好了。

    代码:动态代理:

    以下是动态代理的演示,采用了JDK提供的工具,省了几十行代码:

    package com.pat.proxy;
    /**
     * 本类中
     */
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    /**
     * 被代理类接口
     */
    public interface People {
        public int getNum() ;
    }
    
    /**
     * 被代理类实现类
     */
    class Peopleimpl implements People{
         private int num=100;
    
        public int getNum() {
            return num;
        }
    
        public void setNum(int num) {
            this.num = num;
        }
    }
    /**
     *代理类,需要实现InvocationHandler接口
     */
    class MyInv implements InvocationHandler{
        //被代理对象
        Object obj;
    
        public  MyInv(Object obj){
            this.obj=obj;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = method.invoke(obj, args);
            return result;
        }
    }

    测试类:

    package com.pat.proxy;
    
    import java.lang.reflect.Proxy;
    import java.util.Objects;
    
    /**
     * 测试类:
     * 补充,非空判断这么用比较装逼:
     * People p2=null;
     * Objects.requireNonNull(p2,"第二个对象不能为空");
     */
    public class Test2 {
        public static void main(String[] args) {
            //动态代理有JDK和CGLIB两种工具,cglib是更底层的写法,所以效率会高一些
            People p = new Peopleimpl();
            //真正的代理者。
            MyInv my = new MyInv(p);
            //第一个参数是类加载器,第二个参数是这个代理者实现哪些接口(与被代理者实现的是相同的接口)
            People pp= (People)Proxy.newProxyInstance(p.getClass().getClassLoader(),p.getClass().getInterfaces(),my);
            System.out.println(pp.getNum());
    
    
    
    
        }
    }
    

    结果:

    100

    动态代理JDK中的实现:

    每日手记:
    2018年5月15日11:16:07
    Returns an instance of a proxy class for the specified interfaces
    返回一个指定接口的代理实例
    that dispatches method invocations to the specified invocation
    handler.
    将方法调用分派到指定的处理程序
    
    
        /**
         //糊上注释是为了看一下老外是怎么写注释的,即使看不懂全部,也可以对比自己的注释参考一下,其实注释写得多也给人感觉很牛逼
         * Returns an instance of a proxy class for the specified interfaces
         * that dispatches method invocations to the specified invocation
         * handler.
         *
         * <p>{@code Proxy.newProxyInstance} throws
         * {@code IllegalArgumentException} for the same reasons that
         * {@code Proxy.getProxyClass} does.
         *
         * @param   loader the class loader to define the proxy class
         * @param   interfaces the list of interfaces for the proxy class
         *          to implement
         * @param   h the invocation handler to dispatch method invocations to
         * @return  a proxy instance with the specified invocation handler of a
         *          proxy class that is defined by the specified class loader
         *          and that implements the specified interfaces
         * @throws  IllegalArgumentException if any of the restrictions on the
         *          parameters that may be passed to {@code getProxyClass}
         *          are violated
         * @throws  SecurityException if a security manager, <em>s</em>, is present
         *          and any of the following conditions is met:
         *          <ul>
         *          <li> the given {@code loader} is {@code null} and
         *               the caller's class loader is not {@code null} and the
         *               invocation of {@link SecurityManager#checkPermission
         *               s.checkPermission} with
         *               {@code RuntimePermission("getClassLoader")} permission
         *               denies access;</li>
         *          <li> for each proxy interface, {@code intf},
         *               the caller's class loader is not the same as or an
         *               ancestor of the class loader for {@code intf} and
         *               invocation of {@link SecurityManager#checkPackageAccess
         *               s.checkPackageAccess()} denies access to {@code intf};</li>
         *          <li> any of the given proxy interfaces is non-public and the
         *               caller class is not in the same {@linkplain Package runtime package}
         *               as the non-public interface and the invocation of
         *               {@link SecurityManager#checkPermission s.checkPermission} with
         *               {@code ReflectPermission("newProxyInPackage.{package name}")}
         *               permission denies access.</li>
         *          </ul>
         * @throws  NullPointerException if the {@code interfaces} array
         *          argument or any of its elements are {@code null}, or
         *          if the invocation handler, {@code h}, is
         *          {@code null}
         */
        @CallerSensitive
        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            Objects.requireNonNull(h);
            //克隆了接口参数
            final Class<?>[] intfs = interfaces.clone();
            //安全管理器,有关权限以及安全的东西,无视之
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
    	//下面就是通过反射创建动态代理对象的过程了:
    				
            /*
             * Look up or generate the designated proxy class.查找或生成指定的代理类。           
               //如果由给定的加载器定义的代理类。
               //给定的接口存在,这将简单地返回缓存的副本(这个缓存取自哪里?猜想:类加载???);
               //否则,它将通过ProxyClassFactory创建代理类。
    	         //如果没有接口参数intfs,则会抛出空指针
             */
            Class<?> cl = getProxyClass0(loader, intfs);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
    	    //获取构造方法
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (!Modifier.isPublic(cl.getModifiers())) {//非公有方法处理
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            cons.setAccessible(true);
                            return null;
                        }
                    });
                }
                //返回创建的代理对象
                return cons.newInstance(new Object[]{h});
            } catch (IllegalAccessException|InstantiationException e) {
                throw new InternalError(e.toString(), e);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString(), t);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
        }

  • 相关阅读:
    SSM实现mysql数据库账号密码加密连接
    获取系统相关信息 (CPU使用率 内存使用率 系统磁盘大小)
    JavaWeb(一) / /* /**的区别
    IDEA(一) 使用IDEA搭建SSM框架项目
    Mysql连接数据库异常汇总【必收藏】
    Java代理模式及动态代理详解
    SpringBoot集成Thymeleaf
    设计师,程序员,当心字体侵权
    Java开发神器Lombok使用详解
    日期格式化跨年bug,是否与你不期而遇?
  • 原文地址:https://www.cnblogs.com/the-fool/p/11054141.html
Copyright © 2020-2023  润新知