你好!當 JavaRush 管理部門正在努力提高新水平時,我想開始撰寫一系列有關 Spring 框架的培訓文章。是的,我知道互聯網上已經有很多關於這個主題的材料,但是,正如實踐所示,它們都處於 Hello World 級別。我不想談論如何正確放置註釋,而是談論它在“幕後”如何工作。本文面向那些已經以某種方式使用過該框架並熟悉基本概念的人。
初始化上下文。
那麼就讓我們從基礎開始吧。在我看來,最重要的一點是了解上下文是如何設定的以及 beans 是如何初始化的。如您所知,在Spring開始工作之前,需要對其進行配置。在遠古時代,這是使用 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-3.0.xsd">
<bean id="helloWorld" class="ru.javarush.HelloWorld">
<property name="message" value="Hello World!"/>
</bean>
</beans>
總的來說,這足以創建幾個控制器並啟動一個啟動程序(這不會啟動)。但是這個配置將如何讓 Spring 運作呢?這就是事情變得有趣的地方。為了讓 Spring 能夠理解我們的配置,有一個XmlBeanDefinitionReader
. 這是一個內部 Spring 元件,它掃描(解析)xml 並根據我們在那裡編寫的內容創建BeanDefinition
xml 。BeanDefinition
是一個儲存有關 bean 資訊的物件。這包括:應該從哪個類別(bean)創建;範圍; 是否安裝了延遲初始化;是否有必要在此 bean 之前初始化另一個以及 xml 中描述的其他屬性。所有收到的BeanDefinition
都會被添加到 中HashMap
,其中標識符是 bean 的名稱(由您設定或由 Spring 分配)和BeanDefinition
物件本身。一切都創建完成後BeanDefinition
,一個新的英雄出現在舞台上— BeanFactory
。該物件迭代HashMap’e
s BeanDefinition
,基於它們建立 bean 並將它們放入 IoC 容器中。這裡有一個細微差別,事實上,當應用程式啟動時,IoC 容器將包含具有 Singleton 範圍的 bean(預設),而其餘的 bean 在需要時建立(原型、請求、會話)。現在扯個小題外話,讓我們來認識另一個角色。
來認識 BeanPostProcessor。(BPP)
事實上,bean 不一定是應用程式的業務邏輯類別。bean 也稱為基礎設施 bean。簡而言之,基礎架構 bean 是配置業務邏輯 bean 的類別(是的,bean 太多了)。我將在下面告訴您更多相關信息,但為了讓您更清楚 BPP 的具體配置,我將舉一個範例。大家都熟悉這個摘要嗎@Autowired
?因此,您AutowiredAnnotationBeanPostProcessor
有責任確保所有類別都相互嵌入。
讓我們回到BeanFactory
現在了解了 BPP,您需要澄清的是,在迭代HashMap
' 時,BeanDefinition
首先創建所有 ' 並單獨放置(不在 IoC 容器中)BeanPostProcessor
。此後,我們的業務邏輯的常規 bean 被創建,放入 IoC 容器中,並且它們的配置開始使用單獨的延遲 BPP。事情就是這樣的,每個 BPP 有 2 個方法:
postProcessorBeforeInitialization(Object bean, String beanName);
postProcessorAfterInitialization(Object bean, String beanName);
遍歷我們的垃圾箱兩次。第一次呼叫該方法postProcessorBeforeInitialization
,第二次呼叫該方法postProcessorAfterInitialization
。當然,問題是為什麼需要兩種方法,讓我解釋一下。事實是,為了處理一些註解(例如@Transactional
),我們的 bean 被替換為代理類別。要理解為什麼要這樣做,您需要知道它是如何工作的@Transactional
,這就是它的工作原理。您需要在標有此註解的方法上新增幾行程式碼。怎麼做?沒錯,透過建立一個代理類,在其中將必要的程式碼新增到所需的方法中。現在想像一下這種情況,我們有一個類:
class A {
@Autowired
private SomeClass someClass;
@Transactional
public void method() {
// модификатор доступа обязательно public
}
}
該類別有 2 個註釋@Autowired
和@Transactional
. 兩個註釋都由不同的 BPP 處理。如果它首先起作用AutowiredBPP
,那麼一切都會好起來,但如果沒有,那麼我們就會遇到這個問題。事實是,當創建代理類別時,所有元資訊都會遺失。換句話說,@Autowired
代理類別中不會有關於註釋的信息,因此AutowiredBPP
它將不起作用,這意味著我們的欄位someClass
將具有值null
,這很可能會導致 NPE。還值得知道的是,在方法呼叫之間,postProcessorBeforeInitialization
會postProcessorAfterInitialization
呼叫-method init
(如果有)。這基本上是第二個建構函數,但不同的是,此時我們所有的依賴項都已經嵌入到類別中,我們可以從init
- 方法存取它們。因此,再次介紹上下文初始化演算法:
XmlBeanDefinitionReader
掃描我們的 xml 設定檔。- 創建
BeanDefinition
並將它們放入HashMap
. - 來
BeanFactory
和由此HashMap
分別將所有的相加BeanPostProcessor
。 - 從 's建立
BeanDefinition
bean 並將它們放入 IoC 容器中。 - 這裡 BPP 使用 2 種方法來設定這些 bean。
- 準備好。
GO TO FULL VERSION