今天在看Java八股文時,對這里產生了一些疑惑,因為在目前做的練手項目中還沒有用到過除了new以外的新建對象方式,在請教了其他前輩后對此有了新的理解,所以專門記錄以用于梳理思路和復習基礎。這里著重講解反射機制實現新建對象
這里用很經典的一個框架:Spring IOC容器來舉例講解
========================================================================
Spring的**控制反轉(IOC)**容器是反射最經典的應用場景。其核心邏輯是:通過反射動態加載用戶定義的類,創建對象,并注入依賴。
1. 場景說明
假設我們有一個UserService
類,依賴UserDao
類(用戶定義的類):
// 用戶定義的Dao類(數據訪問層)
public class UserDao {public void save(User user) {System.out.println("保存用戶到數據庫:" + user);}
}// 用戶定義的Service類(業務層),依賴UserDao
public class UserService {private UserDao userDao; // 依賴注入的屬性// 構造器注入(或setter注入)public UserService(UserDao userDao) {this.userDao = userDao;}public void register(User user) {userDao.save(user); // 調用Dao的方法}
}
用戶通過Spring配置文件(或注解)聲明Bean:
<!-- applicationContext.xml 配置文件 -->
<beans><!-- 聲明UserDao Bean:類名是com.example.UserDao --><bean id="userDao" class="com.example.UserDao"/><!-- 聲明UserService Bean:依賴userDao --><bean id="userService" class="com.example.UserService"><constructor-arg ref="userDao"/> <!-- 構造器注入userDao --></bean>
</beans>
2. Spring IOC容器的工作流程(反射應用細節)
Spring容器(如ClassPathXmlApplicationContext
)啟動時,會執行以下反射相關的步驟,創建UserService
和UserDao
對象:
步驟1:加載配置文件,解析Bean定義
Spring讀取applicationContext.xml
,解析出兩個Bean定義:
userDao
:類名com.example.UserDao
,無依賴;userService
:類名com.example.UserService
,依賴userDao
。
步驟2:通過反射加載類(Class.forName()
)
①?解析配置文件,獲取類名
Spring的XmlBeanDefinitionReader
(XML配置解析器)讀取applicationContext.xml
,解析每個<bean>
標簽的class
屬性:
②?加載類的Class
對象
- 對于
userDao
,class
屬性的值是"com.example.UserDao"
; - 對于
userService
,class
屬性的值是"com.example.UserService"
。
Spring使用Class.forName()
加載每個類的Class
對象(運行時動態加載):
// 加載UserDao類(com.example.UserDao)
Class<?> userDaoClass = Class.forName("com.example.UserDao", false, classLoader);
// 加載UserService類(com.example.UserService)
Class<?> userServiceClass = Class.forName("com.example.UserService", false, classLoader);
步驟3:通過反射獲取構造器(getConstructor()
)
Spring需要獲取類的構造器,才能創建對象。對于UserDao
(無依賴),獲取無參構造器;對于UserService
(依賴UserDao
),獲取有參構造器(參數類型為UserDao
):
// 獲取UserDao的無參構造器(UserDao有默認無參構造器)
Constructor<?> userDaoConstructor = userDaoClass.getConstructor();
// 獲取UserService的有參構造器(參數類型為UserDao)
Constructor<?> userServiceConstructor = userServiceClass.getConstructor(UserDao.class);
步驟4:通過反射創建對象(newInstance()
)
- 創建
UserDao
對象:用無參構造器創建: // 創建UserDao對象(無參構造器)UserDao userDao = (UserDao) userDaoConstructor.newInstance();
- 創建
UserService
對象:用有參構造器創建,傳入userDao
對象(依賴注入): // 創建UserService對象(有參構造器,注入userDao)UserService userService = (UserService) userServiceConstructor.newInstance(userDao);
步驟5:將對象存入IOC容器
Spring將創建的userDao
和userService
對象存入IOC容器(如HashMap
,鍵是bean id
,值是對象):
// 模擬IOC容器(HashMap)
Map<String, Object> iocContainer = new HashMap<>();
iocContainer.put("userDao", userDao);
iocContainer.put("userService", userService);
步驟6:使用Bean(從容器中獲取)
用戶通過ApplicationContext
從容器中獲取userService
對象,無需手動new
:
// 啟動Spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 從容器中獲取userService對象(無需new)
UserService userService = (UserService) context.getBean("userService");
// 使用userService調用方法(依賴的userDao已注入)
userService.register(new User("zhangsan", 25));
為什么用反射?
- Spring是通用框架,需要處理任意用戶定義的類(如
UserService
、OrderService
、ProductDao
),無法在框架代碼中硬編碼new UserService()
(因為框架開發者不知道用戶會定義哪些類)。 - 反射的動態性(
Class.forName()
加載任意類,newInstance()
創建任意對象)滿足了這一需求,讓Spring能處理無限多的用戶類。