JavaSE反射與動(dòng)態(tài)代理詳細(xì)代碼示例
一、引言
在Java生態(tài)中,反射與動(dòng)態(tài)代理是支撐框架設(shè)計(jì)的基石,無(wú)論是Spring中的AOP,還是Mybatis的Mapper接口,都離不開(kāi)這兩項(xiàng)技術(shù)。在學(xué)習(xí)之前先了解反射和動(dòng)態(tài)代理分別可以做什么,簡(jiǎn)單說(shuō)反射賦予程序“看清類結(jié)構(gòu)”的功能,通過(guò)反射可以知道類中構(gòu)造方法,成員方法,成員變量等信息;動(dòng)態(tài)代理則實(shí)現(xiàn)了“無(wú)侵入式增強(qiáng)”,二者相輔相成,讓代碼具備靈活性與擴(kuò)展性。
二、Java反射:探索類的“隱藏世界”
2.1 反射的定義
在Java中,反射(Reflection)是一種強(qiáng)大的機(jī)制,允許程序在運(yùn)行時(shí)檢查或修改其自身的結(jié)構(gòu)和行為。通過(guò)反射,你可以查詢類的元數(shù)據(jù)(如類名、方法、字段、注解等),動(dòng)態(tài)地進(jìn)行創(chuàng)建對(duì)象,調(diào)用方法,訪問(wèn)字段等操作。
2.2反射的核心API
Java反射的核心操作集中在java.lang.reflect包中,其中關(guān)鍵類包括Class、Method、Field、Constructor,核心操作分為“獲取Class對(duì)象”“操作類成員”兩步
(1)如何獲取類對(duì)象?
首先在這里定義一個(gè)User類
public class User {
private String name;
public Integer age;
public User(){}
private User(String name) {
this.name = name;
}
public String getName() {
return name;
}
private void setAge(Integer age) {
this.age = age;
}
}獲取類對(duì)象有三種方式分別是:類名.class,對(duì)象.getClass(),Class.forName(“全類名”)
//1.類名.class(編譯時(shí)確定,最高效)
Class<?> class1 = User.class;
System.out.println(class1); //class User
//2.對(duì)象.getClass() (運(yùn)行時(shí)獲取,需要實(shí)例化)
User user = new User();
Class<?> class2 = user.getClass();
System.out.println(class2); //class User
//3.Class.forName("全類名") (運(yùn)行時(shí)加載,最靈活)
Class<?> class3 = Class.forName("User");
System.out.println(class3); //class User
//判斷上面三種方法獲取的是否是同一個(gè)對(duì)象
System.out.println(class1.equals(class2)); //true(2) 利用反射操作類
得到類成員后就可以利用反射操作類的構(gòu)造方法,成員方法,成員變量等元數(shù)據(jù),核心API也很易懂
示例1. 構(gòu)造器
//1.獲取類
Class<User> userClass = User.class;
//2.獲取構(gòu)造器
//2.1獲取公共構(gòu)造器
Constructor<?>[] publicConstructors = userClass.getConstructors();
System.out.println(Arrays.toString(publicConstructors)); //[public User()]
//2.2獲取所有構(gòu)造器
Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors();
System.out.println(Arrays.toString(declaredConstructors)); //[public User(), private User(java.lang.String)]
//2.3獲取一個(gè)公共構(gòu)造器 (可以傳遞參數(shù),參數(shù)是構(gòu)造器中的形參類)
Constructor<User> constructor = userClass.getConstructor();
System.out.println(constructor); //public User()
//2.4獲取一個(gè)構(gòu)造器 (可以傳遞參數(shù),參數(shù)是構(gòu)造器中的形參類)
Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class);
System.out.println(declaredConstructor); // private User(java.lang.String)然后可以對(duì)獲得的構(gòu)造器進(jìn)行操作:
//2.4獲取一個(gè)構(gòu)造器
Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class);
System.out.println(declaredConstructor); // private User(java.lang.String)
//3.Constructor類常用方法
//3.1獲取構(gòu)造器的權(quán)限修飾符
int modifiers = declaredConstructor.getModifiers();
System.out.println(declaredConstructor + "的構(gòu)造方法的修飾符為" + modifiers);//private User(java.lang.String)的構(gòu)造方法的修飾符為2(對(duì)應(yīng)private)
//3.2獲取構(gòu)造器的形參類型
Class<?>[] types = declaredConstructor.getParameterTypes();
System.out.println(declaredConstructor + "的形參類型有" + Arrays.toString(types)); //private User(java.lang.String)的形參類型有[class java.lang.String]
//3.3通過(guò)Constructor實(shí)例化對(duì)象
//因?yàn)楂@取的是私有方法,所以需要暴力反射
declaredConstructor.setAccessible(true);//將私有構(gòu)造器設(shè)置為可訪問(wèn)狀態(tài)
User user = declaredConstructor.newInstance( "無(wú)止境");
System.out.println(user.getName()); //無(wú)止境在 Java 中,java.lang.reflect.Constructor類的getModifiers()方法返回一個(gè)整數(shù),該整數(shù)是構(gòu)造器修飾符的位掩碼(Bitmask)。每個(gè)修飾符對(duì)應(yīng)一個(gè)二進(jìn)制位,不同修飾符通過(guò)按位或(OR)組合成最終的整數(shù)值。
獲取字段,方法等API與獲取構(gòu)造器類似,演示一下通過(guò)反射操作私有成員
//1.獲取類
Class<User> userClass = User.class;
//2.實(shí)例私有構(gòu)造器
Constructor<User> privateConstructor = userClass.getDeclaredConstructor(String.class);
privateConstructor.setAccessible(true);
User user = privateConstructor.newInstance("無(wú)止境");
//3.調(diào)用私有化方法setAge
Method setAge = userClass.getDeclaredMethod("setAge", Integer.class);
setAge.setAccessible(true);
setAge.invoke(user, 18);
System.out.println(user.getName() + "的年齡為" + user.getAge()); //無(wú)止境的年齡為18
//4.獲取私有屬性 name
Field nameField = userClass.getDeclaredField("name");
nameField.setAccessible(true);
String name = (String)nameField.get(user);
System.out.println("利用反射獲取的私有屬性" + name); //利用反射獲取的私有屬性無(wú)止境Method類中invoke()方法比較常用,傳遞的參數(shù)表示1:調(diào)用方法的對(duì)象, 2:調(diào)用方法傳遞的參數(shù);返回值就是原方法返回值
三、動(dòng)態(tài)代理,無(wú)侵入式增強(qiáng)的實(shí)現(xiàn)利器
3.1動(dòng)態(tài)代理定義
動(dòng)態(tài)代理是一種設(shè)計(jì)模式,允許程序在運(yùn)行時(shí)動(dòng)態(tài)生成代理類,代理類會(huì)包裹目標(biāo)對(duì)象,在不修改目標(biāo)代碼的情況下,對(duì)目標(biāo)方法進(jìn)行增強(qiáng)。Java中動(dòng)態(tài)代理主要分為兩種JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理。
3.2JDK動(dòng)態(tài)代理(接口增強(qiáng))
(1)核心組件
- InvocationHandler:定義代理邏輯,所有代理方法的調(diào)用都會(huì)委托給該接口的invoke方法。
- Proxy:用于創(chuàng)建動(dòng)態(tài)代理類和實(shí)例的工廠
(2)實(shí)現(xiàn)步驟
- 定義接口:目標(biāo)類需實(shí)現(xiàn)接口
- 實(shí)現(xiàn)InvocationHandler:處理方法調(diào)用邏輯
- 生成代理對(duì)象:通過(guò)Proxy.newProxyInstance()創(chuàng)建代理
(3)示例代碼
定義接口:目標(biāo)類需實(shí)現(xiàn)接口
//目標(biāo)接口
public interface UserService {
void addUser(String name);
void deleteUser(Integer id);
}
//目標(biāo)實(shí)現(xiàn)類
public class UserServiceImpl implements UserService {
@Override
public void addUser(String name) {
System.out.println("執(zhí)行添加姓名為 : " + name + " 用戶的操作");
}
@Override
public void deleteUser(Integer id) {
System.out.println("執(zhí)行刪除id為 : " + id + " 用戶的操作");
}
}實(shí)現(xiàn)InvocationHandler:處理方法調(diào)用邏輯
public class LogInvocationHandler implements InvocationHandler {
private Object target; //目標(biāo)對(duì)象
public LogInvocationHandler(Object target) {
this.target = target;
}
/**
*
* @param proxy 代理對(duì)象
* @param method 目標(biāo)方法
* @param args 方法參數(shù)
* @return 方法返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//前置日志,記錄日志
System.out.println("【日志】 開(kāi)始調(diào)用方法 : " + method.getName() + ",方法參數(shù)為 : " + Arrays.toString(args));
//調(diào)用目標(biāo)方法
Object result = method.invoke(target, args);
//后置日志,記錄方法完成
System.out.println("【日志】方法調(diào)用結(jié)束 : " + method.getName());
return result;
}
}生成代理對(duì)象:通過(guò)Proxy.newProxyInstance()創(chuàng)建代理
public static void main(String[] args) {
//1.創(chuàng)建目標(biāo)對(duì)象
UserServiceImpl target = new UserServiceImpl();
//2.創(chuàng)建InvocationHandler實(shí)例
LogInvocationHandler handler = new LogInvocationHandler(target);
//3.生成動(dòng)態(tài)代理對(duì)象
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), //類加載器
target.getClass().getInterfaces(), //目標(biāo)實(shí)現(xiàn)的接口
handler
);
//4.調(diào)用代理對(duì)象的方法
proxy.addUser("無(wú)止境");
proxy.deleteUser(1001);
}最后控制臺(tái)輸出結(jié)果
【日志】 開(kāi)始調(diào)用方法 : addUser,方法參數(shù)為 : [無(wú)止境]
執(zhí)行添加姓名為 : 無(wú)止境 用戶的操作
【日志】方法調(diào)用結(jié)束 : addUser
【日志】 開(kāi)始調(diào)用方法 : deleteUser,方法參數(shù)為 : [1001]
執(zhí)行刪除id為 : 1001 用戶的操作
【日志】方法調(diào)用結(jié)束 : deleteUser
至此就完成了一個(gè)簡(jiǎn)單的日志增強(qiáng)效果
(4)核心API用法
- public Object invoke(Object proxy, Method method, Object[] args):三個(gè)參數(shù)分別是代理對(duì)象本身(通常不需要使用);被調(diào)用的目標(biāo)方法;方法參數(shù)數(shù)組。
- public static Object newProxyInstance(
ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h
):里面要傳遞三個(gè)參數(shù)分別是類加載器(通常使用目標(biāo)接口的類加載器);目標(biāo)接口數(shù)組(至少一個(gè)接口);代理邏輯處理器。
3.3CFLIB實(shí)現(xiàn)動(dòng)態(tài)代理(類增強(qiáng))
(1)核心組件
- MethodInterceptor:攔截目標(biāo)類的方法調(diào)用,定義增強(qiáng)邏輯
- Enhancer:生成代理類和實(shí)例的工具
(2)實(shí)現(xiàn)步驟
- 引入CGLIB依賴
- 定義無(wú)接口的目標(biāo)類
- 實(shí)現(xiàn)MethodInterceptor
- 生成CGLIB代理對(duì)象:利用Enhancer.create()方法
(3)示例代碼
引入CGLIB依賴(maven)
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>public class OrderService {
//模擬創(chuàng)建訂單方法
public void createOrder(String orderNo) {
System.out.println("創(chuàng)建訂單,訂單號(hào)為 : " + orderNo);
}
}自定義類實(shí)現(xiàn)MethodInterceptor,處理方法調(diào)用邏輯
public class TransactionMethodInterceptor implements MethodInterceptor {
/**
*
* @param o 目標(biāo)對(duì)象
* @param method 目標(biāo)方法
* @param objects 方法參數(shù)
* @param methodProxy 方法代理對(duì)象
* @return 方法返回值
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//前置增強(qiáng):開(kāi)啟事務(wù)
System.out.println("【事務(wù)】開(kāi)啟數(shù)據(jù)事務(wù)");
//調(diào)用目標(biāo)方法(通過(guò)methodProxy提高性能)
Object result = methodProxy.invokeSuper(o, objects);
//后置增強(qiáng):提交事務(wù)
System.out.println("【事務(wù)】提交數(shù)據(jù)事務(wù)");
return result;
}
}通過(guò)Enhancer.create()生成代理對(duì)象
public class CglibProxyDemo {
public static void main(String[] args) {
//1.創(chuàng)建Enhancer實(shí)例(CGLIB核心)
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderService.class); //設(shè)置目標(biāo)類
enhancer.setCallback(new TransactionMethodInterceptor()); //設(shè)置增強(qiáng)器
//2.生成動(dòng)態(tài)代理對(duì)象
OrderService proxy = (OrderService) enhancer.create();
//3.調(diào)用代理對(duì)象方法
proxy.createOrder("order_112233");
}
}最終輸出結(jié)果:
【事務(wù)】開(kāi)啟數(shù)據(jù)事務(wù)
創(chuàng)建訂單,訂單號(hào)為 : order_112233
【事務(wù)】提交數(shù)據(jù)事務(wù)
(4)核心API用法
- public void setSuperclass(Class<?> superclass); 指定目標(biāo)類
- public void setCallback(Callback callback); 設(shè)置攔截器
- public Object create(); 生成代理實(shí)例
- public Object intercept(
Object obj,
Method method,
Object[] args,
MethodProxy proxy
):參數(shù)分別是obj:代理對(duì)象(通常不需要使用);method:被調(diào)用的目標(biāo)方法;args:方法參數(shù)數(shù)組;proxy:MethodProxy 對(duì)象,比反射調(diào)用更高效
3.3JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理橫向?qū)Ρ?/h3>
| 組件 | JDK動(dòng)態(tài)代理 | CGLIB動(dòng)態(tài)代理 |
|---|---|---|
| 接口 | InvocationHandler | MethodInterceptor |
| 生成代理方式 | Proxy.newProxyInstance | Enhancer.create() |
| 目標(biāo)類型 | 接口 | 類 |
| 調(diào)用目標(biāo)方法 | method.invoke(target, args) | proxy.invokSuper(obj, args) |
四、反射與動(dòng)態(tài)代理的實(shí)際應(yīng)用
(1)反射的實(shí)際應(yīng)用
- Spring 框架:Spring 使用反射來(lái)創(chuàng)建和管理 Bean。例如,通過(guò)配置文件(如 XML 或注解)指定要?jiǎng)?chuàng)建的類,Spring 容器利用反射機(jī)制根據(jù)類名動(dòng)態(tài)加載類,并調(diào)用其構(gòu)造函數(shù)創(chuàng)建對(duì)象實(shí)例。
- JUnit:JUnit 利用反射來(lái)發(fā)現(xiàn)和執(zhí)行測(cè)試方法。它通過(guò)反射遍歷測(cè)試類中的方法,根據(jù)特定的注解(如@Test)識(shí)別出測(cè)試方法,并動(dòng)態(tài)調(diào)用這些方法來(lái)執(zhí)行測(cè)試。例如,在一個(gè)測(cè)試類中定義了多個(gè)測(cè)試方法,JUnit 通過(guò)反射獲取這些方法并依次執(zhí)行,從而實(shí)現(xiàn)對(duì)代碼的自動(dòng)化測(cè)試。
(2) 動(dòng)態(tài)代理的實(shí)際應(yīng)用
- Spring AOP:Spring AOP 大量使用動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)切面功能。例如,在一個(gè)企業(yè)級(jí)應(yīng)用中,可能需要對(duì)業(yè)務(wù)方法進(jìn)行日志記錄、事務(wù)管理等操作。通過(guò)動(dòng)態(tài)代理,可以在不修改目標(biāo)業(yè)務(wù)類代碼的前提下,為目標(biāo)方法添加這些額外的功能。Spring 會(huì)為目標(biāo)對(duì)象創(chuàng)建動(dòng)態(tài)代理對(duì)象,當(dāng)調(diào)用代理對(duì)象的方法時(shí),會(huì)先執(zhí)行切面邏輯(如記錄日志),然后再調(diào)用目標(biāo)對(duì)象的實(shí)際方法,最后還可以在方法調(diào)用后執(zhí)行一些清理或補(bǔ)充邏輯(如事務(wù)提交)。
- 在數(shù)據(jù)庫(kù)事務(wù)管理中,動(dòng)態(tài)代理可以用于管理事務(wù)的開(kāi)啟、提交和回滾。以一個(gè)銀行轉(zhuǎn)賬操作的業(yè)務(wù)方法為例,動(dòng)態(tài)代理可以在方法調(diào)用前開(kāi)啟事務(wù),方法執(zhí)行過(guò)程中如果沒(méi)有異常則提交事務(wù),若出現(xiàn)異常則回滾事務(wù)。這樣可以將事務(wù)管理的邏輯從業(yè)務(wù)代碼中分離出來(lái),提高代碼的可維護(hù)性和復(fù)用性。
通過(guò)上面的例子可以初步了解反射與動(dòng)態(tài)代理,想要深度學(xué)習(xí)的話可以多看看框架中的源碼,學(xué)習(xí)優(yōu)秀的代碼風(fēng)格以及設(shè)計(jì)模式。
到此這篇關(guān)于JavaSE反射與動(dòng)態(tài)代理的文章就介紹到這了,更多相關(guān)JavaSE反射與動(dòng)態(tài)代理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java中反射動(dòng)態(tài)代理接口的詳解及實(shí)例
- Java動(dòng)態(tài)代理和反射機(jī)制詳解
- java利用反射實(shí)現(xiàn)動(dòng)態(tài)代理示例
- java 反射和動(dòng)態(tài)代理詳解及實(shí)例代碼
- Java反射和動(dòng)態(tài)代理的使用解讀
- 詳解Java中的反射機(jī)制和動(dòng)態(tài)代理
- 實(shí)例講解Java中動(dòng)態(tài)代理和反射機(jī)制
- Java反射的應(yīng)用之動(dòng)態(tài)代理深入理解
- Java高級(jí)-反射&動(dòng)態(tài)代理詳解
相關(guān)文章
Java使用XWPFDocument生成word文檔的示例代碼
XWPFDocument 是 Apache POI 庫(kù)中用于操作 .docx 格式 Word 文檔的核心類,本文將針對(duì) XWPFDocument 進(jìn)行詳細(xì)解析,涵蓋其核心功能、常見(jiàn)用法及實(shí)際開(kāi)發(fā)中的關(guān)鍵點(diǎn)2025-12-12
SpringBoot如何獲取application.properties中自定義的值
這篇文章主要介紹了SpringBoot獲取application.properties中的自定義的值,目錄結(jié)構(gòu)文件代碼給大家列舉的非常詳細(xì),需要的朋友可以參考下2021-09-09

