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方法。

  • 相关阅读:
    my first android test
    VVVVVVVVVV
    my first android test
    my first android test
    my first android test
    ini文件
    ZZZZ
    Standard Exception Classes in Python 1.5
    Python Module of the Week Python Module of the Week
    my first android test
  • 原文地址:https://www.cnblogs.com/maydow/p/4850787.html
Copyright © 2011-2022 走看看