深入了解Java ClassLoader、Bytecode 、ASM、cglib

2024-01-14 09:18

本文主要是介绍深入了解Java ClassLoader、Bytecode 、ASM、cglib,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、Java ClassLoader

1,什么是ClassLoader
与 C 或 C++ 编写的程序不同,Java 程序并不是一个可执行文件,而是由许多独立的类文件组成,每一个文件对应于一个 Java 类。
此外,这些类文件并非立即全部都装入内存,而是根据程序需要装入内存。ClassLoader 是 JVM 中将类装入内存的那部分。
而且,Java ClassLoader 就是用 Java 语言编写的。这意味着创建您自己的 ClassLoader 非常容易,不必了解 JVM 的微小细节。

2,一些重要的方法
A)loadClass
ClassLoader.loadClass() 是ClassLoader的入口点。该方法的定义为:Class loadClass( String name, boolean resolve );
name:JVM 需要的类的名称,如 Foo 或 java.lang.Object。
resolve:参数告诉方法是否需要解析类。

B)defineClass
defineClass方法是ClassLoader的主要诀窍。该方法接受由原始字节组成的数组并把它转换成Class对象。

C)findSystemClass
findSystemClass方法从本地文件系统中寻找类文件,如果存在,就使用defineClass将原始字节转换成Class对象,以将该文件转换成类。

D)resolveClass
可以不完全地(不带解析)装入类,也可以完全地(带解析)装入类。当编写我们自己的loadClass时可以调用resolveClass,这取决于loadClass的resolve参数的值。

E)findLoadedClass
findLoadedClass充当一个缓存:当请求loadClass装入类时,它调用该方法来查看ClassLoader是否已装入这个类,这样可以避免重新装入已存在类所造成的麻烦。

3,Java2中ClassLoader的变动
1)loadClass的缺省实现
在Java2中loadClass的实现嵌入了大多数查找类的一般方法,并使您通过覆盖findClass方法来定制它,在适当的时候findClass会调用loadClass。
这种方式的好处是可能不一定要覆盖loadClass,只要覆盖findClass就行了,这减少了工作量。

2)新方法:findClass
loadClass的缺省实现调用这个新方法。

3)新方法:getSystemClassLoader
如果覆盖findClass或loadClass,getSystemClassLoader让我们以实际ClassLoader对象来访问系统ClassLoader,而不是固定的从findSystemClass 调用它。

4)新方法:getParent
为了将类请求委托给父ClassLoader,这个新方法允许ClassLoader获取它的父ClassLoader

4,定制ClassLoader
其实我们或多或少都使用过定制的ClassLoader,因为Applet查看器中就包含一个定制的ClassLoader
它不在本地文件系统中寻找类,而是访问远程服务器上的 Web 站点,经过 HTTP 装入原始的字节码文件,并把它们转换成JVM 内的类。
Applet查看器中的ClassLoader还可以做其它事情:它们支持安全性以及使不同的Applet在不同的页面上运行而互不干扰。
我们将写一个自己的ClassLoader实现示例,它将实现如下步骤,这也是ClassLoader的工作原理:
# 调用 findLoadedClass 来查看是否存在已装入的类。
# 如果没有,那么采用那种特殊的神奇方式来获取原始字节。
# 如果已有原始字节,调用defineClass将它们转换成Class对象。
# 如果没有原始字节,然后调用findSystemClass查看是否从本地文件系统获取类。
# 如果resolve参数是true,那么调用resolveClass解析Class对象。
# 如果还没有类,返回ClassNotFoundException。
# 否则,将类返回给调用程序。
话不多说,看看代码先:
FileClassLoader.java:

