我们知道AOP的生成是代理+反射,其内部是首先对AOP要处理的类进行继承,然后在要执行切面的地方的方法前或后加上其他的代码逻辑,此处就必须要进行动态的class文件生成,因为Java中万物皆对象,所以它只会处理对象,那么必然要将其生成一个新的对象。在Java中要动态生成一个class文件必须要通过classLoader的defineClass进行,此方法中需要一个bytes数组和其他的一些参数比如还需要ProtectionDomain,那么一般会传递null那么会使用默认的ProtectionDomain(这和C++默认参数不一样,C++默认参数是基本参数类型,这是对象)。
protected final Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain) throws ClassFormatError { check();//检查ClassLoader是否被初始化,未初始化抛出未初始化异常 protectionDomain = preDefineClass(name, protectionDomain);//决定保护域(<span style="font-family: Arial, Helvetica, sans-serif;">protectionDomain 权限控制,在此方法中生成默认值),check文件名,不能以java.</span> 开头,因为java.*的文件都是引导文件,我们不可以干预 Class c = null; String source = defineClassSourceLocation(protectionDomain);//获取和CodeSource相关的位置,url是不可变的因此一般返回的是codeSource的位置 try { c = defineClass1(name, b, off, len, protectionDomain, source);//调用native方法生成class文件,native方法的具体实现与JVM相关 } catch (ClassFormatError cfe) { c = defineTransformedClass(name, b, off, len, protectionDomain, cfe, source); } postDefineClass(c, protectionDomain);//给类设置签名,调用本地方法 return c; }通过上述代码可以看到我们要想实现一个动态的类,需要去生成的主要是关于一个类的bytes数组,那么此数组来自于String,那么我们需要关注的是java的class文件格式,然后按照文件格式生成即可,目前比较成熟的jar是asm.java,有兴趣的可以读一下。 AMS5.0源码和Demo
Java字节码文件格式(学过IOSRuntime的同学有没有觉得很相似?反正我觉得很像)
ClassFile { u4 magic; u2 minor_version; u2 major_version;//和mimor构成VMajor.Minor,具体采用哪种由Java的JVM版本决定 u2 constant_pool_count;//常量池的内容是pool_count+1,大于0或小于pool_cout是有效的 cp_info constant_pool[constant_pool_count-1];//存储字符串常量,包括类、接口的名字,属性域的名字以及其他与类结构和子类结构相关的常量 u2 access_flags;//类或接口的访问权限等属性 u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count];//类和接口必须要在常量池有描述,并且不可超过255(因为它用一个字节表示了1-255的个数,内部实现了utf8的转码) u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count];//属性和方法的描述符请参考表,方法是(参数)返回值 u2 attributes_count;//各种信息的文件格式 attribute_info attributes[attributes_count]; }其中u2 u4分别指的是u1, u2, and u4 represent an unsigned one-, two-, or four-byte,是字节。
属性表(Flag之间是有关联的,关联的形式即类或接口的性质)
属性表的标识符
由于常量池里面保管各种信息的描述符,因此对于属性(继承的不放在本身里面)和方法以及接口方法的存放是tag+类索引(常量池表中)+nameAndType的索引(常量池表中)
其他的部分是对属性等方法的定义,有兴趣的可以看原文
关于ASM中,博主看了其中关于HelloWorld的demo,里面需要说明的是ASM通过对字节码文件进行描述来实现动态class文件生成,他的生成是生成byteArray,那么里面关于byteArray是根据class文件的文件结构定义的
PS:其中MethodWriter与ClassWriter是相互关联的对象,ClassWriter里面的firstMethod和lastMethod是method方法的管理用的是链表,他们的初始化采用的是delay方式,此种实现是在MethodWriter中进行的,通过判断是否为空进行管理。他们可以直接对firstMethod和lastMethod进行赋值而无需考虑中间是否还有其他method原因在于所有的method方法的描述会放入常量池,具体相关还请大家看上面的原文链接。英文原文内容较多,博主只是对其中的开头部分进行了说明,有想详细了解的建议读原文。