面試官:講講Spring IOC!
來源:juejin.cn/post/7327447632111779891
推薦:https://t.zsxq.com/17Qr4cXwo
IOC(Inversion of Control),即控制反轉(zhuǎn),它是一種設計思想
- 控制(誰控制誰)
之前通過new()進行創(chuàng)建對象,主動去創(chuàng)建依賴對象,而現(xiàn)在通過IOC容器負責實例化、配置和組裝 bean。
?在Spring IOC中,控制反轉(zhuǎn)指的是將對象的創(chuàng)建和管理交給Spring容器來完成,而不是由程序員手動管理。因此,在Spring IOC中,Spring容器控制對象的創(chuàng)建和管理,而不是對象控制容器。因此,可以說在Spring IOC中,Spring容器控制對象。
- 反轉(zhuǎn)(反轉(zhuǎn)什么)
之前對象主動直接去獲取依賴對象,而現(xiàn)在通過Ioc容器查找及注入(DI) 依賴對象。
?在Spring IOC中,控制反轉(zhuǎn)(Inversion of Control,IoC)指的是將對象的創(chuàng)建和管理交給Spring容器來完成,而不是由應用程序自己來創(chuàng)建和管理對象。控制反轉(zhuǎn)反轉(zhuǎn)的是控制權,即原本由應用程序控制對象的創(chuàng)建和生命周期,變成了由Spring容器控制對象的創(chuàng)建和生命周期。這種反轉(zhuǎn)的控制權可以帶來更好的松耦合、更高的可維護性和可測試性。
- 依賴注入
DI(Dependency Injection),即依賴注入,是IOC具體的實現(xiàn),IOC容器動態(tài)的將某個依賴注入到對象之中
Spring Bean 在Spring中,Spring IoC 容器管理的對象稱為 bean。
Spring IOC
Spring提供了兩種容器:BeanFactory和ApplicationContext
BeanFactory 接口提供配置框架和基本功能,能夠管理任何類型的對象。
ApplicationContext是BeanFactory的子接口
通過上圖我們可以看到ApplicationContext還繼承了其他接口,增加了消息資源處理(用于國際化)、事件發(fā)布、資源訪問等功能。
初始化流程
通過前面的內(nèi)容,我們可以了解到Spring IOC 容器,主要負責管理 Bean 的實例化、配置和組裝,接下來我們看看Spring IOC的初始化過程(以xml配置為例)
<xml version="1.0" encoding="UTF-8">
<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 id="clientMessageReq" class="com.tx.general.rtnp.ws.req.ClientMessageReq" scope="prototype">
</bean>
</beans>
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("clientMessageReq.xml");
ClientMessageReq clientMessageReq = context.getBean("clientMessageReq", ClientMessageReq.class);
clientMessageReq.setParams("text");
System.out.println(JSON.toJSONString(clientMessageReq));
}
通過new ClassPathXmlApplicationContext()方法創(chuàng)建了一個IOC容器,我們主要這個構造方法開始,分析一下IOC容器的初始化過程。
/**
* 構造一個ClassPathXmlApplicationContext對象。
*
* @param configLocations 配置文件路徑數(shù)組
* @param refresh 是否刷新上下文
* @param parent 父上下文對象,可能為空
* @throws BeansException 如果在創(chuàng)建對象時發(fā)生錯誤,拋出BeansException異常
*/
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
// 設置Bean資源加載器
super(parent);
// 設置Xml配置路徑
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
重點就是refresh()方法,它是IOC容器初始化的核心
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// ▲1.調(diào)用容器準備刷新的方法,獲取容器的當時時間,同時給容器設置同步標識
prepareRefresh();
// ▲2.初始化BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// ▲3.準備Bean工廠以在上下文中使用
prepareBeanFactory(beanFactory);
try {
// ▲4.為子類設置BeanFactory的后置處理器
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// ▲5.調(diào)用實現(xiàn)了BeanFactoryPostProcessor的類
invokeBeanFactoryPostProcessors(beanFactory);
// ▲6.注冊bean初始化時候的processor
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// ▲7.對上下文的消息源進行初始化
initMessageSource();
// ▲8.初始化上下文的事件機制
initApplicationEventMulticaster();
// ▲9.初始化其他特殊的Bean
onRefresh();
// ▲10.注冊實現(xiàn)了ApplicationListener的listener
registerListeners();
// ▲11.初始化配置為lazy-init=false的bean
finishBeanFactoryInitialization(beanFactory);
// ▲12.觸發(fā)所有監(jiān)聽ContextRefreshedEvent事件的listener
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
contextRefresh.end();
}
}
}
可以看到容器的創(chuàng)建和初始化就在obtainFreshBeanFactory()方法,重點來看一下
-
obtainFreshBeanFactory()初始化BeanFactory
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
/**
* 刷新Bean工廠 委托模式
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
// 如果已經(jīng)有容器存在,則需要把已有的容器銷毀和關閉,以保證在refresh之后使用的是新建立起來的IoC容器
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 創(chuàng)建DefaultListableBeanFactory對象
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 設置序列化ID
beanFactory.setSerializationId(getId());
// 自定義Bean工廠
customizeBeanFactory(beanFactory);
// 加載定義的Bean
loadBeanDefinitions(beanFactory);
// 將beanFactory賦值給類成員變量
this.beanFactory = beanFactory;
}
catch (IOException ex) {
// 解析bean定義源時發(fā)生I/O錯誤拋出ApplicationContextException異常
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
定位Resource(bean的Xml)
-
AbstractXmlApplicationContext#loadBeanDefinitions()加載BeanDefinition
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 創(chuàng)建XmlBeanDefinitionReader讀取器
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
// 設置Spring資源加載器
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 讀取器讀取Bean定義的Xml資源文件時,啟用Xml的校驗機制
initBeanDefinitionReader(beanDefinitionReader);
// 通過beanDefinitionReader加載BeanDefinitions
loadBeanDefinitions(beanDefinitionReader);
}
-
AbstractXmlApplicationContext#loadBeanDefinitions()定位資源,以Resource的形式去加載資源
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
// 獲得Bean配置文件的資源位置
Resource[] configResources = getConfigResources();
if (configResources != null) {
// 讀取AbstractBeanDefinitionReader中定位的資源
reader.loadBeanDefinitions(configResources);
}
// 獲取ClassPathXmlApplicationContext構造方法中setConfigLocations方法設置的資源
String[] configLocations = getConfigLocations();
if (configLocations != null) {
//通過AbstractBeanDefinitionReader#loadBeanDefinitions()方法,以Resource的形式去加載資源。
reader.loadBeanDefinitions(configLocations);
}
}
載入BeanDefinition
經(jīng)過上面的步驟我們定義的資源以及容器本身需要的資源全部加載到reader中,通過XmlBeanDefinitionReader#loadBeanDefinitions()方法會得到一個XML文件的InputStream,然后 將Bean定義資源轉(zhuǎn)換成Document對象。最后在registerBeanDefinitions()方法中完成對解析
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 創(chuàng)建DefaultBeanDefinitionDocumentReader對象
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// 通過DefaultBeanDefinitionDocumentReader對象的registerBeanDefinitions()方法解析Document
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
而具體的解析過程由BeanDefinitionParserDelegate()來實現(xiàn)
protected void doRegisterBeanDefinitions(Element root) {
// ... 代碼部分省略
// 執(zhí)行XML預處理操作
preProcessXml(root);
// 解析Bean定義
parseBeanDefinitions(root, this.delegate);
// 執(zhí)行XML后處理操作
postProcessXml(root);
// ... 代碼部分省略
}
通過BeanDefinitionParserDelegate#parseBeanDefinitionElement()方法,得到結果BeanDefinitionHolder(BeanDefinitionHolder是BeanDefinition的封裝類,封裝了BeanDefinition,Bean的名字、別名)
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 對給定的<bean>標簽進行解析,得到BeanDefinitionHolder對象
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 向IOC容器注冊解析到BeanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 在BeanDefinition像IOC容器注冊完以后,發(fā)送事件消息
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
BeanDefiniton注冊
最終Xml配置被解析成了BeanDefinitionHolder類,通過DefaultListableBeanFactory#registerBeanDefinition()方法,將BeanDefinition注冊到beanDefinitionsMap中
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
至此,IOC容器完成了初始化,其內(nèi)部使用ConcurrentHashMap(Spring IOC容器本質(zhì))保存了BeanDefinition信息
總結
Spring IOC初始化可以分為三個步驟:
- 通過
ResourceLoader來完成資源文件位置的定位(Resource) - 將Resource定位好的資源載入到BeanDefinition(通過 BeanDefinitionReader來完成定義信息的解析)
- 將BeanDefiniton注冊到容器中(通過BeanDefinitionRegistry接口,將BeanDefinition保存到ConcurrentHashMap中)
Spring IOC容器管理了定義的各種Bean對象及其相互的關系,降低了耦合度