Java代码
  1. import java.io.ByteArrayOutputStream;  
  2. import java.io.File;  
  3. import java.io.FileInputStream;  
  4. import java.io.IOException;  
  5.   
  6. public class FileClassLoader extends ClassLoader {  
  7.   public Class findClass(String name) {  
  8.     byte[] data = loadClassData(name);  
  9.     return defineClass(name, data, 0, data.length);  
  10.   }  
  11.     
  12.   private byte[] loadClassData(String name) {  
  13.     FileInputStream fis = null;  
  14.     byte[] data = null;  
  15.     try {  
  16.       fis = new FileInputStream(new File("D://project//test//" + name + ".class"));  
  17.       ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  18.       int ch = 0;  
  19.       while ((ch = fis.read()) != -1) {  
  20.         baos.write(ch);  
  21.       }  
  22.       data = baos.toByteArray();  
  23.     } catch (IOException e) {  
  24.       e.printStackTrace();  
  25.     }  
  26.     return data;  
  27.   }  
  28. }  
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class FileClassLoader extends ClassLoader {
public Class findClass(String name) {
byte[] data = loadClassData(name);
return defineClass(name, data, 0, data.length);
}
private byte[] loadClassData(String name) {
FileInputStream fis = null;
byte[] data = null;
try {
fis = new FileInputStream(new File("D://project//test//" + name + ".class"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int ch = 0;
while ((ch = fis.read()) != -1) {
baos.write(ch);
}
data = baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
}


MyApp.java:

Java代码
  1. public class MyApp {  
  2.   public static void main(String[] args) throws Exception {  
  3.     FileClassLoader loader = new FileClassLoader();  
  4.     Class objClass = loader.findClass("MyApp");  
  5.     Object obj = objClass.newInstance();  
  6.     System.out.println(objClass.getName());  
  7.     System.out.println(objClass.getClassLoader());  
  8.     System.out.println(obj);  
  9.   }  
  10. }  
public class MyApp {
public static void main(String[] args) throws Exception {
FileClassLoader loader = new FileClassLoader();
Class objClass = loader.findClass("MyApp");
Object obj = objClass.newInstance();
System.out.println(objClass.getName());
System.out.println(objClass.getClassLoader());
System.out.println(obj);
}
}


编译并运行MyApp类,结果为:

Java代码
  1. MyApp  
  2. FileClassLoader@757aef  
  3. MyApp@9cab16 

二、Bytecode

1,什么是Bytecode
C/C++编译器把源代码编译成汇编代码,Java编译器把Java源代码编译成字节码bytecode。
Java跨平台其实就是基于相同的bytecode规范做不同平台的虚拟机,我们的Java程序编译成bytecode后就可以在不同平台跑了。
.net框架有IL(intermediate language),汇编是C/C++程序的中间表达方式,而bytecode可以说是Java平台的中间语言。
了解Java字节码知识对debugging、performance tuning以及做一些高级语言扩展或框架很有帮助。

2,使用javap生成Bytecode
JDK自带的javap.exe文件可以反汇编Bytecode,让我们看个例子:
Test.java:

Java代码
  1. public class Test {  
  2.   public static void main(String[] args) {  
  3.     int i = 10000;  
  4.     System.out.println("Hello Bytecode! Number = " + i);  
  5.   }  
  6. }  
public class Test {
public static void main(String[] args) {
int i = 10000;
System.out.println("Hello Bytecode! Number = " + i);
}
}


编译后的Test.class:

Java代码
  1. 漱壕   1 +  
  2.          
  3.      
  4.     
  5.     
  6.     
  7.      <init> ()V Code LineNumberTable main ([Ljava/lang/String;)V   
  8. SourceFile   Test.java      
  9.     ! " java/lang/StringBuilder Hello Bytecode! Number =   # $  # %  & ' (  ) * Test java/lang/Object java/lang/System out Ljava/io/PrintStream; append -(Ljava/lang/String;)Ljava/lang/StringBuilder; (I)Ljava/lang/StringBuilder; toString ()Ljava/lang/String; java/io/PrintStream println (Ljava/lang/String;)V !   
  10.             
  11.           *                      >     '<  Y                              
漱壕   1 +
<init> ()V Code LineNumberTable main ([Ljava/lang/String;)V 
SourceFile   Test.java  
! " java/lang/StringBuilder Hello Bytecode! Number =  # $ # % & ' ( ) * Test java/lang/Object java/lang/System out Ljava/io/PrintStream; append -(Ljava/lang/String;)Ljava/lang/StringBuilder; (I)Ljava/lang/StringBuilder; toString ()Ljava/lang/String; java/io/PrintStream println (Ljava/lang/String;)V ! 
*                      >     '<  Y                            


使用javap -c Test > Test.bytecode生成的Test.bytecode:

Java代码
  1. Compiled from "Test.java"  
  2. public class Test extends java.lang.Object{  
  3. public Test();  
  4.   Code:  
  5.    0:  aload_0  
  6.    1:  invokespecial  #1//Method java/lang/Object."<init>":()V  
  7.    4:  return  
  8.   
  9. public static void main(java.lang.String[]);  
  10.   Code:  
  11.    0:  sipush  10000  
  12.    3:  istore_1  
  13.    4:  getstatic  #2//Field java/lang/System.out:Ljava/io/PrintStream;  
  14.    7:  new  #3//class java/lang/StringBuilder  
  15.    10:  dup  
  16.    11:  invokespecial  #4//Method java/lang/StringBuilder."<init>":()V  
  17.    14:  ldc  #5//String Hello Bytecode! Number =   
  18.    16:  invokevirtual  #6//Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;  
  19.    19:  iload_1  
  20.    20:  invokevirtual  #7//Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;  
  21.    23:  invokevirtual  #8//Method java/lang/StringBuilder.toString:()Ljava/lang/String;  
  22.    26:  invokevirtual  #9//Method java/io/PrintStream.println:(Ljava/lang/String;)V  
  23.    29:  return  
  24.   
  25. }  
Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
Code:
0:  aload_0
1:  invokespecial  #1; //Method java/lang/Object."<init>":()V
4:  return
public static void main(java.lang.String[]);
Code:
0:  sipush  10000
3:  istore_1
4:  getstatic  #2; //Field java/lang/System.out:Ljava/io/PrintStream;
7:  new  #3; //class java/lang/StringBuilder
10:  dup
11:  invokespecial  #4; //Method java/lang/StringBuilder."<init>":()V
14:  ldc  #5; //String Hello Bytecode! Number = 
16:  invokevirtual  #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19:  iload_1
20:  invokevirtual  #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
23:  invokevirtual  #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
26:  invokevirtual  #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
29:  return
}


JVM就是一个基于stack的机器,每个thread拥有一个存储着一些frames的JVM stack,每次调用一个方法时生成一个frame。
一个frame包括一个local variables数组(本地变量表),一个Operand LIFO stack和运行时常量池的一个引用。

我们来简单分析一下生成的字节码指令:
aload和iload指令的“a”前缀和“i”分别表示对象引用和int类型,其他还有“b”表示byte,“c”表示char,“d”表示double等等
我们这里的aload_0表示将把local variable table中index 0的值push到Operand stack,iload_1类似
invokespecial表示初始化对象,return表示返回
sipush表示把10000这个int值push到Operand stack
getstatic表示取静态域
invokevirtual表示调用一些实例方法
这些指令又称为opcode,Java一直以来只有约202個Opcode,具体请参考Java Bytecode规范。

我们看到Test.class文件不全是二进制的指令,有些是我们可以识别的字符,这是因为有些包名、类名和常量字符串没有编译成二进制Bytecode指令。

3,体验字节码增强的魔力
我们J2EE常用的Hibernate、Spring都用到了动态字节码修改来改变类的行为。
让我们通过看看ASM的org.objectweb.asm.MethodWriter类的部分方法来理解ASM是如何修改字节码的:

Java代码
  1. class MethodWriter implements MethodVisitor {  
  2.   
  3.     private ByteVector code = new ByteVector();  
  4.   
  5.     public void visitIntInsn(final int opcode, final int operand) {  
  6.         // Label currentBlock = this.currentBlock;  
  7.         if (currentBlock != null) {  
  8.             if (compute == FRAMES) {  
  9.                 currentBlock.frame.execute(opcode, operand, nullnull);  
  10.             } else if (opcode != Opcodes.NEWARRAY) {  
  11.                 // updates current and max stack sizes only for NEWARRAY  
  12.                 // (stack size variation = 0 for BIPUSH or SIPUSH)  
  13.                 int size = stackSize + 1;  
  14.                 if (size > maxStackSize) {  
  15.                     maxStackSize = size;  
  16.                 }  
  17.                 stackSize = size;  
  18.             }  
  19.         }  
  20.         // adds the instruction to the bytecode of the method  
  21.         if (opcode == Opcodes.SIPUSH) {  
  22.             code.put12(opcode, operand);  
  23.         } else { // BIPUSH or NEWARRAY  
  24.             code.put11(opcode, operand);  
  25.         }  
  26.     }  
  27.   
  28.     public void visitMethodInsn(  
  29.         final int opcode,  
  30.         final String owner,  
  31.         final String name,  
  32.         final String desc)  
  33.     {  
  34.         boolean itf = opcode == Opcodes.INVOKEINTERFACE;  
  35.         Item i = cw.newMethodItem(owner, name, desc, itf);  
  36.         int argSize = i.intVal;  
  37.         // Label currentBlock = this.currentBlock;  
  38.         if (currentBlock != null) {  
  39.             if (compute == FRAMES) {  
  40.                 currentBlock.frame.execute(opcode, 0, cw, i);  
  41.             } else {  
  42.                 /* 
  43.                  * computes the stack size variation. In order not to recompute 
  44.                  * several times this variation for the same Item, we use the 
  45.                  * intVal field of this item to store this variation, once it 
  46.                  * has been computed. More precisely this intVal field stores 
  47.                  * the sizes of the arguments and of the return value 
  48.                  * corresponding to desc. 
  49.                  */  
  50.                 if (argSize == 0) {  
  51.                     // the above sizes have not been computed yet,  
  52.                     // so we compute them...  
  53.                     argSize = getArgumentsAndReturnSizes(desc);  
  54.                     // ... and we save them in order  
  55.                     // not to recompute them in the future  
  56.                     i.intVal = argSize;  
  57.                 }  
  58.                 int size;  
  59.                 if (opcode == Opcodes.INVOKESTATIC) {  
  60.                     size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;  
  61.                 } else {  
  62.                     size = stackSize - (argSize >> 2) + (argSize & 0x03);  
  63.                 }  
  64.                 // updates current and max stack sizes  
  65.                 if (size > maxStackSize) {  
  66.                     maxStackSize = size;  
  67.                 }  
  68.                 stackSize = size;  
  69.             }  
  70.         }  
  71.         // adds the instruction to the bytecode of the method  
  72.         if (itf) {  
  73.             if (argSize == 0) {  
  74.                 argSize = getArgumentsAndReturnSizes(desc);  
  75.                 i.intVal = argSize;  
  76.             }  
  77.             code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 20);  
  78.         } else {  
  79.             code.put12(opcode, i.index);  
  80.         }  
  81.     }  
  82. }  
class MethodWriter implements MethodVisitor {
private ByteVector code = new ByteVector();
public void visitIntInsn(final int opcode, final int operand) {
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES) {
currentBlock.frame.execute(opcode, operand, null, null);
} else if (opcode != Opcodes.NEWARRAY) {
// updates current and max stack sizes only for NEWARRAY
// (stack size variation = 0 for BIPUSH or SIPUSH)
int size = stackSize + 1;
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
}
// adds the instruction to the bytecode of the method
if (opcode == Opcodes.SIPUSH) {
code.put12(opcode, operand);
} else { // BIPUSH or NEWARRAY
code.put11(opcode, operand);
}
}
public void visitMethodInsn(
final int opcode,
final String owner,
final String name,
final String desc)
{
boolean itf = opcode == Opcodes.INVOKEINTERFACE;
Item i = cw.newMethodItem(owner, name, desc, itf);
int argSize = i.intVal;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES) {
currentBlock.frame.execute(opcode, 0, cw, i);
} else {
/*
* computes the stack size variation. In order not to recompute
* several times this variation for the same Item, we use the
* intVal field of this item to store this variation, once it
* has been computed. More precisely this intVal field stores
* the sizes of the arguments and of the return value
* corresponding to desc.
*/
if (argSize == 0) {
// the above sizes have not been computed yet,
// so we compute them...
argSize = getArgumentsAndReturnSizes(desc);
// ... and we save them in order
// not to recompute them in the future
i.intVal = argSize;
}
int size;
if (opcode == Opcodes.INVOKESTATIC) {
size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;
} else {
size = stackSize - (argSize >> 2) + (argSize & 0x03);
}
// updates current and max stack sizes
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
}
// adds the instruction to the bytecode of the method
if (itf) {
if (argSize == 0) {
argSize = getArgumentsAndReturnSizes(desc);
i.intVal = argSize;
}
code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);
} else {
code.put12(opcode, i.index);
}
}
}


通过注释我们可以大概理解visitIntInsn和visitMethodInsn方法的意思。
比如visitIntInsn先计算stack的size,然后根据opcode来判断是SIPUSH指令还是BIPUSH or NEWARRAY指令,并相应的调用字节码修改相关的方法。

三、ASM
我们知道Java是静态语言,而python、ruby是动态语言,Java程序一旦写好很难在运行时更改类的行为,而python、ruby可以。
不过基于bytecode层面上我们可以做一些手脚,来使Java程序多一些灵活性和Magic,ASM就是这样一个应用广泛的开源库。

ASM is a Java bytecode manipulation framework. It can be used to dynamically generate stub classes or other proxy classes,
directly in binary form, or to dynamically modify classes at load time, i.e., just before they are loaded into the Java
Virtual Machine.

ASM完成了BCEL和SERP同样的功能,但ASM
只有30多k,而后两者分别是350k和150k。apache真是越来越过气了。

让我们来看一个ASM的简单例子Helloworld.java,它生成一个Example类和一个main方法,main方法打印"Hello world!"语句:

Java代码
  1. import java.io.FileOutputStream;  
  2. import java.io.PrintStream;  
  3.   
  4. import org.objectweb.asm.ClassWriter;  
  5. import org.objectweb.asm.MethodVisitor;  
  6. import org.objectweb.asm.Opcodes;  
  7. import org.objectweb.asm.Type;  
  8. import org.objectweb.asm.commons.GeneratorAdapter;  
  9. import org.objectweb.asm.commons.Method;  
  10.   
  11. public class Helloworld extends ClassLoader implements Opcodes {  
  12.   
  13.   public static void main(final String args[]) throws Exception {  
  14.   
  15.     // creates a ClassWriter for the Example public class,  
  16.     // which inherits from Object  
  17.   
  18.     ClassWriter cw = new ClassWriter(0);  
  19.     cw.visit(V1_1, ACC_PUBLIC, "Example"null"java/lang/Object"null);  
  20.     MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>""()V"null,  
  21.         null);  
  22.     mw.visitVarInsn(ALOAD, 0);  
  23.     mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object""<init>""()V");  
  24.     mw.visitInsn(RETURN);  
  25.     mw.visitMaxs(11);  
  26.     mw.visitEnd();  
  27.     mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main",  
  28.         "([Ljava/lang/String;)V"nullnull);  
  29.     mw.visitFieldInsn(GETSTATIC, "java/lang/System""out",  
  30.         "Ljava/io/PrintStream;");  
  31.     mw.visitLdcInsn("Hello world!");  
  32.     mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream""println",  
  33.         "(Ljava/lang/String;)V");  
  34.     mw.visitInsn(RETURN);  
  35.     mw.visitMaxs(22);  
  36.     mw.visitEnd();  
  37.     byte[] code = cw.toByteArray();  
  38.     FileOutputStream fos = new FileOutputStream("Example.class");  
  39.     fos.write(code);  
  40.     fos.close();  
  41.     Helloworld loader = new Helloworld();  
  42.     Class exampleClass = loader  
  43.         .defineClass("Example", code, 0, code.length);  
  44.     exampleClass.getMethods()[0].invoke(nullnew Object[] { null });  
  45.   
  46.     // ------------------------------------------------------------------------  
  47.     // Same example with a GeneratorAdapter (more convenient but slower)  
  48.     // ------------------------------------------------------------------------  
  49.   
  50.     cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);  
  51.     cw.visit(V1_1, ACC_PUBLIC, "Example"null"java/lang/Object"null);  
  52.     Method m = Method.getMethod("void <init> ()");  
  53.     GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, nullnull,  
  54.         cw);  
  55.     mg.loadThis();  
  56.     mg.invokeConstructor(Type.getType(Object.class), m);  
  57.     mg.returnValue();  
  58.     mg.endMethod();  
  59.     m = Method.getMethod("void main (String[])");  
  60.     mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, nullnull, cw);  
  61.     mg.getStatic(Type.getType(System.class), "out", Type  
  62.         .getType(PrintStream.class));  
  63.     mg.push("Hello world!");  
  64.     mg.invokeVirtual(Type.getType(PrintStream.class), Method  
  65.         .getMethod("void println (String)"));  
  66.     mg.returnValue();  
  67.     mg.endMethod();  
  68.     cw.visitEnd();  
  69.     code = cw.toByteArray();  
  70.     loader = new Helloworld();  
  71.     exampleClass = loader.defineClass("Example", code, 0, code.length);  
  72.     exampleClass.getMethods()[0].invoke(nullnew Object[] { null });  
  73.   }  

