Ⅰ java 幾種動態代理實現及其性能比較
1. 動態代理是指在運行時,動態生成代理類。代理類的位元組碼將在運行時生成並載入當前的ClassLoader.
生成動態代理類的方法很多,如JDK自帶的動態代理、CGLIB、Javassist或者ASM庫。
JDK動態代理使用簡單,它內置在JDK中,因此不需要引入第三方Jar包,但相對功能比較弱。CGLIB和Javassist都是高級的位元組碼生成庫,總體性能比JDK自帶的動態代理好,而且功能十分強大。ASM是低級的位元組碼生成工具,使用ASM已經近乎在於使用Javabytecode編程,對開發人員要求較高,也是性能最好的一種動態代理生辰工具。但ASM的使用是在過於繁瑣,而且性能也沒有數量級的提升,與CGLIB等高級位元組碼生成工具相比,ASM程序的可維護性也較差。
JDK實現
1、步驟
1)通過實現InvocationHandler介面創建自己的調用處理器
2)通過為Proxy類指定ClassLoader對象和一組interface來創建動態代理類
3)通過反射機制獲得動態代理類的構造函數,其唯一參數類型是調用處理器介面類型
4)通過構造函數創建動態代理類實例,構造時調用處理器對象作為參數被傳入
2、創建代理
//InvocationHandlerImpl 實現了InvocationHandler介面,並能實現方法調用從代理類到委託類的分派轉發
//其內部通常包含指向委託類實例的引用,用於真正執行分派轉發過來的方法調用
InvocationHandler handler = new InvocaitonHandlerImpl(..);
//通過Proxy為包括Interface介面在內的一組介面動態創建代理類的對象
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{Interface.class,...});
//通過反射從生成的類對象獲得構造函數對象
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
//通過構造函數對象創建動態代理類實例
Interface Proxy = (Interface)constructor.newInstance(new Object[]{handler});
//Proxy類的靜態方法newProxyInstance對上面具體步驟的後三步做了封裝,簡化了動態代理對象的獲取過程。
//InvocationHandlerImpl實現了InvocaitonHandler介面,並能實現方法調用從代理類到委託類的分派轉發
InvocaitonHandler handler = new InvocationHandlerImpl(..);
//通過Proxy直接創建動態代理類實例
nterface proxy = (Interface)Proxy.newProxyInstance(classLoader,new Class[]{Interface.class},handler);
3、代碼
/**
* 介面
*
*/
public interface IDBQuery {
String request();
}
/**
* 真實的實現類,具體的目標對象
*
*/
public class DBQuery implements IDBQuery {
public DBQuery(){
try {
Thread.sleep(1000); //可能包含資料庫連接等耗時操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String request() {
return "request string";
}
}
/**
* JDK動態代理的實現類
*
*/
public class JdkDbQueryHandler implements InvocationHandler{
IDBQuery real = null; //主題介面
/**
* 生成Handler
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(real == null)
real = new DBQuery(); //如果是第一次調用,則生成真實對象
return real.request(); //使用真實主題完成實際的操作
}
/**
* 利用Handler生成動態代理對象
*/
public static IDBQuery createJdkProxy(){
//根據指定的類載入器和介面以及截獲器,返回代理類的一個實例對象
//ClassLoader loader :指定被代理對象的類載入器
//Class[] Interfaces :指定被代理對象所以事項的介面
//InvocationHandler h :指定需要調用的InvocationHandler對象
IDBQuery jdkProxy = (IDBQuery) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{IDBQuery.class}, new JdkDbQueryHandler());
return jdkProxy;
}
}
Ⅱ java 可以動態創建 屬性欄位么
可以的,你用 javassist, cglib 或者更為底層的工具 ASM 都是可以。
ASM 的話相對復雜一些,參考代碼:
下面這個是用 ASM 工具為 Student 類添加一個 public String 類型的 address 屬性:
1,需要添加屬性的原始類:Student.java
Java code?
public class Student {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2,添加屬性的適配器:AddFieldAdapter.java
Java code?
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
public class AddFieldAdapter extends ClassAdapter {
private int accessModifier;
private String name;
private String desc;
private boolean isFieldPresent;
public AddFieldAdapter(ClassVisitor cv, int accessModifier, String name, String desc) {
super(cv);
this.accessModifier = accessModifier;
this.name = name;
this.desc = desc;
}
@Override
public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value) {
if (name.equals(this.name)) {
isFieldPresent = true;
}
return cv.visitField(access, name, desc, signature, value);
}
@Override
public void visitEnd() {
if (!isFieldPresent) {
FieldVisitor fv = cv.visitField(accessModifier, name, desc, null, null);
if (fv != null) {
fv.visitEnd();
}
}
cv.visitEnd();
}
}
3,添加屬性的工具 AddField.java
Java code?
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
public class AddField {
private Class clazz = null;
private ClassReader cr = null;
private ClassWriter cw = null;
private ClassAdapter ca = null;
private File classFile = null;
private final static String CLASS_FILE_SUFFIX = ".class";
public AddField(Class clazz) {
this.clazz = clazz;
}
/**
* 添加一個 public 的類成員
* @param fieldName 類成員名
* @param fieldDesc 類成員類型描述
*/
public void addPublicField(String fieldName, String fieldDesc) {
if(cr == null) {
try {
cr = new ClassReader(clazz.getCanonicalName());
} catch (IOException e) {
e.printStackTrace();
}
cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
}
if(ca == null) {
ca = new AddFieldAdapter(cw, Opcodes.ACC_PUBLIC, fieldName, fieldDesc);
} else {
ca = new AddFieldAdapter(ca, Opcodes.ACC_PUBLIC, fieldName, fieldDesc);
}
}
/**
* 將位元組碼寫入類的 .class 文件
*
*/
public void writeByteCode() {
cr.accept(ca, ClassReader.SKIP_DEBUG);
byte[] bys = cw.toByteArray();
OutputStream os = null;
try {
os = new FileOutputStream(getFile());
os.write(bys);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 獲得類文件的 File 對象
* @return
*/
private File getFile() {
if(classFile == null) {
StringBuffer sb = new StringBuffer();
sb.append(clazz.getResource("/"))
.append(clazz.getCanonicalName().replace(".", File.separator))
.append(CLASS_FILE_SUFFIX);
classFile = new File(sb.substring(6));
}
return classFile;
}
}
Ⅲ java 的ASM位元組碼編輯如果替換方法中的類名
等同腔祥於java代碼:
System.currentTimeMillis();
替清圓雀換成time.zz()(要答早求time是類名,不是對象,zz是靜態方法)
很容易就出錯。
Ⅳ Java: asm.jar中的ClassReader方法執行時找不到類---Class not found
【A:JavaProject中的運行結果】你這副圖:你看它導入的是哪一個包裡面的ClassReader,先聲明,這個包不是在asm-5.0.3.jar;我剛看了這個jar,包名是這個org.objectweb.asm。
在java項目中的那個ClassReader你這樣用newClassReader("com.test")有用,但是到web裡面你用的就是asm-5.0.3.jar中的ClassReader,這時候你newClassReader("com.test")就有問題了!
這是asm裡面的構造函數:
publicClassReader(StringparamString)
throwsIOException
{
this(a(ClassLoader.getSystemResourceAsStream(paramString.replace('.','/')+".class"),true));
}
你看它做的事!對於com.test,它會把「.」替換成「/」,然後+「.class」,這時候你哪裡有這個類啊!
Ⅳ 史上最通俗易懂的ASM教程
ASM是一種針對Java位元組碼的強大框架,具備全面的操作和分析能力。它可以用於對已有class文件進行修改或生成全新的class文件。其優點在於能提供一些常用的位元組碼轉換和分析演算法,便於開發人員構建定製的復雜轉化和代碼分析工具,其性能優化設計使其尤其適合動態系統中使用。
使用javac命令編譯Java文件為class文件,JVM在執行程序時會使用這些class文件。ASM允許我們深入位元組碼層級進行操作,更近一步地接觸和理解JVM的運行機制。通過ASM,我們能夠對類欄位、方法定義及局部變數等元素進行直接修改,實現更精細、個性化的類文件定製。
了解ASM之前,我們首先需要掌握class位元組碼的結構。Java編譯器產生的class文件經過多次優化和精簡,欄位、方法的名稱和參數信息都已嵌入常量池中,實現了減小文件體積的目的。方法定義則被轉換為JVM指令執行。JVM基於棧的設計模式,通過棧幀來管理方法執行的上下文,每個方法調用創建新的棧幀並執行,之後回退並丟棄舊幀。
局部變數表用於存儲方法參數和局部變數,其索引能夠快速定位到具體變數。每個變數在局部變數表中佔用一個位置,通常變數名、類型、索引號為本地變數表的三個關鍵元素。操作數棧作為計算支持,用來組織和管理操作數和結果,支持方法執行中的運算和指令執行,通過入棧與出棧操作來控制變數值的存儲和使用。
JVM的指令集通過操作數棧和局部變數表共同驅動程序執行,每個指令負責對棧頂元素進行操作,並更新棧或局部變數表。例如在執行`a = b + c`時,首先從操作數棧中獲取b和c的值,將結果存儲回局部變數表,完成賦值操作。
通過ASM進行位元組碼編輯,能夠更精細地修改代碼行為,實現代碼重構、功能擴展或代碼優化等目的。ASM API採用訪問者模式設計,提供如ClassVisitor、MethodVisitor、FieldVisitor等介面,支持在類、方法、欄位等不同層次對位元組碼進行訪問和操作。
雖然ASM的操作較為復雜,但藉助相關插件如ASM ByteCode Outline,可以大大簡化生成ASM API代碼的過程,提高開發效率。結合深入學習資源和實踐經驗,開發者能夠充分發揮ASM的強大能力,實現更多可能的代碼定製與優化。通過理解和應用ASM,可以更深入掌握位元組碼層面的操作,提升Java程序性能和靈活性。
Ⅵ java asm label獲取調用方法
通過反射調用。
java動態代理是利用反射機制生成一個實現代理介面的匿名類,在調用具體方法前調用InvokeHandler來處理。而cglib動態代理是利用asm開源包,對代理對象類的class文件載入進來,通過修改其位元組碼生成子類來處理。
ASM 是一個 Java 位元組碼操控框架,它能被用來動態生成類或者增強既有類的功能,ASM 可以直接產生二進制 class 文件,也可以在類被載入入 Java 虛擬機之前動態改變類行為。
Ⅶ 史上最通俗易懂的ASM教程
ASM教程,帶你探索Java世界的底層秘密。正如王爾德所說,「我們都生活在代碼的陰溝里,但ASM讓我們能仰望更高級的編程藝術。」這是一種通用的Java位元組碼操作框架,旨在修改和動態生成class文件,以實現高性能的代碼操作。
在深入學習之前,你需要理解class位元組碼和JVM的基礎。Java源文件編譯成class文件後,欄位、方法名等信息存儲在常量池,以減小文件大小。方法定義則轉換為JVM指令,這涉及對基於棧的JVM設計模式的理解,其中局部變數表用於存儲方法參數和局部變數,操作數棧則負責數據的處理和交換。
舉例來說,當你看到"a = b + c"的位元組碼執行過程,就能理解操作數棧和局部變數表的動態變化。ASM提供ClassVisitor, MethodVisitor, FieldVisitor等API介面,讓你能直接操作位元組碼,實現定製化的需求。雖然使用起來可能有些復雜,但ASM ByteCode Outline這樣的插件能極大簡化過程。
參考資源如Lyon的《Java虛擬機》、位元組碼增強技術、kancloud.cn的教程、袁輝輝的技術博客等,深入研究JVM棧幀、操作數棧、局部變數表以及ASM的具體應用。通過學習,你將能掌握ASM這一強大的工具,為你的Java編程旅程增添新維度。