zoukankan      html  css  js  c++  java
  • 设计模式之

    代理模式:代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。很多可以框架中都有用到,比如: spring的AOP的实现主要就是动态代理, mybatis的Mapper代理等。

    如下来看下代理模式的UML图(来自百度图片):

      

    代理类和被代理类实现共同的接口, 其中代理类中包含一个被代理类的实例引用。代理模式可以分为静态代理和动态代理,这里主要学习下动态代理。动态代理作用可以实现业务代理和通用逻辑代码解耦,在不改变业务逻辑的同时,动态的给原逻辑代码添加一些通用功能,比如打印调用日志,权限判定,事务处理等等。

    下面用代码实现动态代理:

    1. 定义一个人的动作行为接口

    package cn.aries.pattern.ProxyPattern;
    /**
     * 人的行为接口
     * @author aries
     */
    public interface PersonAction {
    
        /**
         * 说话
         */
        public void personSay();
        /**
         * 跑步
         */
        public void personRunning();
        /**
         * 吃东西
         */
        public void personEating();
        
    }

    2. 创建人行为的的实现类

    package cn.aries.pattern.ProxyPattern;
    public class PersonActionImpl implements PersonAction{
        @Override
        public void personSay() {
            System.out.println("人在说话...");
        }
        @Override
        public void personRunning() {
            System.out.println("人在跑步...");        
        }
        @Override
        public void personEating() {
            System.out.println("人在吃东西...");
        }
    }

    3. 动态代理需要一个实现了InvoketionHandler接口的类

    package cn.aries.pattern.ProxyPattern;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyPerson implements InvocationHandler{
        //被代理的实例对象
        PersonAction obj;
        private ProxyPerson(PersonAction obj){
            this.obj = obj;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //执行方法之前打印动作开始。
            System.out.println(method.getName() + "ation start ...");
            //使用反射执行目标方法
            method.invoke(obj, args);
            //在方法执行结束时打印动作结束。
            System.out.println(method.getName() + "ation end ...");
            return null;
        }
       //定义一个静态方法生成代理对象
        public static Object getProxyPersonAction(PersonAction obj){
            PersonAction proxy = (PersonAction) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new ProxyPerson(obj));
            return proxy;
        }
    }

    4. 客户端代码

    package cn.aries.pattern.ProxyPattern;
    
    public class App {
        public static void main(String[] args) throws Exception {
            //设置系统参数,将生成的代理类的class文件保存到本地
            System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
            PersonAction pa = new PersonActionImpl();
            //调用生成代理类的方法
            PersonAction proxyPa = (PersonAction) ProxyPerson.getProxyPersonAction(pa);  
         //用代理对象调用目标方法 proxyPa.personSay(); proxyPa.personRunning(); proxyPa.personEating();
    //打印代理对象的父类 System.out.println(proxyPa.getClass().getSuperclass()); } }

    执行结果:

    personSayation start ...
    人在说话...
    personSayation end ...
    personRunningation start ...
    人在跑步...
    personRunningation end ...
    personEatingation start ...
    人在吃东西...
    personEatingation end ...
    class java.lang.reflect.Proxy

    当方法在中的是分别执行我们在目标方法执行前后添加的代码。

    5. 代理对象是通过Proxy.newProxyInstance(...)这个方法生成的,我们进入源代码查看下

    public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{
            if (h == null) {
                throw new NullPointerException();
            }
            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.
          这里生成代理类的字节码文件
    */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler.
    */
    try {
          //在这里获取代理类的构造函数,从前面的运行结果中可以得知,代理类是Proxy类的子类
          //而constructorParams在Proxy类中是一个静态的常量: private static final Class<?>[] constructorParams = { InvocationHandler.class };
          //所以这里获取的带InvocationHandler对象为入参的构造函数,也就是其父类Proxy的构造函数:protected Proxy(InvocationHandler h){...}
    final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h;
            //这里调用newInstance()方法创建代理对象,其内部实现是:return cons.newInstance(new Object[] {h} );使用反射通过含参(hanlder)生成代理对象。
           //其中h赋值给了其父类Proxy类的成员变量: protected InvocationHandler h;
           //最终在这里生成代理对象并返回
    if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { // create proxy instance with doPrivilege as the proxy class may // implement non-public interfaces that requires a special permission return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return newInstance(cons, ih); } }); } else { return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } }

     6. 到此我了解了代理对象的生产过程,但是代理对象和handler是什么关系呢,又是如何调用其invoke(...)方法呢,这暂时是个谜团让我们来看下生成的代理类的源码,这些就都清楚了。

      注:System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 这个是设置系统参数,将生产的代理类自己码文件保存在本地,然后我们通过反编译就可以获得其Java代码。

    package com.sun.proxy;
    
    import cn.aries.pattern.ProxyPattern.PersonAction;
    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 PersonAction {
        //这五个静态变量前三个m0,m1,m2分别是代理类继承的Object类的hashcode(),equals(),toString()方法
        //其他从m3开始是继承的们定义的接口类的方法根据方法的多少m后面的数字递增
        private static Method m1;
        private static Method m3;
        private static Method m5;
        private static Method m0;
        private static Method m4;
        private static Method m2;
        
        static {
            try {
                //这里使用静态代码块对通过反射对代理对象中的方法进行实例化
                m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
                m3 = Class.forName("cn.aries.pattern.ProxyPattern.PersonAction").getMethod("personEating", new Class[0]);
                m5 = Class.forName("cn.aries.pattern.ProxyPattern.PersonAction").getMethod("personRunning", new Class[0]);
                m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
                m4 = Class.forName("cn.aries.pattern.ProxyPattern.PersonAction").getMethod("personSay", new Class[0]);
                m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
                return;
            } catch (NoSuchMethodException localNoSuchMethodException) {
                throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
            } catch (ClassNotFoundException localClassNotFoundException) {
                throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
            }
        }
        
        public $Proxy0(InvocationHandler paramInvocationHandler)throws{
            super(paramInvocationHandler);
        }
    
        //这里是对我们定义的personEating方法进行实现
        //根据类文件我们可以看到,代理类继承了Proxy类,所以其成员变量中包含一个Handler实例对象的引用
        //在创建代理实例对象的时候,我们使用的protected Proxy(InvocationHandler h) {this.h = h;}这个构造函数
        //所以下面的h就是我们传进去的handler对象
        //这里使用handler对象调用自己的invoke()方法,m3就是我们要执行的方法,
        //后面的方法的参数,如果有参数就传对应的参数,没有就传null
        //此时我们明白了代理对象和handler的关系,以及如何调用到invoke()方法有了明确的认识了。
        public final void personEating()throws{
            try
            {
              this.h.invoke(this, m3, null);
              return;
            }catch (Error|RuntimeException localError){
              throw localError;
            }catch (Throwable localThrowable){
              throw new UndeclaredThrowableException(localThrowable);
            }
        }
        //这里原理同上,为了节省空间这里就不贴出来了
        public final void personSay(){...}
        public final void personRunning(){...}
    
        public final int hashCode()throws{
            try{
              return ((Integer)this.h.invoke(this, m0, null)).intValue();
            }catch (Error|RuntimeException localError){
              throw localError;
            }catch (Throwable localThrowable){
              throw new UndeclaredThrowableException(localThrowable);
            }
        }
        
        public final String toString()throws {
            try{
              return (String)this.h.invoke(this, m2, null);
            }catch (Error|RuntimeException localError){
              throw localError;
            }catch (Throwable localThrowable)
            {
              throw new UndeclaredThrowableException(localThrowable);
            }
        }
        
        public final boolean equals(Object paramObject)throws{
            try{
              return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
            }catch (Error|RuntimeException localError){
              throw localError;
            }catch (Throwable localThrowable){
              throw new UndeclaredThrowableException(localThrowable);
            }
        }
    }

    写完后浏览了一下,好像没有发现被代理对象的引用在代理类中出现;然后想了下,代理类继承了Proxy类,其中Proxy类中有我们写的InvoketionHandler对象的是实例,而这个handler实例中就存有我们创建的被代理对象的实例引用,在invoke方法中,传入的实例对象就是我们创建的这个被代理对象;这样就间接的持有了被代理对象的实例引用。

    到此动态代理的生成过程,以及是如何调用invoke()方法的原理已经搞清楚,到此本文完结。

  • 相关阅读:
    bugzilla 下载以及安装
    python 自定义异常
    centos7 安装testlink
    testlink errormsg:Your password does not satisfy the current policy requirements [<<][5fff09aa489f0831604244][DEFAULT][/testlink/install/installNewDB.php]
    httprunner中用output,可以在testcase间传递变量值
    windows下如何生成公钥和私钥
    selenium 页面滚动,滚动条滚动
    selenium 修改元素属性值
    MQ(Message Queue)消息队列
    Real-time operating system
  • 原文地址:https://www.cnblogs.com/qq-361807535/p/7106457.html
Copyright © 2011-2022 走看看