四、cglib
cglib is a powerful, high performance and quality Code Generation Library, It is used to extend JAVA classes and implements interfaces at runtime.
cglib是Code Generation Library的缩写。
cglib依赖于ASM库。
Hibernate主要是利用cglib生成pojo的子类并override get方法来实现lazy loading机制,Spring则是利用cglib来实现动态代理。
而JDK的动态代理机制要求有接口才行,这样就强制我们的pojo实现某个接口。

这里还是提供一个cglib的入门级的示例:
MyClass. java:
Java代码
  1. public class MyClass {  
  2.   
  3.   public void print() {  
  4.     System.out.println("I'm in MyClass.print!");  
  5.   }  
  6.   
  7. }  
public class MyClass {
public void print() {
System.out.println("I'm in MyClass.print!");
}
}

Main. java:
Java代码
  1. import java.lang.reflect.Method;  
  2. import net.sf.cglib.proxy.Enhancer;  
  3. import net.sf.cglib.proxy.MethodInterceptor;  
  4. import net.sf.cglib.proxy.MethodProxy;  
  5.   
  6. public class Main {  
  7.   
  8.   public static void main(String[] args) {  
  9.   
  10.     Enhancer enhancer = new Enhancer();  
  11.     enhancer.setSuperclass(MyClass.class);  
  12.     enhancer.setCallback(new MethodInterceptorImpl());  
  13.     MyClass my = (MyClass) enhancer.create();  
  14.     my.print();  
  15.   }  
  16.   
  17.   private static class MethodInterceptorImpl implements MethodInterceptor {  
  18.     public Object intercept(Object obj, Method method, Object[] args,  
  19.         MethodProxy proxy) throws Throwable {  
  20.       // log something  
  21.       System.out.println(method + " intercepted!");  
  22.   
  23.       proxy.invokeSuper(obj, args);  
  24.       return null;  
  25.     }  
  26.   }  
  27. }  
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyClass.class);
enhancer.setCallback(new MethodInterceptorImpl());
MyClass my = (MyClass) enhancer.create();
my.print();
}
private static class MethodInterceptorImpl implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
// log something
System.out.println(method + " intercepted!");
proxy.invokeSuper(obj, args);
return null;
}
}
}

