• java 动态代理分析


    为什么要使用代理。

    先看一个简单的使用案例

     1 import java.lang.reflect.Proxy;
     2 
     3 public class ProxyTest {
     4 
     5     public static void main(String[] args) {
     6      //开启保存代理类
     7         System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
     8      
     9         Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), new Class[]{ProxyDemo.class}, new InvocationHandlerDemo());
    10 
    11     }
    12 }
    Proxy.newProxyInstance方法传入三个参数,类加载器,需要被代理的接口,代理类处理接口。结果返回的是Object对象。
    jdk版本1.7,源码如下:
    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            if (h == null) {
                throw new NullPointerException();
            }
         //检查权限
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
            }
    
            //生成代理类,核心是这个方法
            Class<?> cl = getProxyClass0(loader, interfaces);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
           //获取构造器
    final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = 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()); } }
        private static Class<?> getProxyClass0(ClassLoader loader,
                                               Class<?>... interfaces) {
            if (interfaces.length > 65535) {
                throw new IllegalArgumentException("interface limit exceeded");
            }
    
         //首先从缓存中获取代理类,如果没有,通过Proxy.ProxyClassFactory类生成代理类并放入缓存,然后返回代理类。 return proxyClassCache.get(loader, interfaces); }

    下面分析 Proxy.ProxyClassFactory 类的apply方法:

     1 @Override
     2         public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
     3 
     4             Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
     5             for (Class<?> intf : interfaces) {
     6                 /*
     7                  * 验证代理接口
     8                  * 
     9                  */
    10                 Class<?> interfaceClass = null;
    11                 try {
    12                     interfaceClass = Class.forName(intf.getName(), false, loader);
    13                 } catch (ClassNotFoundException e) {
    14                 }
    15                 if (interfaceClass != intf) {
    16                     throw new IllegalArgumentException(
    17                         intf + " is not visible from class loader");
    18                 }
    19                 /*
    20                  * 验证是否是接口
    21                  * 
    22                  */
    23                 if (!interfaceClass.isInterface()) {
    24                     throw new IllegalArgumentException(
    25                         interfaceClass.getName() + " is not an interface");
    26                 }
    27                 /*
    28                  * 验证接口是否有多个
    29                  */
    30                 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
    31                     throw new IllegalArgumentException(
    32                         "repeated interface: " + interfaceClass.getName());
    33                 }
    34             }
    35 
    36             String proxyPkg = null;     // package to define proxy class in
    37 
    38             /*
    39              *  
    40              *  验证同一包下非公共接口,包名是否一致
    41              *  
    42              */
    43             for (Class<?> intf : interfaces) {
    44                 int flags = intf.getModifiers();
    45                 if (!Modifier.isPublic(flags)) {
    46                     String name = intf.getName();
    47                     int n = name.lastIndexOf('.');
    48                     String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
    49                     if (proxyPkg == null) {
    50                         proxyPkg = pkg;
    51                     } else if (!pkg.equals(proxyPkg)) {
    52                         throw new IllegalArgumentException(
    53                             "non-public interfaces from different packages");
    54                     }
    55                 }
    56             }
    57 
    58             if (proxyPkg == null) {
    59                 // 如果是pulic接口,采用默认包名
    60                 proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
    61             }
    62 
    63             /*
    64              * 组装代理类名
    65              */
    66             long num = nextUniqueNumber.getAndIncrement();
    67             String proxyName = proxyPkg + proxyClassNamePrefix + num;
    68 
    69             /*
    70              * 生成代理类,核心方法是ProxyGenerator 中的 generateClassFile方法。
    71              */
    72             byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
    73                 proxyName, interfaces);
    74             try {
    75                 return defineClass0(loader, proxyName,
    76                                     proxyClassFile, 0, proxyClassFile.length);
    77             } catch (ClassFormatError e) {
    78                 /*
    79                  * A ClassFormatError here means that (barring bugs in the
    80                  * proxy class generation code) there was some other
    81                  * invalid aspect of the arguments supplied to the proxy
    82                  * class creation (such as virtual machine limitations
    83                  * exceeded).
    84                  */
    85                 throw new IllegalArgumentException(e.toString());
    86             }
    87         }

     private byte[] generateClassFile() {
         //添加hashCode、equals、toString方法,到代理方法集合中
    this.addProxyMethod(hashCodeMethod, Object.class); this.addProxyMethod(equalsMethod, Object.class); this.addProxyMethod(toStringMethod, Object.class); int var1; int var3;
         //遍历代理接口,将接口中的方法添加到代理方法集合中
    for(var1 = 0; var1 < this.interfaces.length; ++var1) { Method[] var2 = this.interfaces[var1].getMethods(); for(var3 = 0; var3 < var2.length; ++var3) { this.addProxyMethod(var2[var3], this.interfaces[var1]); } } Iterator var7 = this.proxyMethods.values().iterator();      //验证代理方法集合中返回类型 List var8; while(var7.hasNext()) { var8 = (List)var7.next(); checkReturnTypes(var8); } Iterator var11; try {
           //生成含有InvoicationHandler参数的构造方法,并添加到方法集合中
    this.methods.add(this.generateConstructor()); var7 = this.proxyMethods.values().iterator();        //遍历代理方法集合,生成方法,字段,放入到方法集合,字段集合中 while(var7.hasNext()) { var8 = (List)var7.next(); var11 = var8.iterator(); while(var11.hasNext()) { ProxyGenerator.ProxyMethod var4 = (ProxyGenerator.ProxyMethod)var11.next(); this.fields.add(new ProxyGenerator.FieldInfo(var4.methodFieldName, "Ljava/lang/reflect/Method;", 10)); this.methods.add(var4.generateMethod()); } }        //生成类构造器,添加到方法中 this.methods.add(this.generateStaticInitializer()); } catch (IOException var6) { throw new InternalError("unexpected I/O Exception"); } if (this.methods.size() > 65535) { throw new IllegalArgumentException("method limit exceeded"); } else if (this.fields.size() > 65535) { throw new IllegalArgumentException("field limit exceeded"); } else { this.cp.getClass(dotToSlash(this.className)); this.cp.getClass("java/lang/reflect/Proxy"); for(var1 = 0; var1 < this.interfaces.length; ++var1) { this.cp.getClass(dotToSlash(this.interfaces[var1].getName())); } this.cp.setReadOnly();
           //构造字节输出流,按照class文件格式,顺序输出到字节数组中 ByteArrayOutputStream var9
    = new ByteArrayOutputStream(); DataOutputStream var10 = new DataOutputStream(var9); try {
              //写入魔数 var10.writeInt(
    -889275714);
              //写入此版本号 var10.writeShort(
    0);
              //写入主版本号 var10.writeShort(
    49);
              //写入常量池
    this.cp.write(var10);
              //写入访问修饰符 var10.writeShort(
    49);
              //写入类索引 var10.writeShort(
    this.cp.getClass(dotToSlash(this.className)));
              //写入父类索引 var10.writeShort(
    this.cp.getClass("java/lang/reflect/Proxy"));
              //写入接口数量 var10.writeShort(
    this.interfaces.length);           //写入接口 for(var3 = 0; var3 < this.interfaces.length; ++var3) { var10.writeShort(this.cp.getClass(dotToSlash(this.interfaces[var3].getName()))); }           //写入字段计数 var10.writeShort(this.fields.size()); var11 = this.fields.iterator();           //写入字段 while(var11.hasNext()) { ProxyGenerator.FieldInfo var12 = (ProxyGenerator.FieldInfo)var11.next(); var12.write(var10); }            //写入方法计数 var10.writeShort(this.methods.size()); var11 = this.methods.iterator();           //写入方法 while(var11.hasNext()) { ProxyGenerator.MethodInfo var13 = (ProxyGenerator.MethodInfo)var11.next(); var13.write(var10); }           //写入属性计数 var10.writeShort(0); return var9.toByteArray(); } catch (IOException var5) { throw new InternalError("unexpected I/O Exception"); } } }

    从上面源码,可以看出生成的代理类应该包括代理接口中所有方法,Object的hashcode,equals,toString方法,一个含有InvocationHandler参数的实例构造方法,一个类初始方法。下面让我们看下生成的代理类源码:

     1 public final class $Proxy0 extends Proxy implements ProxyDemo {
     2     private static Method m1;
     3     private static Method m3;
     4     private static Method m0;
     5     private static Method m2;
     6 
     7     public $Proxy0(InvocationHandler var1) throws  {
     8         super(var1);
     9     }
    10 
    11     public final boolean equals(Object var1) throws  {
    12         try {
    13             return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
    14         } catch (RuntimeException | Error var3) {
    15             throw var3;
    16         } catch (Throwable var4) {
    17             throw new UndeclaredThrowableException(var4);
    18         }
    19     }
    20 
    21     public final void sayHello(String var1) throws  {
    22         try {
    23             super.h.invoke(this, m3, new Object[]{var1});
    24         } catch (RuntimeException | Error var3) {
    25             throw var3;
    26         } catch (Throwable var4) {
    27             throw new UndeclaredThrowableException(var4);
    28         }
    29     }
    30 
    31     public final int hashCode() throws  {
    32         try {
    33             return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
    34         } catch (RuntimeException | Error var2) {
    35             throw var2;
    36         } catch (Throwable var3) {
    37             throw new UndeclaredThrowableException(var3);
    38         }
    39     }
    40 
    41     public final String toString() throws  {
    42         try {
    43             return (String)super.h.invoke(this, m2, (Object[])null);
    44         } catch (RuntimeException | Error var2) {
    45             throw var2;
    46         } catch (Throwable var3) {
    47             throw new UndeclaredThrowableException(var3);
    48         }
    49     }
    50 
    51     static {
    52         try {
    53             m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
    54             m3 = Class.forName("com.yishi.invoice.proxy.ProxyDemo").getMethod("sayHello", Class.forName("java.lang.String"));
    55             m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    56             m2 = Class.forName("java.lang.Object").getMethod("toString");
    57         } catch (NoSuchMethodException var2) {
    58             throw new NoSuchMethodError(var2.getMessage());
    59         } catch (ClassNotFoundException var3) {
    60             throw new NoClassDefFoundError(var3.getMessage());
    61         }
    62     }
    63 }
    View Code

    我们可以看到,调用代理类的sayHello方法,实际上调用的是InvocationHandler.invoke方法。

    需要注意:代理类中通过反射获取到代理接口的Method对象,并传入到InvocationHandler.invoke方法中。也就是说invoke方法中需要通过Method发射来调用被代理类的方法。

    下面看一个复杂的例子:

    包结构如下:

      1 import java.io.File;
      2 import java.io.IOException;
      3 import java.lang.reflect.Modifier;
      4 import java.net.URL;
      5 import java.util.*;
      6 
      7 public class DaoContainer {
      8 
      9     /**
     10      * 存储代理类
     11      * key为接口名称
     12      * value为代理类
     13      */
     14     private static Map<String, Object> daoMap = new HashMap<String, Object>();
     15 
     16     /**
     17      * 接口class对象
     18      */
     19     private static List<Class<?>> interfaces = new ArrayList<Class<?>>();
     20 
     21     /**
     22      * 存储实例对象
     23      * key 为 接口class对象
     24      * value为 实例对象
     25      */
     26     private static Map<Class<?>, List<Object>> maps = new HashMap<Class<?>, List<Object>>();
     27 
     28     /**
     29      * 解析包下的接口
     30      * @param pkgBase
     31      */
     32     public void parseInterface(String pkgBase) {
     33         //解析包路径
     34         //此处简单处理,支持单路劲
     35         String[] p = splitPkg(pkgBase);
     36         for (String str:p) {
     37             Enumeration<URL> resources = null;
     38             try {
     39                 resources = DaoContainer.class.getClassLoader().getResources(str);
     40                 while (resources.hasMoreElements()){
     41                     URL url = resources.nextElement();
     42                     File rootFile = new File(url.getFile());
     43                     if (rootFile.exists()) {
     44                         if (rootFile.isDirectory()) {
     45                             File[] subFiles = rootFile.listFiles();
     46                             for (File f : subFiles) {
     47                                 String interfaceName = pkgBase+ "." + f.getName().substring(0,f.getName().lastIndexOf("."));
     48                                 System.out.println(interfaceName);
     49                                 try {
     50                                     /**
     51                                      * 可以通过解析class文件,获取类属性,减少不必要的类加载。然后通过判断哪些类是需要加载的
     52                                      * 此处简单处理
     53                                      */
     54                                     Class<?> cl = Class.forName(interfaceName,false, DaoContainer.class.getClassLoader());
     55                                     if (Modifier.isInterface(cl.getModifiers())) {
     56                                         interfaces.add(cl);
     57                                     } else if (!Modifier.isInterface(cl.getModifiers()) && !Modifier.isAbstract(cl.getModifiers()) && Modifier.isPublic(cl.getModifiers())) {
     58                                         //如果不是接口,分析父类接口
     59                                         Class<?>[] inter = cl.getInterfaces();
     60                                         for (Class i : inter ) {
     61                                             if (Modifier.isPublic(i.getModifiers())) {
     62                                                 List<Object> objs = maps.get(i);
     63                                                 if (objs == null) {
     64                                                     objs = new ArrayList<Object>();
     65                                                     maps.put(i, objs);
     66                                                 }
     67                                                 objs.add(cl.newInstance());
     68                                             }
     69                                         }
     70                                     }
     71                                 } catch (ClassNotFoundException e) {
     72                                     e.printStackTrace();
     73                                 } catch (IllegalAccessException e) {
     74                                     e.printStackTrace();
     75                                 } catch (InstantiationException e) {
     76                                     e.printStackTrace();
     77                                 }
     78                             }
     79                         }
     80                     }
     81                 }
     82             } catch (IOException e) {
     83                 e.printStackTrace();
     84             }
     85         }
     86     }
     87 
     88     /**
     89      * 以 ,或者; 作为分隔符
     90      * @param pkgBase
     91      * @return
     92      */
     93     private String[] splitPkg(String pkgBase) {
     94         //简单处理
     95         pkgBase = pkgBase.replace(".","/");
     96         return new String[]{pkgBase};
     97     }
     98 
     99     /**
    100      * 创建代理类
    101      */
    102     private void createProxy() {
    103         for (Class<?> cl : interfaces) {
    104             //创建代理类
    105             Object proxy = DaoProxyFactory.proxyInstance(cl);
    106 
    107             daoMap.put(cl.getName(),proxy);
    108         }
    109 
    110         Set<Map.Entry<Class<?>, List<Object>>> keys = maps.entrySet();
    111         Iterator<Map.Entry<Class<?>, List<Object>>> iterator = keys.iterator();
    112 
    113         while (iterator.hasNext()) {
    114             Map.Entry<Class<?>, List<Object>> entry = iterator.next();
    115 
    116             Class<?> key = entry.getKey();
    117 
    118             List<Object> objs = entry.getValue();
    119 
    120             for (int i = 0; null != objs && i < objs.size(); i++) {
    121                 Object o = objs.get(i);
    122                 Object obj = DaoProxyFactory.proxyInstance(key, o);
    123                 daoMap.put(o.getClass().getName(), obj);
    124             }
    125         }
    126 
    127     }
    128 
    129     /**
    130      * 搜索包下接口
    131      * @param pkgBase
    132      */
    133     public void searchPkgBean(String pkgBase) {
    134         parseInterface(pkgBase);
    135         createProxy();
    136     }
    137 
    138     /**
    139      * 通过key获取代理类
    140      * @return
    141      */
    142     public Object getProxyObjectByKey(String key) {
    143         return daoMap.get(key);
    144     }
    145 
    146 }
    View Code
     1 import java.lang.reflect.InvocationHandler;
     2 import java.lang.reflect.Method;
     3 import java.lang.reflect.Proxy;
     4 
     5 public class DaoProxyFactory {
     6 
     7     private static CommonSolvDemo commonSolvDemo = new CommonSolvDemo();
     8 
     9     public DaoProxyFactory() {
    10     }
    11 
    12     public DaoProxyFactory(CommonSolvDemo com) {
    13         this.commonSolvDemo = com;
    14     }
    15 
    16     //代理接口
    17     public static Object proxyInstance(Class<?> inter) {
    18         Object obj = null;
    19         try {
    20             obj = Proxy.newProxyInstance(DaoProxyFactory.class.getClassLoader(), new Class[]{inter}, new DaoInvocationHandler());
    21         } catch (Exception e) {
    22             e.printStackTrace();
    23         }
    24         return obj;
    25     }
    26 
    27     //代理实现共同接口的实例
    28     public static Object proxyInstance(Class<?> inter, Object obj) {
    29         Object res = null;
    30         try {
    31             res =  Proxy.newProxyInstance(DaoProxyFactory.class.getClassLoader(), new Class[]{inter}, new DaoInvocationHandler1(obj));
    32         } catch (Exception e) {
    33             e.printStackTrace();
    34         }
    35         return res;
    36     }
    37 
    38     private static class DaoInvocationHandler implements InvocationHandler {
    39 
    40         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    41 
    42             commonSolvDemo.commonSolvStart();
    43 
    44             commonSolvDemo.commonSolvEnd();
    45 
    46             return null;
    47         }
    48     }
    49 
    50     private static class DaoInvocationHandler1 implements InvocationHandler {
    51 
    52         private Object target;
    53 
    54         public DaoInvocationHandler1(Object target) {
    55             this.target = target;
    56         }
    57 
    58         public DaoInvocationHandler1() {
    59         }
    60 
    61         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    62             commonSolvDemo.commonSolvStart();
    63             method.invoke(target,args);
    64             commonSolvDemo.commonSolvEnd();
    65             return null;
    66         }
    67     }
    68 }
    View Code
     1 public class CommonSolvDemo {
     2 
     3 
     4     public void commonSolvStart() {
     5         System.out.println("公共处理开始");
     6     }
     7 
     8 
     9     public void commonSolvEnd() {
    10         System.out.println("公共处理结束");
    11     }
    12 
    13 }
    View Code
     1 import java.lang.reflect.Proxy;
     2 
     3 public class ProxyTest {
     4 
     5     public static void main(String[] args) {
     6 //        test1();
     7         test2();
     8     }
     9 
    10     public static void test2() {
    11         DaoContainer container = new DaoContainer();
    12         container.searchPkgBean("com.yishi.invoice.proxy");
    13 //        //这种情况类似mybatis底层的代理生成方式
    14         ProxyDemo p = (ProxyDemo) container.getProxyObjectByKey("com.yishi.invoice.proxy.ProxyDemo");
    15         p.sayHello("haha");
    16 
    17         //代理实现统一接口的实例,类似spring底层代理的一种
    18         SqlSession sql = (SqlSession) container.getProxyObjectByKey("com.yishi.invoice.proxy.SqlSessionTemplete");
    19         sql.selectLIst("312","21");
    20     }
    21 
    22     public static void test1() {
    23         System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
    24 
    25         Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), new Class[]{ProxyDemo.class}, new InvocationHandlerDemo());
    26     }
    27 
    28 }

    总结下,动态代理底层是字节码层面的技术,利用反射获取到需要代理接口方法,然后根据class字节码标准,生成二进制数据。

    回到最开始的问题,为什么要使用代理?当需要对一些类,接口提供统一功能的时候,可以考虑使用代理模式。静态代理采用硬编码的方式实现,其思想本质上是一样的。

    spring采用两种代理,cglib与jdk代理。如果是接口的话,采用jdk方式,如果是非接口,采用cglib方式。

    后面将分析cglib代理原理,敬请期待。

  • 相关阅读:
    easy_install
    do some projects in macine learning using python
    awesome-scala
    val, lazy, def
    Scala命令设置JVM参数的规则
    CMMI-4中19个PA的大致描述
    项目管理中的十一个原则
    php代码在服务器中查看接值
    PHP进程锁
    解决百度网盘(百度云)分享链接不存在失效、分享的文件已经被取消的问题
  • 原文地址:https://www.cnblogs.com/hf-china/p/9451003.html
Copyright © 2020-2023  润新知