Spring IoC容器
Spring IoC容器相關(guān)知識(shí)整理
3.1 IoC容器
概念與核心作用
IoC即控制反轉(zhuǎn)(Inversion of Control),它是一種設(shè)計(jì)思想。傳統(tǒng)開發(fā)中,對(duì)象創(chuàng)建與依賴關(guān)系管理由程序自身負(fù)責(zé),這導(dǎo)致代碼耦合度高、可維護(hù)性差。IoC容器的出現(xiàn)改變了這一局面,它負(fù)責(zé)對(duì)象的創(chuàng)建、管理以及依賴注入。通過IoC,對(duì)象的控制權(quán)從應(yīng)用程序轉(zhuǎn)移到了容器,實(shí)現(xiàn)了松耦合,提高了軟件的可擴(kuò)展性和可維護(hù)性。例如在一個(gè)電商系統(tǒng)中,商品服務(wù)(ProductService)可能依賴于商品數(shù)據(jù)訪問對(duì)象(ProductDAO)。在沒有IoC容器時(shí),ProductService內(nèi)部需要手動(dòng)創(chuàng)建ProductDAO實(shí)例,而有了IoC容器,容器會(huì)自動(dòng)創(chuàng)建并注入ProductDAO實(shí)例到ProductService中。
IoC容器的工作原理
- 對(duì)象定義:開發(fā)人員通過配置文件(XML)或注解,在IoC容器中定義對(duì)象(也稱為Bean)以及它們之間的依賴關(guān)系。比如定義一個(gè)UserService的Bean,同時(shí)聲明它依賴于UserDAO的Bean。
- 容器初始化:在應(yīng)用程序啟動(dòng)時(shí),IoC容器會(huì)讀取配置信息,創(chuàng)建并管理這些Bean。它會(huì)按照配置的依賴關(guān)系,遞歸地創(chuàng)建所有相關(guān)的Bean。例如先創(chuàng)建UserDAO的實(shí)例,再創(chuàng)建UserService的實(shí)例,并將UserDAO實(shí)例注入到UserService中。
- 依賴注入:當(dāng)應(yīng)用程序需要使用某個(gè)Bean時(shí),IoC容器會(huì)將該Bean以及其依賴的其他Bean提供給應(yīng)用程序。這可以通過構(gòu)造函數(shù)注入、Setter方法注入或者字段注入來(lái)實(shí)現(xiàn)。比如通過構(gòu)造函數(shù)注入U(xiǎn)serDAO到UserService中,UserService類就可以如下定義:
public class UserService {
private UserDAO userDAO;
public UserService(UserDAO userDAO) {
this.userDAO = userDAO;
}
// 業(yè)務(wù)方法
}
Spring中的IoC容器實(shí)現(xiàn)
Spring框架提供了多種IoC容器的實(shí)現(xiàn),最常用的是BeanFactory和ApplicationContext。
- BeanFactory:是Spring IoC容器的基礎(chǔ)接口,提供了基本的IoC功能,如創(chuàng)建Bean、獲取Bean等。它采用延遲加載的方式,只有當(dāng)應(yīng)用程序第一次請(qǐng)求某個(gè)Bean時(shí),才會(huì)創(chuàng)建該Bean實(shí)例。這種方式適合資源有限、對(duì)啟動(dòng)性能要求較高的場(chǎng)景。
- ApplicationContext:是BeanFactory的子接口,它在BeanFactory的基礎(chǔ)上,增加了許多企業(yè)級(jí)功能,如事件發(fā)布、國(guó)際化支持等。ApplicationContext在容器啟動(dòng)時(shí),會(huì)預(yù)先實(shí)例化所有的單例Bean,這有助于在啟動(dòng)時(shí)發(fā)現(xiàn)配置錯(cuò)誤。同時(shí),它還支持多種配置方式(XML、注解等),更適合企業(yè)級(jí)應(yīng)用開發(fā)。例如在一個(gè)Web應(yīng)用中,通常使用ApplicationContext來(lái)管理Bean,因?yàn)樗梢愿玫嘏cSpring的其他模塊(如Spring MVC)集成。
Bean的生命周期
在IoC容器管理下,Bean的生命周期涵蓋多個(gè)階段:
- 實(shí)例化:IoC容器根據(jù)配置信息創(chuàng)建Bean的實(shí)例,比如通過
new
關(guān)鍵字調(diào)用類的構(gòu)造函數(shù)。若Bean存在依賴,此時(shí)依賴尚未注入。例如在創(chuàng)建UserService
實(shí)例時(shí),僅完成對(duì)象的初步構(gòu)建,UserDAO
依賴尚未處理。 - 屬性賦值:實(shí)例化后,容器按照配置進(jìn)行依賴注入,為Bean的屬性設(shè)置值。如將
UserDAO
實(shí)例注入到UserService
的對(duì)應(yīng)屬性中,可通過構(gòu)造函數(shù)、Setter方法或字段注入實(shí)現(xiàn)。 - 初始化前:在屬性賦值后,Bean實(shí)例準(zhǔn)備進(jìn)入初始化階段前,若Bean實(shí)現(xiàn)了
BeanNameAware
、BeanFactoryAware
或ApplicationContextAware
接口,容器會(huì)調(diào)用對(duì)應(yīng)的setBeanName
、setBeanFactory
、setApplicationContext
方法,使Bean能獲取自身名稱、所在的工廠或應(yīng)用上下文等信息。 - 初始化:此階段執(zhí)行Bean的初始化邏輯。若Bean實(shí)現(xiàn)了
InitializingBean
接口,容器會(huì)調(diào)用其afterPropertiesSet
方法;若在配置中指定了init - method
,容器會(huì)調(diào)用該方法。比如在UserService
中定義了init
方法用于初始化資源,配置<bean id="userService" class="com.example.service.UserService" init - method="init"/>
,容器會(huì)在合適時(shí)機(jī)調(diào)用init
方法。 - 使用:Bean初始化完成后,可供應(yīng)用程序使用,處理業(yè)務(wù)請(qǐng)求等操作。如在控制器中調(diào)用
UserService
的業(yè)務(wù)方法進(jìn)行用戶相關(guān)業(yè)務(wù)處理。 - 銷毀前:當(dāng)容器關(guān)閉時(shí),在Bean銷毀前,若Bean實(shí)現(xiàn)了
DisposableBean
接口,容器會(huì)調(diào)用其destroy
方法;若配置中指定了destroy - method
,容器會(huì)調(diào)用該方法。例如UserService
實(shí)現(xiàn)資源清理邏輯在destroyService
方法,配置<bean id="userService" class="com.example.service.UserService" destroy - method="destroyService"/>
,容器關(guān)閉時(shí)會(huì)執(zhí)行destroyService
。 - 銷毀:Bean完成生命周期,資源被釋放,從容器中移除。
3.2 基于XML管理Bean
XML配置文件基礎(chǔ)
在Spring中,通過XML配置文件來(lái)定義Bean及其依賴關(guān)系是一種傳統(tǒng)且常用的方式。XML配置文件通常命名為applicationContext.xml,當(dāng)然也可以自定義文件名。文件結(jié)構(gòu)一般如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 在這里定義Bean -->
</beans>
Bean的定義與配置
- 定義Bean:使用
<bean>
標(biāo)簽來(lái)定義一個(gè)Bean,通過id
屬性唯一標(biāo)識(shí)Bean,class
屬性指定Bean的實(shí)現(xiàn)類。例如定義一個(gè)UserService的Bean:
<bean id="userService" class="com.example.service.UserService"/>
- 設(shè)置屬性值:可以通過
<property>
標(biāo)簽來(lái)設(shè)置Bean的屬性值。如果UserService類有一個(gè)名為userDAO的屬性,且提供了對(duì)應(yīng)的Setter方法,那么可以如下配置:
<bean id="userService" class="com.example.service.UserService">
<property name="userDAO" ref="userDAO"/>
</bean>
<bean id="userDAO" class="com.example.dao.UserDAO"/>
這里name
屬性指定屬性名,ref
屬性引用另一個(gè)Bean的id
,表示依賴注入。
3. 構(gòu)造函數(shù)注入:如果要使用構(gòu)造函數(shù)注入,可以通過<constructor-arg>
標(biāo)簽。假設(shè)UserService的構(gòu)造函數(shù)接收一個(gè)UserDAO參數(shù),配置如下:
<bean id="userService" class="com.example.service.UserService">
<constructor-arg ref="userDAO"/>
</bean>
<bean id="userDAO" class="com.example.dao.UserDAO"/>
<constructor-arg>
標(biāo)簽的順序?qū)?yīng)構(gòu)造函數(shù)參數(shù)的順序。
4. 設(shè)置基本類型屬性:對(duì)于基本類型(如int、String等)的屬性,可以直接設(shè)置值。比如UserService類有一個(gè)名為defaultUserName的String屬性,配置如下:
<bean id="userService" class="com.example.service.UserService">
<property name="defaultUserName" value="admin"/>
</bean>
高級(jí)XML配置
- Bean的作用域:通過
scope
屬性來(lái)定義Bean的作用域。常見的作用域有:- singleton(單例,默認(rèn)):在IoC容器中只存在一個(gè)實(shí)例,所有對(duì)該Bean的請(qǐng)求都返回同一個(gè)實(shí)例。
- prototype(原型):每次請(qǐng)求該Bean時(shí),都會(huì)創(chuàng)建一個(gè)新的實(shí)例。例如對(duì)于一個(gè)無(wú)狀態(tài)的工具類Bean,可以使用singleton作用域以減少內(nèi)存開銷;而對(duì)于有狀態(tài)的、與用戶會(huì)話相關(guān)的Bean,可能更適合使用prototype作用域。配置示例:
<bean id="userService" class="com.example.service.UserService" scope="singleton"/>
- 依賴注入集合屬性:如果Bean的屬性是集合類型(如List、Set、Map等),也可以在XML中配置。例如UserService類有一個(gè)List類型的userRoles屬性,配置如下:
<bean id="userService" class="com.example.service.UserService">
<property name="userRoles">
<list>
<value>admin</value>
<value>user</value>
</list>
</property>
</bean>
對(duì)于Map類型,示例如下:
<bean id="userService" class="com.example.service.UserService">
<property name="userPermissions">
<map>
<entry key="read" value="true"/>
<entry key="write" value="false"/>
</map>
</property>
</bean>
- 引用外部屬性文件:可以將一些配置信息(如數(shù)據(jù)庫(kù)連接字符串)放在外部屬性文件中,然后在XML中引用。首先在src/main/resources目錄下創(chuàng)建一個(gè)jdbc.properties文件,內(nèi)容如下:
jdbc.url=jdbc:mysql://localhost:3306/mydb
jdbc.username=root
jdbc.password=123456
然后在XML中配置如下:
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
這里<context:property-placeholder>
標(biāo)簽用于加載外部屬性文件,${}
語(yǔ)法用于引用屬性文件中的值。
Bean生命周期在XML配置中的體現(xiàn)
在XML配置中,可通過init - method
和destroy - method
屬性明確指定Bean的初始化和銷毀方法。例如:
<bean id="customService" class="com.example.service.CustomService"
init - method="initCustom" destroy - method="cleanupCustom"/>
在CustomService
類中需對(duì)應(yīng)定義initCustom
和cleanupCustom
方法,容器在合適階段(初始化和銷毀時(shí))會(huì)調(diào)用它們,實(shí)現(xiàn)Bean生命周期特定階段的自定義邏輯。
3.3 基于注解管理Bean(☆)
常用注解介紹
- @Component:這是一個(gè)通用的組件注解,用于將一個(gè)類標(biāo)記為Spring中的一個(gè)組件,會(huì)被IoC容器掃描并管理。例如定義一個(gè)工具類:
@Component
public class Utils {
// 工具方法
}
- @Repository:用于標(biāo)記數(shù)據(jù)訪問層(DAO層)的組件。它不僅具備@Component的功能,還可以在發(fā)生數(shù)據(jù)訪問異常時(shí),進(jìn)行特定的異常轉(zhuǎn)換。例如:
@Repository
public class UserDAO {
// 數(shù)據(jù)庫(kù)操作方法
}
- @Service:用于標(biāo)記業(yè)務(wù)邏輯層(Service層)的組件。它同樣具備@Component的功能,并且可以在該類中進(jìn)行事務(wù)管理等業(yè)務(wù)邏輯相關(guān)的操作。例如:
@Service
public class UserService {
private UserDAO userDAO;
// 假設(shè)通過構(gòu)造函數(shù)注入
public UserService(UserDAO userDAO) {
this.userDAO = userDAO;
}
// 業(yè)務(wù)方法
}
- @Controller:用于標(biāo)記表現(xiàn)層(如Spring MVC中的控制器)的組件。它也繼承了@Component的功能,主要用于處理Web請(qǐng)求。例如:
@Controller
public class UserController {
private UserService userService;
// 假設(shè)通過構(gòu)造函數(shù)注入
public UserController(UserService userService) {
this.userService = userService;
}
// 處理請(qǐng)求的方法
}
依賴注入注解
- @Autowired:這是最常用的依賴注入注解。它可以用在構(gòu)造函數(shù)、Setter方法、字段上,用于自動(dòng)裝配Bean。例如在UserService中注入U(xiǎn)serDAO:
@Service
public class UserService {
@Autowired
private UserDAO userDAO;
// 業(yè)務(wù)方法
}
當(dāng)用在構(gòu)造函數(shù)上時(shí),如果構(gòu)造函數(shù)只有一個(gè)參數(shù),@Autowired注解可以省略。例如:
@Service
public class UserService {
private UserDAO userDAO;
@Autowired
public UserService(UserDAO userDAO) {
this.userDAO = userDAO;
}
// 業(yè)務(wù)方法
}
- @Qualifier:當(dāng)有多個(gè)相同類型的Bean時(shí),@Autowired無(wú)法確定要注入哪個(gè)Bean,此時(shí)可以結(jié)合@Qualifier注解使用。例如有兩個(gè)UserDAO的實(shí)現(xiàn)類UserDAOImpl1和UserDAOImpl2:
@Repository
public class UserDAOImpl1 implements UserDAO {
// 實(shí)現(xiàn)方法
}
@Repository
public class UserDAOImpl2 implements UserDAO {
// 實(shí)現(xiàn)方法
}
在UserService中指定注入U(xiǎn)serDAOImpl1:
@Service
public class UserService {
@Autowired
@Qualifier("userDAOImpl1")
private UserDAO userDAO;
// 業(yè)務(wù)方法
}
這里@Qualifier中的值對(duì)應(yīng)Bean的名稱(默認(rèn)是類名首字母小寫)。 3. @Resource:這是JDK提供的注解,也可以用于依賴注入。它有一個(gè)name屬性,用于指定要注入的Bean的名稱。例如:
@Service
public class UserService {
@Resource(name = "userDAO")
private UserDAO userDAO;
// 業(yè)務(wù)方法
}
@Resource注解如果不指定name屬性,默認(rèn)會(huì)按照Bean的名稱進(jìn)行注入。如果Bean的名稱與注入點(diǎn)的變量名相同,也可以省略name屬性。
注解驅(qū)動(dòng)的配置
要啟用基于注解的Bean管理,需要在Spring配置文件中進(jìn)行相應(yīng)的配置。如果使用XML配置文件,可以通過<context:component-scan>
標(biāo)簽來(lái)指定要掃描的包,Spring會(huì)自動(dòng)掃描該包及其子包下的所有類,將標(biāo)記了@Component及其衍生注解的類注冊(cè)為Bean。例如:
<context:component-scan base-package="com.example"/>
這里base-package
屬性指定要掃描的基礎(chǔ)包路徑。如果使用Java配置類,可以通過@ComponentScan
注解來(lái)實(shí)現(xiàn)相同的功能。例如:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
// 配置類中的其他配置方法
}
在實(shí)際開發(fā)中,基于注解的Bean管理方式更加簡(jiǎn)潔、靈活,能夠減少大量的XML配置代碼,提高開發(fā)效率,因此在現(xiàn)代Spring項(xiàng)目中被廣泛應(yīng)用。同時(shí),它也可以與基于XML的配置方式混合使用,根據(jù)具體的業(yè)務(wù)場(chǎng)景選擇最合適的配置方式。
Bean生命周期在注解配置中的體現(xiàn)
- 實(shí)現(xiàn)特定接口:與XML配置類似,若Bean實(shí)現(xiàn)
InitializingBean
接口,容器在屬性注入完成后會(huì)調(diào)用afterPropertiesSet
方法進(jìn)行初始化;實(shí)現(xiàn)DisposableBean
接口,容器關(guān)閉時(shí)會(huì)調(diào)用destroy
方法進(jìn)行銷毀。例如:
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
@Service
public class LifecycleService implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
// 初始化邏輯
}
@Override
public void destroy() throws Exception {
// 銷毀邏輯
}
}
- 使用@PostConstruct和@PreDestroy注解:這是Java EE提供的注解,在Spring中也可用于管理Bean生命周期。
@PostConstruct
注解的方法會(huì)在Bean屬性注入后執(zhí)行,類似XML中的init - method
;@PreDestroy
注解的方法會(huì)在Bean銷毀前執(zhí)行,類似XML中的destroy - method
。示例如下:
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.stereotype.Service;
@Service
public class AnotherLifecycleService {
@PostConstruct
public void init() {
// 初始化邏輯
}
@PreDestroy
public void cleanUp() {
// 銷毀邏輯
}
}
通過這些方式,無(wú)論是基于XML還是注解配置,開發(fā)人員都能精確控制Bean在IoC容器中的生命周期,確保資源合理初始化與釋放,提升應(yīng)用程序的穩(wěn)定性和性能 。
Spring 生態(tài)是以 Spring Framework 為核心,衍生出的一系列相互關(guān)聯(lián)、功能互補(bǔ)的技術(shù)和工具集合,用于簡(jiǎn)化企業(yè)級(jí)應(yīng)用開發(fā),覆蓋從單體應(yīng)用到分布式微服務(wù)、從 Web 開發(fā)到數(shù)據(jù)處理等諸多場(chǎng)景。