打印结果为:
Java代码
  1. public void MyClass.print() intercepted!  
  2. I'm in MyClass.print!  
public void MyClass.print() intercepted!
I'm in MyClass.print!

这个示例就基本上实现了日志AOP的功能,很简单吧。

参考资料
CLR和JRE的运行机制的初步总结
Java虚拟机
了解Java ClassLoader
Java Virtual Machine Specification
Java bytecode
解读字节码文件
Java Bytecode Specification and Verification
ASM User Guide
Hello, ASM
cglig指南
Java下的框架编程--cglib的应用
AOP = Proxy Pattern + Method Reflection + Aspect DSL + 自动代码生成
深入浅出Spring AOP


From: http://www.javaeye.com/topic/98178

这篇关于深入了解Java ClassLoader、Bytecode 、ASM、cglib的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/604664

相关文章

Java中对象的创建和销毁过程详析

《Java中对象的创建和销毁过程详析》:本文主要介绍Java中对象的创建和销毁过程,对象的创建过程包括类加载检查、内存分配、初始化零值内存、设置对象头和执行init方法,对象的销毁过程由垃圾回收机... 目录前言对象的创建过程1. 类加载检查2China编程. 分配内存3. 初始化零值4. 设置对象头5. 执行

SpringBoot整合easy-es的详细过程

《SpringBoot整合easy-es的详细过程》本文介绍了EasyES,一个基于Elasticsearch的ORM框架,旨在简化开发流程并提高效率,EasyES支持SpringBoot框架,并提供... 目录一、easy-es简介二、实现基于Spring Boot框架的应用程序代码1.添加相关依赖2.添

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

SpringBoot中整合RabbitMQ(测试+部署上线最新完整)的过程

《SpringBoot中整合RabbitMQ(测试+部署上线最新完整)的过程》本文详细介绍了如何在虚拟机和宝塔面板中安装RabbitMQ,并使用Java代码实现消息的发送和接收,通过异步通讯,可以优化... 目录一、RabbitMQ安装二、启动RabbitMQ三、javascript编写Java代码1、引入

spring-boot-starter-thymeleaf加载外部html文件方式

《spring-boot-starter-thymeleaf加载外部html文件方式》本文介绍了在SpringMVC中使用Thymeleaf模板引擎加载外部HTML文件的方法,以及在SpringBoo... 目录1.Thymeleaf介绍2.springboot使用thymeleaf2.1.引入spring

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在