zoukankan      html  css  js  c++  java
  • Java 动态代理机制简单理解

      Spring有两个核心的思想,一个是IOC,另一个就是AOP,而这个AOP就是建立在JAVA动态代理基础上的,下面先用一个简单的示例来说明动态代理的用法,然后简单叙述动态代理实现的原理。

    一、示例

    实现代理有四个步骤

    1、创建一个接口

    public interface Login {
        void validate();
        void login();
    }

    2、编写这个接口的实现类,这个类里面只包含业务方法

    public class LoginImpl implements Login{
    
        @Override
        public void validate() {
            // TODO Auto-generated method stub
            System.out.println("validate");
        }
    
        @Override
        public void login() {
            // TODO Auto-generated method stub
            System.out.println("login");
        }
    
    }

    3、创建代理类,实现InvacationHandler,通过这个代理类可以动态创建代理对象,并且完成相关业务。

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    
    public class LoginProxy implements InvocationHandler {
        private Object proxyObj;
        public  LoginProxy(Object obj)
        {
            this.proxyObj=obj;
        }
        
        public static Object bind(Object obj)
        {
            Class clz=obj.getClass();
            return Proxy.newProxyInstance(clz.getClassLoader(), clz.getInterfaces(), new LoginProxy(obj));
        }
        @Override
        public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
            // TODO Auto-generated method stub
            login(arg1);
            Object res=arg1.invoke(proxyObj, arg2);
            logout(arg1);
            return res;
        }
        
        private void login(Method method)
        {
            System.out.println("before "+method.getName());
        }
        
        private void logout(Method method)
        {
            System.out.println("after "+method.getName());
        }
    }

    4、创建测试类。

    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    
    import sun.misc.ProxyGenerator;
    public class Main {
        public static void main(String[] args) {
            //生成一个实现Login接口的类的字节码数组,测试用
            byte[] clazzFile = ProxyGenerator.generateProxyClass("$Proxy11", LoginImpl.class.getInterfaces());  
            OutputStream os=null;
            try {
                //生成的class文件放在bin文件夹里面
                os=new FileOutputStream(Main.class.getClassLoader().getResource("").getPath()+"/$Proxy11.class");
                os.write(clazzFile);
                os.flush();
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }finally{
                try {
                    os.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            //测试动态代理,生成一个代理对象,这个代理对象实现了LoginImpl实现的接口
            Login login=(Login)LoginProxy.bind(new LoginImpl());
            //代理类调用方法
            login.validate();
            login.login();
        }
    }

    输出

    before validate
    validate
    after validate
    before login
    login
    after login

    二、原理简述

      代理对象怎么获得的呢?这得从Proxy类的newProxyInstance开始,

        public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)  throws IllegalArgumentException
        {
            if (h == null) {
                throw new NullPointerException();
            }
            /*
             * Look up or generate the designated proxy class.
             */
            Class<?> cl = getProxyClass0(loader, interfaces); // stack walk magic: do not refactor
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                SecurityManager sm = System.getSecurityManager();
                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());
            }
        }

      具体的细节可以不去看他,这里只要看红色的部分,显示获取了一个代理类,这个代理类里面有个参数是接口,正是需要代理的对象的所实现的接口,最后返回一个这个类的实例,这个类怎么得到的呢?    //弱引用hashmap key是classloader,value是Map,缓存对应类加载器的接口数组 object存放的是生成的类的弱引用

         private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache= new WeakHashMap<>();
    //存放生成的代理类
        private static Map<Class<?>, Void> proxyClasses = Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>());

    private static Class<?> getProxyClass0(ClassLoader loader , Class<?>... interfaces) { ....................此处省略一对判断 ....................此处省略得到接口名数组             //对象的接口数组           List<String> key = Arrays.asList(interfaceNames); ....................又是一堆验证           //通过上面的接口数组生成一个类 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);   try {    proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);     } catch (ClassFormatError e) {    throw new IllegalArgumentException(e.toString());      }       }     // add to set of all generated proxy classes, for isProxyClass     proxyClasses.put(proxyClass, null);           }
              finally {       synchronized (cache) {           //这里的cache是Map<List<String>,Object>的对象,最上面的那个    if (proxyClass != null) {    cache.put(key, new WeakReference<Class<?>>(proxyClass));   } else {   cache.remove(key);   }     cache.notifyAll();      }     }     //返回类      return proxyClass;

      在这函数里面,先获取这个对象的接口数组,再在缓存中去搜索看看有没有这个类,具体过程就是先通过classLoader来找Map,根据这个接口链表来找存放类的弱引用,假如没有的话就重新生成,至于怎么生成这个类的字节码,太复杂了,这里就省略了。

      那么为什么这个实现了接口的代理对象调用的是login方法,而结果确实invoke被调用了呢?我们看一下生成的$Proxy1这个类,把它反编译一下就能够看到。

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class $Proxy11 extends Proxy  implements Login
    {
      private static Method m1;
      private static Method m4;
      private static Method m3;
      private static Method m0;
      private static Method m2;
      
      public $Proxy11(InvocationHandler paramInvocationHandler)
      {
        super(paramInvocationHandler);
      }
      
      public final boolean equals(Object paramObject)
      {
        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);
        }
      }
      
      public final void validate()
      {
        try
        {
          this.h.invoke(this, m4, null);
          return;
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
      
      public final void login()
      {
        try
        {
          this.h.invoke(this, m3, null);
          return;
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
      
      public final int hashCode()
      {
        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()
      {
        try
        {
          return (String)this.h.invoke(this, m2, null);
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
      
      static
      {
        try
        {
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m4 = Class.forName("Login").getMethod("validate", new Class[0]);
          m3 = Class.forName("Login").getMethod("login", new Class[0]);
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", 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());
        }
      }
    }

    而Proxy类里面

        protected InvocationHandler h;
    
        protected Proxy(InvocationHandler h) {
            doNewInstanceCheck();
            this.h = h;
        }

      到此真正的幕后已经出来了,就是生成了一个类,然后实例化一个对象,并把InvocationHandler接口的实现类的对象作为参数传进去,那么这个对象在调用login或者validate方法时,就会调用这个InvocationHandler接口的实现类的对象的invoke方法。

  • 相关阅读:
    django之分页
    linux后台运行和关闭、查看后台任务
    Django的模板系统
    Django的视图系统
    Django的配置文件(settings.py)
    Django的URL路由
    初始Django
    shell if判断总结
    一个抓取智联招聘数据并存入表格的python爬虫
    Python MySQLdb 查询中文出现问号的解决方法
  • 原文地址:https://www.cnblogs.com/maydow/p/4850787.html
Copyright © 2011-2022 走看看