ASM是一款基于java字节码层面的代码分析和修改工具。
ASM的目标是生成,转换和分析已编译的java class文件,可使用ASM工具读/写/转换JVM指令集。
ASM工具提供两种方式来产生和转换已编译的class文件,它们分别是基于事件和基于对象的表示模型。
其中,基于事件的表示模型使用一个有序的事件序列表示一个class文件,class文件中的每一个元素使用一个事件来表示,比如class的头部,变量,方法声明,JVM指令都有相对应的事件表示
ASM使用自带的事件解析器能将每一个class文件解析成一个事件序列。而基于对象的表示模型则使用对象树结构来解析每一个class文件。
起点和终点分别是ClassReader(Class文件解析器)、ClassWriter(class事件序列转换到class字节码),中间的过程由若干个自定义的事件过滤器组成
JAVA动态性的两种常见实现方式
运行时操作字节码可以让我们实现如下功能
常见的字节码操作类库
操作java字节码的工具有两个比较流行,一个是ASM,一个是Javassit 。
ASM :直接操作字节码指令,执行效率高,要是使用者掌握Java字节码文件格式及指令,对使用者的要求比较高。
Javassit :提供了更高级的API,执行效率相对较差,对使用者要求较低.
ASM工具生产和转换class文件内容的所有工作都是基于ClassVisitor这个抽象类进行的。
ClassVisitor抽象类中的每一个方法会对应到class文件的相应区域,每个方法负责处理class文件相应区域的字节码内容
我们可以把继承ClassVisitor的抽象类的自定义类看作负责class文件各个区域内容的修改和生成
1、遍历Class字节码类信息
//官网案例
public class ClassPrinter extends ClassVisitor {
public ClassPrinter() {
super(ASM4);
}
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
System.out.println(name + " extends " + superName + " {");
}
public void visitSource(String source, String debug) {}
public void visitOuterClass(String owner, String name, String desc) {}
public AnnotationVisitor visitAnnotation(String desc,boolean visible) {
return null;
}
public void visitAttribute(Attribute attr) {
}
public void visitInnerClass(String name, String outerName,
String innerName, int access) {
}
public FieldVisitor visitField(int access, String name, String desc,String signature, Object value) {
System.out.println(" " + desc + " " + name);
return null;
}
public MethodVisitor visitMethod(int access, String name,
String desc, String signature, String[] exceptions) {
System.out.println(" " + name + desc);
return null;
}
public void visitEnd() {
System.out.println("}");
}
}
ClassPrinter cp = new ClassPrinter();
ClassReader cr = new ClassReader("java.lang.Runnable");
cr.accept(cp, 0);
result:
java/lang/Runnable extends java/lang/Object {
run()V
}
2、生成自定义类对应的class字节码内容
package pkg;
public interface Comparable extends Mesurable {
int LESS = -1;
int EQUAL = 0;
int GREATER = 1;
int compareTo(Object o);
}
test.java内容:
ClassWriter cw = new ClassWriter(0);
cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, "pkg/Comparable", null, "java/lang/Object", new String[] { "pkg/Mesurable" });
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "LESS", "I", null, new Integer(-1)).visitEnd();
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "EQUAL", "I", null, new Integer(0)).visitEnd();
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "GREATER", "I", null, new Integer(1)).visitEnd(); cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "compareTo", "(Ljava/lang/Object;)I", null, null).visitEnd();
cw.visitEnd();
byte[] b = cw.toByteArray();
3、动态加载2生产出的class字节码并实例化该类,使用继承自ClassLoader的类,并重写defineClass方法
//第一种方法:通过ClassLoader的defineClass动态加载字节码
class MyClassLoader extends ClassLoader {
public Class defineClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}
//直接调用方法
Class c = myClassLoader.defineClass("pkg.Comparable", b);
使用继承自ClassLoader的类,并重写findClass内部类
class StubClassLoader extends ClassLoader {
@Override
protected Class findClass(String name) throws ClassNotFoundException {
if (name.endsWith("_Stub")) {
ClassWriter cw = new ClassWriter(0);
...
byte[] b = cw.toByteArray();
return defineClass(name, b, 0, b.length);
}
return super.findClass(name);
}
}
4、ClassReader生产者生产的class字节码bytes可以被ClassWriter直接消费
byte[] b1 = ...;
ClassWriter cw = new ClassWriter(0);
ClassReader cr = new ClassReader(b1);
cr.accept(cw, 0);
byte[] b2 = cw.toByteArray(); //这里的b2与b1表示同一个类且值一样
5、ClassReader生产者生产的字节码bytes可以先被继承自ClassVisitor的自定义类过滤,最后被ClassWrite消费
byte[] b1 = ...;
ClassWriter cw = new ClassWriter(0);
// cv forwards all events to cw
ClassVisitor cv = new ChangeVersionAdapter (cw) { };
ClassReader cr = new ClassReader(b1);
cr.accept(cv, 0);
byte[] b2 = cw.toByteArray(); //这里的b2与b1表示同一个类但值不一样
整个字节码转换过程的时序图如下
public class ChangeVersionAdapter extends ClassVisitor { //只是继承了ClassVisitor
public ChangeVersionAdapter(ClassVisitor cv) {
super(ASM4, cv);
}
@Override //重写visit()
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
//过滤出类中的方法并指定方法的版本号(v1.5)
cv.visit(V1_5, access, name, signature, superName, interfaces);
}
}
java字节码以二进制的形式存储在.class文件中,每一个.class文件包含一个java类或者接口。
javassist就是一个用来处理java字节码的类库。可以在一个编译好的类中添加方法(也就是我们说的增强),或者修改已有方法。
而且也可以生成一个新的类对象,手动方式。
在javassist中,类Javassist.CtClass表示class文件。
ClassPool是CtClass对象的容器。ClassPool会在内存中维护创建过的CtClass对象,该对象过多时,占有内存也就会多,可以调用CtClass的detach()方法释放内存
CtClass相关方法:
.class
文件;创建新的方法可以使用CtMethod:
由于JVM通过字节码的二进制信息加载类的,那么,如果我们在运行期系统中,遵循Java编译系统组织.class文件的格式和结构,生成相应的二进制数据,然后再把这个二进制数据加载转换成对应的类,这样,就完成了在代码中,动态创建一个类的能力了
代理接口
public interface PrintService {
void print(String message);
}
代理接口实现类
public class PrintServiceImpl implements PrintService {
@Override
public void print(String message) {
System.out.println("message : "+message);
}
}
编写Proxy的调用处理程序 InvocationHandler
public class MyInvocationHandler implements InvocationHandler {
private Object proxyTarget;//要代理的真实对象
public MyInvocationHandler(Object proxyTarget) {
this.proxyTarget = proxyTarget;
}
//为PrintService 生成代理对象
public Object getProxy(){
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), proxyTarget.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before proxy invoke ================"+method.getName());
Object invoke = method.invoke(proxyTarget, args);
System.out.println("after proxy invoke ================="+method.getName());
return invoke;
}
}
Test代码
public static void main(String[] args) {
PrintServiceImpl printService = new PrintServiceImpl();
MyInvocationHandler invokeHandler = new MyInvocationHandler(printService);
PrintService proxy =(PrintService) invokeHandler.getProxy();
System.out.println("proxy class : "+proxy.getClass().getName());
proxy.print("my proxy test");
}
/******************************************************************************************/
/**
proxy class : com.sun.proxy.$Proxy0
before proxy invoke ================print
message : my proxy test
after proxy invoke =================print
*/
Proxy类以及接口 InvocationHandler是实现JDK动态代理的核心。看下newProxyInstance
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader, //类加载器loader
Class>[] interfaces, //代理类所实现的接口
InvocationHandler h)//当前动态代理对象调用的方法关联InvokerHandler对象
throws IllegalArgumentException
{
.........
/*
*省略其他代码,关注下getProxyClass0这个方法
*/
Class> cl = getProxyClass0(loader, intfs);
}
//getProxyClass0这个方法获取代理类的字节码
private static Class> getProxyClass0(ClassLoader loader,
Class>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
//这里缓存的对象proxyClassCache采用weakReference实现,GC回收会被回收,属于堆外内存
return proxyClassCache.get(loader, interfaces);
//看下get方法
}
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
ConcurrentMap
public Class> apply(ClassLoader loader, Class>[] interfaces) {
Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
apply首先加载要实现的接口,然后做了一系列配置,接着通过ProxyGenerator.generateProxyClass来生成Class字节码文件,再调用defineClass0(native方法)对其进行加载。
public static byte[] generateProxyClass(final String var0, Class>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
总结
1、缓存的对象proxyClassCache采用weakReference实现,GC回收会被回收
2、将InvocationHanlder h 作为参数,创建实例对象,所有constructorParams的类型肯定是InvocationHandler.class。
3、最后会调用native关键字的defineClass0这个方法
4、至此整代理类的生成过程已经全部完成,可以看出 JDK生成代理类时是基于接口生成代理类把目标类 当做属性放在代理类里面
代理对象(不需要接口)
public class PrintServiceImpl {
public void print(String message) {
System.out.println("message : "+message);
}
}
编写MethodInterceptor
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, //所生成的代理对象
Method method, //要被拦截的方法
Object[] objects, //方法参数
MethodProxy methodProxy //要触发的父类的方法对象(即父类方法代理)
) throws Throwable {
System.out.println("proxy by cglib ....."+method.getName());
//此处一定要是methodProxy来调用,不然会死循环
Object res = methodProxy.invokeSuper(o, objects);
return res;
}
}
测试
public static void main(String[] args) {
//将生成的代理class写入到文件夹下
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\Cglib");
Enhancer enhancer = new Enhancer();
//设置代理类的对象
enhancer.setSuperclass(PrintServiceImpl.class);
//设置代理
enhancer.setCallback(new MyMethodInterceptor());
PrintServiceImpl printServiceProxy = (PrintServiceImpl)enhancer.create();
System.out.println(printServiceProxy.getClass().getName());
printServiceProxy.print("print aaa ");
}
/*************************************************************************************/
/**
leonardo.ezio.boot.demo.proxy.PrintServiceImpl$$EnhancerByCGLIB$$f8e5363
proxy by cglib .....print
message : print aaa
*/
CGLIB原理
create()
public Object create() {
this.classOnly = false;
this.argumentTypes = null;
return this.createHelper();
}
private Object createHelper() {
this.preValidate();
//private static final Enhancer.EnhancerKey KEY_FACTORY 创建了一个EnhancerKey对象
Object key = KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter), this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID);
this.currentKey = key;
//调用父类AbstractClassGenerator方法
Object result = super.create(key);
return result;
}
protected Object create(Object key) {
try {
ClassLoader loader = this.getClassLoader();
Map cache = CACHE;
AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
if (data == null) {
Class var5 = AbstractClassGenerator.class;
synchronized(AbstractClassGenerator.class) {
cache = CACHE;
data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
if (data == null) {
Map newCache = new WeakHashMap(cache);
data = new AbstractClassGenerator.ClassLoaderData(loader);
newCache.put(loader, data);
CACHE = newCache;
}
}
}
this.key = key;
Object obj = data.get(this, this.getUseCache());
return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
} catch (Error | RuntimeException var9) {
throw var9;
} catch (Exception var10) {
throw new CodeGenerationException(var10);
}
}
newInstance
public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {
this.setThreadCallbacks(callbacks);
Object var4;
try {
if (this.primaryConstructorArgTypes == argumentTypes || Arrays.equals(this.primaryConstructorArgTypes, argumentTypes)) {
var4 = ReflectUtils.newInstance(this.primaryConstructor, arguments);
return var4;
}
//最终通过newInstance0反射生成代理对象
var4 = ReflectUtils.newInstance(this.generatedClass, argumentTypes, arguments);
} finally {
this.setThreadCallbacks((Callback[])null);
}
return var4;
}
通过反射来生成代理对象,阅读源码的过程中我们可以发现cglib生成动态代理的过程中使用了WeakHashMap做为缓存,与jdk中的ProxyClassCache有相似之处,都是采用弱引用实现的
JDK基于接口生成代理类 把目标类当做属性放在代理类里面
CGLIB基于class生成实现类 继承目标对象加强
CGLIB通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,所以final方法无法处理
实现CGLIB动态代理必须实现MethodInterceptor
javassist部分引用,来自https://blog.csdn.net/ShuSheng0007/article/details/81269295