一、Java ClassLoader
1,什么是ClassLoader
與 C 或 C++ 編寫的程序不同,Java 程序并不是一個(gè)可執(zhí)行文件,而是由許多獨(dú)立的類文件組成,每一個(gè)文件對(duì)應(yīng)于一個(gè) Java 類。
此外,這些類文件并非立即全部都裝入內(nèi)存,而是根據(jù)程序需要裝入內(nèi)存。ClassLoader 是 JVM 中將類裝入內(nèi)存的那部分。
而且,Java ClassLoader 就是用 Java 語(yǔ)言編寫的。這意味著創(chuàng)建您自己的 ClassLoader 非常容易,不必了解 JVM 的微小細(xì)節(jié)。
2,一些重要的方法
A)loadClass
ClassLoader.loadClass() 是ClassLoader的入口點(diǎn)。該方法的定義為:Class loadClass( String name, boolean resolve );
name:JVM 需要的類的名稱,如 Foo 或 java.lang.Object。
resolve:參數(shù)告訴方法是否需要解析類。
B)defineClass
defineClass方法是ClassLoader的主要訣竅。該方法接受由原始字節(jié)組成的數(shù)組并把它轉(zhuǎn)換成Class對(duì)象。
C)findSystemClass
findSystemClass方法從本地文件系統(tǒng)中尋找類文件,如果存在,就使用defineClass將原始字節(jié)轉(zhuǎn)換成Class對(duì)象,以將該文件轉(zhuǎn)換成類。
D)resolveClass
可以不完全地(不帶解析)裝入類,也可以完全地(帶解析)裝入類。當(dāng)編寫我們自己的loadClass時(shí)可以調(diào)用resolveClass,這取決于loadClass的resolve參數(shù)的值。
E)findLoadedClass
findLoadedClass充當(dāng)一個(gè)緩存:當(dāng)請(qǐng)求loadClass裝入類時(shí),它調(diào)用該方法來(lái)查看ClassLoader是否已裝入這個(gè)類,這樣可以避免重新裝入已存在類所造成的麻煩。
3,Java2中ClassLoader的變動(dòng)
1)loadClass的缺省實(shí)現(xiàn)
在Java2中l(wèi)oadClass的實(shí)現(xiàn)嵌入了大多數(shù)查找類的一般方法,并使您通過(guò)覆蓋findClass方法來(lái)定制它,在適當(dāng)?shù)臅r(shí)候findClass會(huì)調(diào)用loadClass。
這種方式的好處是可能不一定要覆蓋loadClass,只要覆蓋findClass就行了,這減少了工作量。
2)新方法:findClass
loadClass的缺省實(shí)現(xiàn)調(diào)用這個(gè)新方法。
3)新方法:getSystemClassLoader
如果覆蓋findClass或loadClass,getSystemClassLoader讓我們以實(shí)際ClassLoader對(duì)象來(lái)訪問(wèn)系統(tǒng)ClassLoader,而不是固定的從findSystemClass 調(diào)用它。
4)新方法:getParent
為了將類請(qǐng)求委托給父ClassLoader,這個(gè)新方法允許ClassLoader獲取它的父ClassLoader。
4,定制ClassLoader
其實(shí)我們或多或少都使用過(guò)定制的ClassLoader,因?yàn)锳pplet查看器中就包含一個(gè)定制的ClassLoader。
它不在本地文件系統(tǒng)中尋找類,而是訪問(wèn)遠(yuǎn)程服務(wù)器上的 Web 站點(diǎn),經(jīng)過(guò) HTTP 裝入原始的字節(jié)碼文件,并把它們轉(zhuǎn)換成JVM 內(nèi)的類。
Applet查看器中的ClassLoader還可以做其它事情:它們支持安全性以及使不同的Applet在不同的頁(yè)面上運(yùn)行而互不干擾。
我們將寫一個(gè)自己的ClassLoader實(shí)現(xiàn)示例,它將實(shí)現(xiàn)如下步驟,這也是ClassLoader的工作原理:
# 調(diào)用 findLoadedClass 來(lái)查看是否存在已裝入的類。
# 如果沒(méi)有,那么采用那種特殊的神奇方式來(lái)獲取原始字節(jié)。
# 如果已有原始字節(jié),調(diào)用defineClass將它們轉(zhuǎn)換成Class對(duì)象。
# 如果沒(méi)有原始字節(jié),然后調(diào)用findSystemClass查看是否從本地文件系統(tǒng)獲取類。
# 如果resolve參數(shù)是true,那么調(diào)用resolveClass解析Class對(duì)象。
# 如果還沒(méi)有類,返回ClassNotFoundException。
# 否則,將類返回給調(diào)用程序。
話不多說(shuō),看看代碼先:
FileClassLoader.java:
- 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;
- }
- }
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:
- 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);
- }
- }
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);
}
}
編譯并運(yùn)行MyApp類,結(jié)果為:
- MyApp
- FileClassLoader@757aef
- MyApp@9cab16
二、Bytecode
1,什么是Bytecode
C/C++編譯器把源代碼編譯成匯編代碼,Java編譯器把Java源代碼編譯成字節(jié)碼bytecode。
Java跨平臺(tái)其實(shí)就是基于相同的bytecode規(guī)范做不同平臺(tái)的虛擬機(jī),我們的Java程序編譯成bytecode后就可以在不同平臺(tái)跑了。
.net框架有IL(intermediate language),匯編是C/C++程序的中間表達(dá)方式,而bytecode可以說(shuō)是Java平臺(tái)的中間語(yǔ)言。
了解Java字節(jié)碼知識(shí)對(duì)debugging、performance tuning以及做一些高級(jí)語(yǔ)言擴(kuò)展或框架很有幫助。
2,使用javap生成Bytecode
JDK自帶的javap.exe文件可以反匯編Bytecode,讓我們看個(gè)例子:
Test.java:
- public class Test {
- public static void main(String[] args) {
- int i = 10000;
- System.out.println("Hello Bytecode! Number = " + i);
- }
- }
public class Test {
public static void main(String[] args) {
int i = 10000;
System.out.println("Hello Bytecode! Number = " + i);
}
}
編譯后的Test.class:
- 漱壕 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
漱壕 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:
- Compiled from "Test.java"
- public class Test extends java.lang.Object{
- public Test();
- Code:
- 0: aload_0
- 1: invokespecial #1;
- 4: return
-
- public static void main(java.lang.String[]);
- Code:
- 0: sipush 10000
- 3: istore_1
- 4: getstatic #2;
- 7: new #3;
- 10: dup
- 11: invokespecial #4;
- 14: ldc #5;
- 16: invokevirtual #6;
- 19: iload_1
- 20: invokevirtual #7;
- 23: invokevirtual #8;
- 26: invokevirtual #9;
- 29: return
-
- }
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就是一個(gè)基于stack的機(jī)器,每個(gè)thread擁有一個(gè)存儲(chǔ)著一些frames的JVM stack,每次調(diào)用一個(gè)方法時(shí)生成一個(gè)frame。
一個(gè)frame包括一個(gè)local variables數(shù)組(本地變量表),一個(gè)Operand LIFO stack和運(yùn)行時(shí)常量池的一個(gè)引用。
我們來(lái)簡(jiǎn)單分析一下生成的字節(jié)碼指令:
aload和iload指令的“a”前綴和“i”分別表示對(duì)象引用和int類型,其他還有“b”表示byte,“c”表示char,“d”表示double等等
我們這里的aload_0表示將把local variable table中index 0的值push到Operand stack,iload_1類似
invokespecial表示初始化對(duì)象,return表示返回
sipush表示把10000這個(gè)int值push到Operand stack
getstatic表示取靜態(tài)域
invokevirtual表示調(diào)用一些實(shí)例方法
這些指令又稱為opcode,Java一直以來(lái)只有約202個(gè)Opcode,具體請(qǐng)參考Java Bytecode規(guī)范。
我們看到Test.class文件不全是二進(jìn)制的指令,有些是我們可以識(shí)別的字符,這是因?yàn)橛行┌㈩惷统A孔址疀](méi)有編譯成二進(jìn)制Bytecode指令。
3,體驗(yàn)字節(jié)碼增強(qiáng)的魔力
我們J2EE常用的Hibernate、Spring都用到了動(dòng)態(tài)字節(jié)碼修改來(lái)改變類的行為。
讓我們通過(guò)看看ASM的org.objectweb.asm.MethodWriter類的部分方法來(lái)理解ASM是如何修改字節(jié)碼的:
- class MethodWriter implements MethodVisitor {
-
- private ByteVector code = new ByteVector();
-
- public void visitIntInsn(final int opcode, final int operand) {
-
- if (currentBlock != null) {
- if (compute == FRAMES) {
- currentBlock.frame.execute(opcode, operand, null, null);
- } else if (opcode != Opcodes.NEWARRAY) {
-
-
- int size = stackSize + 1;
- if (size > maxStackSize) {
- maxStackSize = size;
- }
- stackSize = size;
- }
- }
-
- if (opcode == Opcodes.SIPUSH) {
- code.put12(opcode, operand);
- } else {
- 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;
-
- if (currentBlock != null) {
- if (compute == FRAMES) {
- currentBlock.frame.execute(opcode, 0, cw, i);
- } else {
-
-
-
-
-
-
-
-
- if (argSize == 0) {
-
-
- argSize = getArgumentsAndReturnSizes(desc);
-
-
- i.intVal = argSize;
- }
- int size;
- if (opcode == Opcodes.INVOKESTATIC) {
- size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;
- } else {
- size = stackSize - (argSize >> 2) + (argSize & 0x03);
- }
-
- if (size > maxStackSize) {
- maxStackSize = size;
- }
- stackSize = size;
- }
- }
-
- 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);
- }
- }
- }
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);
}
}
}
通過(guò)注釋我們可以大概理解visitIntInsn和visitMethodInsn方法的意思。
比如visitIntInsn先計(jì)算stack的size,然后根據(jù)opcode來(lái)判斷是SIPUSH指令還是BIPUSH or NEWARRAY指令,并相應(yīng)的調(diào)用字節(jié)碼修改相關(guān)的方法。
三、ASM
我們知道Java是靜態(tài)語(yǔ)言,而python、ruby是動(dòng)態(tài)語(yǔ)言,Java程序一旦寫好很難在運(yùn)行時(shí)更改類的行為,而python、ruby可以。
不過(guò)基于bytecode層面上我們可以做一些手腳,來(lái)使Java程序多一些靈活性和Magic,ASM就是這樣一個(gè)應(yīng)用廣泛的開(kāi)源庫(kù)。
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真是越來(lái)越過(guò)氣了。
讓我們來(lái)看一個(gè)ASM的簡(jiǎn)單例子Helloworld.java,它生成一個(gè)Example類和一個(gè)main方法,main方法打印"Hello world!"語(yǔ)句:
- import java.io.FileOutputStream;
- import java.io.PrintStream;
-
- import org.objectweb.asm.ClassWriter;
- import org.objectweb.asm.MethodVisitor;
- import org.objectweb.asm.Opcodes;
- import org.objectweb.asm.Type;
- import org.objectweb.asm.commons.GeneratorAdapter;
- import org.objectweb.asm.commons.Method;
-
- public class Helloworld extends ClassLoader implements Opcodes {
-
- public static void main(final String args[]) throws Exception {
-
-
-
-
- ClassWriter cw = new ClassWriter(0);
- cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null);
- MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null,
- null);
- mw.visitVarInsn(ALOAD, 0);
- mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
- mw.visitInsn(RETURN);
- mw.visitMaxs(1, 1);
- mw.visitEnd();
- mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main",
- "([Ljava/lang/String;)V", null, null);
- mw.visitFieldInsn(GETSTATIC, "java/lang/System", "out",
- "Ljava/io/PrintStream;");
- mw.visitLdcInsn("Hello world!");
- mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println",
- "(Ljava/lang/String;)V");
- mw.visitInsn(RETURN);
- mw.visitMaxs(2, 2);
- mw.visitEnd();
- byte[] code = cw.toByteArray();
- FileOutputStream fos = new FileOutputStream("Example.class");
- fos.write(code);
- fos.close();
- Helloworld loader = new Helloworld();
- Class exampleClass = loader
- .defineClass("Example", code, 0, code.length);
- exampleClass.getMethods()[0].invoke(null, new Object[] { null });
-
-
-
-
-
- cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
- cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null);
- Method m = Method.getMethod("void <init> ()");
- GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null,
- cw);
- mg.loadThis();
- mg.invokeConstructor(Type.getType(Object.class), m);
- mg.returnValue();
- mg.endMethod();
- m = Method.getMethod("void main (String[])");
- mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw);
- mg.getStatic(Type.getType(System.class), "out", Type
- .getType(PrintStream.class));
- mg.push("Hello world!");
- mg.invokeVirtual(Type.getType(PrintStream.class), Method
- .getMethod("void println (String)"));
- mg.returnValue();
- mg.endMethod();
- cw.visitEnd();
- code = cw.toByteArray();
- loader = new Helloworld();
- exampleClass = loader.defineClass("Example", code, 0, code.length);
- exampleClass.getMethods()[0].invoke(null, new Object[] { null });
- }
- }
import java.io.FileOutputStream;
import java.io.PrintStream;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
public class Helloworld extends ClassLoader implements Opcodes {
public static void main(final String args[]) throws Exception {
// creates a ClassWriter for the Example public class,
// which inherits from Object
ClassWriter cw = new ClassWriter(0);
cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null);
MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null,
null);
mw.visitVarInsn(ALOAD, 0);
mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
mw.visitInsn(RETURN);
mw.visitMaxs(1, 1);
mw.visitEnd();
mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main",
"([Ljava/lang/String;)V", null, null);
mw.visitFieldInsn(GETSTATIC, "java/lang/System", "out",
"Ljava/io/PrintStream;");
mw.visitLdcInsn("Hello world!");
mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println",
"(Ljava/lang/String;)V");
mw.visitInsn(RETURN);
mw.visitMaxs(2, 2);
mw.visitEnd();
byte[] code = cw.toByteArray();
FileOutputStream fos = new FileOutputStream("Example.class");
fos.write(code);
fos.close();
Helloworld loader = new Helloworld();
Class exampleClass = loader
.defineClass("Example", code, 0, code.length);
exampleClass.getMethods()[0].invoke(null, new Object[] { null });
// ------------------------------------------------------------------------
// Same example with a GeneratorAdapter (more convenient but slower)
// ------------------------------------------------------------------------
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null);
Method m = Method.getMethod("void <init> ()");
GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null,
cw);
mg.loadThis();
mg.invokeConstructor(Type.getType(Object.class), m);
mg.returnValue();
mg.endMethod();
m = Method.getMethod("void main (String[])");
mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw);
mg.getStatic(Type.getType(System.class), "out", Type
.getType(PrintStream.class));
mg.push("Hello world!");
mg.invokeVirtual(Type.getType(PrintStream.class), Method
.getMethod("void println (String)"));
mg.returnValue();
mg.endMethod();
cw.visitEnd();
code = cw.toByteArray();
loader = new Helloworld();
exampleClass = loader.defineClass("Example", code, 0, code.length);
exampleClass.getMethods()[0].invoke(null, new Object[] { null });
}
}
我們看到上面的例子分別使用ASM的MethodVisitor和GeneratorAdapter兩種方式來(lái)動(dòng)態(tài)生成Example類并調(diào)用打印語(yǔ)句。
四、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庫(kù)。
Hibernate主要是利用cglib生成pojo的子類并override get方法來(lái)實(shí)現(xiàn)lazy loading機(jī)制,Spring則是利用cglib來(lái)實(shí)現(xiàn)動(dòng)態(tài)代理。
而JDK的動(dòng)態(tài)代理機(jī)制要求有接口才行,這樣就強(qiáng)制我們的pojo實(shí)現(xiàn)某個(gè)接口。
這里還是提供一個(gè)cglib的入門級(jí)的示例:
MyClass.java:
- public class MyClass {
-
- public void print() {
- System.out.println("I'm in MyClass.print!");
- }
-
- }
public class MyClass {
public void print() {
System.out.println("I'm in MyClass.print!");
}
}
Main.java:
- 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 {
-
- System.out.println(method + " intercepted!");
-
- proxy.invokeSuper(obj, args);
- return null;
- }
- }
- }
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;
}
}
}
打印結(jié)果為:
- public void MyClass.print() intercepted!
- I'm in MyClass.print!
public void MyClass.print() intercepted!
I'm in MyClass.print!
這個(gè)示例就基本上實(shí)現(xiàn)了日志AOP的功能,很簡(jiǎn)單吧。
參考資料
CLR和JRE的運(yùn)行機(jī)制的初步總結(jié)
Java虛擬機(jī)
了解Java ClassLoader
Java Virtual Machine Specification
Java bytecode
解讀字節(jié)碼文件
Java Bytecode Specification and Verification
ASM User Guide
Hello, ASM
cglig指南
Java下的框架編程--cglib的應(yīng)用
AOP = Proxy Pattern + Method Reflection + Aspect DSL + 自動(dòng)代碼生成
深入淺出Spring AOP
Traceback:http://www.javaeye.com/topic/98178