Spring解析(一) IOC容器
1、IOC簡介
spring框架是java web開發中的重要框架,其中IOC(Inversion of Control)控制反轉和AOP切面程式設計是spring的核心,IOC不僅僅是一個技術,同時也是一種設計思想。傳統的java開發當中,一個物件內往往需要多個物件的參與才能正常執行,物件的生成大多是通過 new object()的方式生存,這樣也需要我們自己去管理物件的生命週期。而在IOC中,則是將物件交給IOC容器來控制物件的建立,所謂反轉的意思就是從我們主動獲取物件變成了容器幫忙建立和注入依賴物件,由容器替我們管理生命週期。
2、IOC的好處
IOC作為一種技術思想,能有效地幫我們設計出鬆耦合,面向介面的程式。傳統程式中由於高度耦合,導致測試和複用困難。當使用了IOC容器後,我們只需要關注自身的業務邏輯就能完成系統的開發。
3、DI和IOC
DI-Dependency Injection,依賴注入,就是IOC容器動態的將依賴關係注入到元件當中。其實IOC和DI是同一個概念的不同描述。我們的應用程式依賴於IOC容器,在應用程式執行時,IOC容器會檢查所需要的依賴,然後給應用程式注入物件或資源。
4、IOC容器具體資訊
spring中,IOC容器主要就是BeanFactory這個介面,其中xmlBeanFactory是一個比較簡單地IOC容器,下圖為類繼承關係。
spring中,實際上是把DefaultListableBeanFactory作為一個預設的功能完整的IOC容器來使用的,XmlBeanFactory就是繼承了它的同時,增加了一些功能。在spring中,ApplicationContext作為更高階的容器,其原理也和XmlBeanFactory一樣,擴充套件或持有了DefaultListableBeanFactory來獲得容器功能。
上面的這段程式碼非常有代表性地敘述了IOC容器使用方式:
(1)建立配置檔案的抽象資源,其中包含了BeanDefinition,比如applicationcontext.xml
(2)然後建立BeanFactory
(3)最後建立個讀取BeanDefinition的讀取器
(4)讀完配置資訊後,解析過程就由XmlBeanDefinitionReader來完成。
5、IOC容器初始化過程
IOC容器啟動包括了BeanDefinition的Resource定位,載入和註冊三個過程。
(1)、resource定位:主要是由ResourceLoader通過統一的Resource介面來完成,Resource對各種形式的BeanDefinition的使用都提供了統一的介面。這個過程就是尋找注入的資料。
(2)、BeanDefinition的載入:
這個載入就是把使用者定義好的Bean表示成IOC容器內部的資料結構,這個資料結構就是BeanDefinition。
(3)、向IOC容器註冊BeanDefinition:
通過呼叫BeanDefinition介面的實現來完成,IOC容器內部將BeanDefinition注入到一個hashMap中,通過這個HashMap來持有BeanDefinition。
PS:初始化過程中,一般不包含Bean依賴注入,IOC設計中,Bean載入和依賴注入是兩個獨立的過程,依賴注入一般發生在應用第一次通過getBean向容器索取Bean的時候,當然,如果你使用了lazyinit屬性,那麼依賴注入在IOC初始化時就完成了,否則都要等到第一次使用getBean才注入。
6、IOC初始化具體流程
通過我們比較熟悉的XmlApplicationContext容器來看一下IOC的流程:
(1)ApplicationContext =new FileSystemXmlApplicationContext(xmlPath);
這裡實際呼叫了
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
其中的refresh很重要,啟動容器初始化的入口。
(2)設定資源載入器
上段程式碼中的super(parent);就是設定了Bean資源載入器,呼叫了父類的父類AbstractRefreshableConfigApplicationContext中的setConfigLocaions(configLocations)方法設定資原始檔的定位路徑。
//處理單個資原始檔路徑為一個字串的情況
public void setConfigLocation(String location) {
//String CONFIG_LOCATION_DELIMITERS = ",; /t/n";
//即多個資原始檔路徑之間用” ,; /t/n”分隔,解析成陣列形式
setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
}
//解析Bean定義資原始檔的路徑,處理多個資原始檔字串陣列
public void setConfigLocations(String[] locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
// resolvePath為同一個類中將字串解析為路徑的方法
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
(3)AbstractApplicationContext的refresh函式載入Bean定義:
ps:這裡的refresh方法相當於重啟了IOC容器,重啟之前會先檢查是否存在容器,如果存在則銷燬容器中的Beans將其關閉
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//這裡使用了委派設計模式,父類定義了抽象的refreshBeanFactory()方法,具體實現呼叫子類容器的refreshBeanFactory()方法
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
refresh()過程中的obtaiFreshBeanFactory方法中呼叫了refreshBeanFactory()方法,內容如下:
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {//如果已經有容器,銷燬容器中的bean,關閉容器
destroyBeans();
closeBeanFactory();
}
try {
//建立IoC容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
//對IoC容器進行定製化,如設定啟動引數,開啟註解的自動裝配等
customizeBeanFactory(beanFactory);
//呼叫載入Bean定義的方法,主要這裡又使用了一個委派模式,在當前類中只定義了抽象的loadBeanDefinitions方法,具體的實現呼叫子類容器
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
首先判斷是否有BeanFactory存在,如果存在則銷燬Bean並且關閉容器,然後建立一個新容器,使用loadBeanDefinitions方法載入BeanDefinition。
(4)AbstractXmlApplicationContext實現loadBeanDefinitions
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
……
//實現父類抽象的載入Bean定義方法
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//建立XmlBeanDefinitionReader,即建立Bean讀取器,並通過回撥設定到容器中去,容 器使用該讀取器讀取Bean定義資源
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//為Bean讀取器設定Spring資源載入器,AbstractXmlApplicationContext的
//祖先父類AbstractApplicationContext繼承DefaultResourceLoader,因此,容器本身也是一個資源載入器
beanDefinitionReader.setResourceLoader(this);
//為Bean讀取器設定SAX xml解析器
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
//當Bean讀取器讀取Bean定義的Xml資原始檔時,啟用Xml的校驗機制
initBeanDefinitionReader(beanDefinitionReader);
//Bean讀取器真正實現載入的方法
loadBeanDefinitions(beanDefinitionReader);
}
//Xml Bean讀取器載入Bean定義資源
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//獲取Bean定義資源的定位
Resource[] configResources = getConfigResources();
if (configResources != null) {
//Xml Bean讀取器呼叫其父類AbstractBeanDefinitionReader讀取定位
//的Bean定義資源
reader.loadBeanDefinitions(configResources);
}
//如果子類中獲取的Bean定義資源定位為空,則獲取FileSystemXmlApplicationContext構造方法中setConfigLocations方法設定的資源
String[] configLocations = getConfigLocations();
if (configLocations != null) {
//Xml Bean讀取器呼叫其父類AbstractBeanDefinitionReader讀取定位
//的Bean定義資源
reader.loadBeanDefinitions(configLocations);
}
}
//這裡又使用了一個委託模式,呼叫子類的獲取Bean定義資源定位的方法
//該方法在ClassPathXmlApplicationContext中進行實現,對於我們
//舉例分析原始碼的FileSystemXmlApplicationContext沒有使用該方法
protected Resource[] getConfigResources() {
return null;
} ……
}
互動過程如下:(5)AbstractBeanDefinitionReader讀取BeanDefinition
上段程式碼最後呼叫了AbstractBeanDefinitionReader的loadBeanDefinition方法,通過getResource(String location)函式將location轉化成resources[],最後進入下面的函式
(6)xmlBeanDefinitonReader載入Bean定義資源
上圖中最後都轉化成了loadBeanDefinitions(Resource resource)方法,這個方法是其子類XmlBeanDefinitionReader中實現的。
//XmlBeanDefinitionReader載入資源的入口方法
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//將讀入的XML資源進行特殊編碼處理
return loadBeanDefinitions(new EncodedResource(resource));
}
//這裡是載入XML形式Bean定義資原始檔方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
.......
try {
//將資原始檔轉為InputStream的IO流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//從InputStream中得到XML的解析源
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//這裡是具體的讀取過程
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
//關閉從Resource中得到的IO流
inputStream.close();
}
}
.........
}
//從特定XML檔案中實際載入Bean定義資源的方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
//將XML檔案轉換為DOM物件,解析過程由documentLoader實現
Document doc = this.documentLoader.loadDocument(
inputSource, this.entityResolver, this.errorHandler, validationMode, this.namespaceAware);
//這裡是啟動對Bean定義解析的詳細過程,該解析過程會用到Spring的Bean配置規則
return registerBeanDefinitions(doc, resource);
}
.......
}
最後是將documentLoader.loadDocuement方法將某個xml檔案轉化成document物件,然後
loadDocuement方法如下:
//使用標準的JAXP將載入的Bean定義資源轉換成document物件
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
//建立檔案解析器工廠
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
//建立文件解析器
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
//解析Spring的Bean定義資源
return builder.parse(inputSource);
}
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
throws ParserConfigurationException {
//建立文件解析工廠
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(namespaceAware);
//設定解析XML的校驗
if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
factory.setValidating(true);
if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
factory.setNamespaceAware(true);
try {
factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
}
catch (IllegalArgumentException ex) {
ParserConfigurationException pcex = new ParserConfigurationException(
"Unable to validate using XSD: Your JAXP provider [" + factory +
"] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
"Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
pcex.initCause(ex);
throw pcex;
}
}
}
return factory;
}
(7)註冊BeanDefitions
得到了Document物件後,呼叫了registerBeanDefinitions(doc,resource)方法來完成解析:
//按照Spring的Bean語義要求將Bean定義資源解析並轉換為容器內部資料結構
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//得到BeanDefinitionDocumentReader來對xml格式的BeanDefinition解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//獲得容器中註冊的Bean數量
int countBefore = getRegistry().getBeanDefinitionCount();
//解析過程入口,這裡使用了委派模式,BeanDefinitionDocumentReader只是個介面,//具體的解析實現過程有實現類DefaultBeanDefinitionDocumentReader完成
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//統計解析的Bean數量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
//建立BeanDefinitionDocumentReader物件,解析Document物件
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
}
可以看到,這裡呼叫xml解析器來將Bean定義檔案先轉換成document物件,然後再完成xml解析後,再按照spring的Bean規則對Document物件進行解析。所以這裡是兩個解析步驟。最後是呼叫了BeanDefinitionDocumentReader的實現類DefaultBeanDefinitionDocumentReader中的方法進行物件解析。
(8)DefaultBeanDefinitionDocumentReader進行解析:
//根據Spring DTD對Bean的定義規則解析Bean定義Document物件
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
//獲得XML描述符
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
//獲得Document的根元素
Element root = doc.getDocumentElement();
//具體的解析過程由BeanDefinitionParserDelegate實現,
//BeanDefinitionParserDelegate中定義了Spring Bean定義XML檔案的各種元素
BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
//在解析Bean定義之前,進行自定義的解析,增強解析過程的可擴充套件性
preProcessXml(root);
//從Document的根元素開始進行Bean定義的Document物件
parseBeanDefinitions(root, delegate);
//在解析Bean定義之後,進行自定義的解析,增加解析過程的可擴充套件性
postProcessXml(root);
}
//建立BeanDefinitionParserDelegate,用於完成真正的解析過程
protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root) {
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
//BeanDefinitionParserDelegate初始化Document根元素
delegate.initDefaults(root);
return delegate;
}
//使用Spring的Bean規則從Document的根元素開始進行Bean定義的Document物件
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//Bean定義的Document物件使用了Spring預設的XML名稱空間
if (delegate.isDefaultNamespace(root)) {
//獲取Bean定義的Document物件根元素的所有子節點
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//獲得Document節點是XML元素節點
if (node instanceof Element) {
Element ele = (Element) node;
//Bean定義的Document的元素節點使用的是Spring預設的XML名稱空間
if (delegate.isDefaultNamespace(ele)) {
//使用Spring的Bean規則解析元素節點
parseDefaultElement(ele, delegate);
}
else {
//沒有使用Spring預設的XML名稱空間,則使用使用者自定義的解//析規則解析元素節點
delegate.parseCustomElement(ele);
}
}
}
}
else {
//Document的根節點沒有使用Spring預設的名稱空間,則使用使用者自定義的
//解析規則解析Document根節點
delegate.parseCustomElement(root);
}
}
//使用Spring的Bean規則解析Document元素節點
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//如果元素節點是<Import>匯入元素,進行匯入解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//如果元素節點是<Alias>別名元素,進行別名解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//元素節點既不是匯入元素,也不是別名元素,即普通的<Bean>元素,
//按照Spring的Bean規則解析元素
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
}
//解析<Import>匯入元素,從給定的匯入路徑載入Bean定義資源到Spring IoC容器中
protected void importBeanDefinitionResource(Element ele) {
//獲取給定的匯入元素的location屬性
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
//如果匯入元素的location屬性值為空,則沒有匯入任何資源,直接返回
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
//使用系統變數值解析location屬性值
location = SystemPropertyUtils.resolvePlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<Resource>(4);
//標識給定的匯入元素的location是否是絕對路徑
boolean absoluteLocation = false;
try {
absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
}
catch (URISyntaxException ex) {
//給定的匯入元素的location不是絕對路徑
}
//給定的匯入元素的location是絕對路徑
if (absoluteLocation) {
try {
//使用資源讀入器載入給定路徑的Bean定義資源
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
if (logger.isDebugEnabled()) {
logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
}
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error(
"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
}
}
else {
//給定的匯入元素的location是相對路徑
try {
int importCount;
//將給定匯入元素的location封裝為相對路徑資源
Resource relativeResource = getReaderContext().getResource().createRelative(location);
//封裝的相對路徑資源存在
if (relativeResource.exists()) {
//使用資源讀入器載入Bean定義資源
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
actualResources.add(relativeResource);
}
//封裝的相對路徑資源不存在
else {
//獲取Spring IoC容器資源讀入器的基本路徑
String baseLocation = getReaderContext().getResource().getURL().toString();
//根據Spring IoC容器資源讀入器的基本路徑載入給定匯入
//路徑的資源
importCount = getReaderContext().getReader().loadBeanDefinitions(
StringUtils.applyRelativePath(baseLocation, location), actualResources);
}
if (logger.isDebugEnabled()) {
logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
}
}
catch (IOException ex) {
getReaderContext().error("Failed to resolve current resource location", ele, ex);
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
ele, ex);
}
}
Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
//在解析完<Import>元素之後,傳送容器匯入其他資源處理完成事件
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
//解析<Alias>別名元素,為Bean向Spring IoC容器註冊別名
protected void processAliasRegistration(Element ele) {
//獲取<Alias>別名元素中name的屬性值
String name = ele.getAttribute(NAME_ATTRIBUTE);
//獲取<Alias>別名元素中alias的屬性值
String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
boolean valid = true;
//<alias>別名元素的name屬性值為空
if (!StringUtils.hasText(name)) {
getReaderContext().error("Name must not be empty", ele);
valid = false;
}
//<alias>別名元素的alias屬性值為空
if (!StringUtils.hasText(alias)) {
getReaderContext().error("Alias must not be empty", ele);
valid = false;
}
if (valid) {
try {
//向容器的資源讀入器註冊別名
getReaderContext().getRegistry().registerAlias(name, alias);
}
catch (Exception ex) {
getReaderContext().error("Failed to register alias '" + alias +
"' for bean with name '" + name + "'", ele, ex);
}
//在解析完<Alias>元素之後,傳送容器別名處理完成事件
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
}
//解析Bean定義資源Document物件的普通元素
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// BeanDefinitionHolder是對BeanDefinition的封裝,即Bean定義的封裝類
//對Document物件中<Bean>元素的解析由BeanDefinitionParserDelegate實現 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//向Spring IoC容器註冊解析得到的Bean定義,這是Bean定義向IoC容器註冊的入口
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
//在完成向Spring IoC容器註冊解析得到的Bean定義之後,傳送註冊事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
(9)解析<Bean>元素:
//解析<Bean>元素的入口
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
//解析Bean定義資原始檔中的<Bean>元素,這個方法中主要處理<Bean>元素的id,name
//和別名屬性
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
//獲取<Bean>元素中的id屬性值
String id = ele.getAttribute(ID_ATTRIBUTE);
//獲取<Bean>元素中的name屬性值
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
////獲取<Bean>元素中的alias屬性值
List<String> aliases = new ArrayList<String>();
//將<Bean>元素中的所有name屬性值存放到別名中
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
//如果<Bean>元素中沒有配置id屬性時,將別名中的第一個值賦值給beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
//檢查<Bean>元素所配置的id或者name的唯一性,containingBean標識<Bean>
//元素中是否包含子<Bean>元素
if (containingBean == null) {
//檢查<Bean>元素所配置的id、name或者別名是否重複
checkNameUniqueness(beanName, aliases, ele);
}
//詳細對<Bean>元素中配置的Bean定義進行解析的地方
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
//如果<Bean>元素中沒有配置id、別名或者name,且沒有包含子//<Bean>元素,為解析的Bean生成一個唯一beanName並註冊
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
//如果<Bean>元素中沒有配置id、別名或者name,且包含了子//<Bean>元素,為解析的Bean使用別名向IoC容器註冊
beanName = this.readerContext.generateBeanName(beanDefinition);
//為解析的Bean使用別名註冊時,為了向後相容 //Spring1.2/2.0,給別名新增類名字尾
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
//當解析出錯時,返回null
return null;
}
//詳細對<Bean>元素中配置的Bean定義其他屬性進行解析,由於上面的方法中已經對//Bean的id、name和別名等屬性進行了處理,該方法中主要處理除這三個以外的其他屬性資料
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
//記錄解析的<Bean>
this.parseState.push(new BeanEntry(beanName));
//這裡只讀取<Bean>元素中配置的class名字,然後載入到BeanDefinition中去
//只是記錄配置的class名字,不做例項化,物件的例項化在依賴注入時完成
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
//如果<Bean>元素中配置了parent屬性,則獲取parent屬性的值
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
//根據<Bean>元素配置的class名稱和parent屬性值建立BeanDefinition
//為載入Bean定義資訊做準備
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//對當前的<Bean>元素中配置的一些屬性進行解析和設定,如配置的單態(singleton)屬性等
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//為<Bean>元素解析的Bean設定description資訊 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//對<Bean>元素的meta(元資訊)屬性解析
parseMetaElements(ele, bd);
//對<Bean>元素的lookup-method屬性解析
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//對<Bean>元素的replaced-method屬性解析
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析<Bean>元素的構造方法設定
parseConstructorArgElements(ele, bd);
//解析<Bean>元素的<property>設定
parsePropertyElements(ele, bd);
//解析<Bean>元素的qualifier屬性
parseQualifierElements(ele, bd);
//為當前解析的Bean設定所需的資源和依賴物件
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
//解析<Bean>元素出錯時,返回null
return null;
}
(10)解析<property>元素:
BeanDefinitionParserDelegate呼叫了parsePropertyElements方法來解析<Bean>中的<property>,
//解析<Bean>元素中的<property>子元素
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
//獲取<Bean>元素中所有的子元素
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//如果子元素是<property>子元素,則呼叫解析<property>子元素方法解析
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);
}
}
}
//解析<property>元素
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//獲取<property>元素的名字
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
//如果一個Bean中已經有同名的property存在,則不進行解析,直接返回。
//即如果在同一個Bean中配置同名的property,則只有第一個起作用
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
//解析獲取property的值
Object val = parsePropertyValue(ele, bd, propertyName);
//根據property的名字和值建立property例項
PropertyValue pv = new PropertyValue(propertyName, val);
//解析<property>元素中的屬性
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
//解析獲取property值
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
String elementName = (propertyName != null) ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element";
//獲取<property>的所有子元素,只能是其中一種型別:ref,value,list等
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//子元素不是description和meta屬性
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {//當前<property>元素包含有子元素
subElement = (Element) node;
}
}
}
//判斷property的屬性值是ref還是value,不允許既是ref又是value
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
//如果屬性是ref,建立一個ref的資料物件RuntimeBeanReference,這個物件
//封裝了ref資訊
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
//一個指向執行時所依賴物件的引用
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
//設定這個ref的資料物件是被當前的property物件所引用
ref.setSource(extractSource(ele));
return ref;
}
//如果屬性是value,建立一個value的資料物件TypedStringValue,這個物件
//封裝了value資訊
else if (hasValueAttribute) {
//一個持有String型別值的物件
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
//設定這個value資料物件是被當前的property物件所引用
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
//如果當前<property>元素還有子元素
else if (subElement != null) {
//解析<property>的子元素
return parsePropertySubElement(subElement, bd);
}
else {
//propery屬性中既不是ref,也不是value屬性,解析出錯返回null error(elementName + " must specify a ref or value", ele);
return null;
}
}
(10)解析<property>的子元素://解析<property>元素中ref,value或者集合等子元素
public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
//如果<property>沒有使用Spring預設的名稱空間,則使用使用者自定義的規則解析//內嵌元素
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
//如果子元素是bean,則使用解析<Bean>元素的方法解析
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
//如果子元素是ref,ref中只能有以下3個屬性:bean、local、parent
else if (nodeNameEquals(ele, REF_ELEMENT)) {
//獲取<property>元素中的bean屬性值,引用其他解析的Bean的名稱
//可以不再同一個Spring配置檔案中,具體請參考Spring對ref的配置規則
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
//獲取<property>元素中的local屬性值,引用同一個Xml檔案中配置
//的Bean的id,local和ref不同,local只能引用同一個配置檔案中的Bean
refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
if (!StringUtils.hasLength(refName)) {
//獲取<property>元素中parent屬性值,引用父級容器中的Bean
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean', 'local' or 'parent' is required for <ref> element", ele);
return null;
}
}
}
//沒有配置ref的目標屬性值
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
//建立ref型別資料,指向被引用的物件
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
//設定引用型別值是被當前子元素所引用
ref.setSource(extractSource(ele));
return ref;
}
//如果子元素是<idref>,使用解析ref元素的方法解析
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
//如果子元素是<value>,使用解析value元素的方法解析
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
//如果子元素是null,為<property>設定一個封裝null值的字串資料
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
//如果子元素是<array>,使用解析array集合子元素的方法解析
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
//如果子元素是<list>,使用解析list集合子元素的方法解析
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
//如果子元素是<set>,使用解析set集合子元素的方法解析
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
//如果子元素是<map>,使用解析map集合子元素的方法解析
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
//如果子元素是<props>,使用解析props集合子元素的方法解析
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
//既不是ref,又不是value,也不是集合,則子元素配置錯誤,返回null
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
(11)解析<list>元素,這裡用了遞迴的設計方法,可以借鑑
//解析<list>集合子元素
public List parseListElement(Element collectionEle, BeanDefinition bd) {
//獲取<list>元素中的value-type屬性,即獲取集合元素的資料型別
String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
//獲取<list>集合元素中的所有子節點
NodeList nl = collectionEle.getChildNodes();
//Spring中將List封裝為ManagedList
ManagedList<Object> target = new ManagedList<Object>(nl.getLength());
target.setSource(extractSource(collectionEle));
//設定集合目標資料型別
target.setElementTypeName(defaultElementType);
target.setMergeEnabled(parseMergeAttribute(collectionEle));
//具體的<list>元素解析
parseCollectionElements(nl, target, bd, defaultElementType);
return target;
}
//具體解析<list>集合元素,<array>、<list>和<set>都使用該方法解析
protected void parseCollectionElements(
NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) {
//遍歷集合所有節點
for (int i = 0; i < elementNodes.getLength(); i++) {
Node node = elementNodes.item(i);
//節點不是description節點
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
//將解析的元素加入集合中,遞迴呼叫下一個子元素
target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
}
}
}
最後,經過多次解析,IOC容器將xml形式定義的Bean檔案轉換成了BeanDefinition,下面就是向容器註冊Bean定義資訊才能完成初始化過程
(12)回到第(8)步,解析好的BeanDefinition被封裝成BeanDefinitionHold,然後使用了BeanDefinitionReaderUtils中的registerBeanDefinition方法向IOC容器註冊解析的Bean.
//將解析的BeanDefinitionHold註冊到容器中
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
//獲取解析的BeanDefinition的名稱
String beanName = definitionHolder.getBeanName();
//向IoC容器註冊BeanDefinition
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
//如果解析的BeanDefinition有別名,向容器為其註冊別名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}
使用BeanDefinitionReaderUtils時,完成註冊功能的是DefaultListableBeanFactory,因為它實現了BeanDefinitionRegistry這個介面,這個介面的實現完成了將BeanDefinition註冊到容器中,主要是放入hashMap中,如果在hashMap中遇到同名的bean,就要根據設定是否允許同名來設定。
//儲存註冊的俄BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
//向IoC容器註冊解析的BeanDefiniton
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");
//校驗解析的BeanDefiniton
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
//註冊的過程中需要執行緒同步,以保證資料的一致性
synchronized (this.beanDefinitionMap) {
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
//檢查是否有同名的BeanDefinition已經在IoC容器中註冊,如果已經註冊,
//並且不允許覆蓋已註冊的Bean,則丟擲註冊失敗異常
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else {//如果允許覆蓋,則同名的Bean,後註冊的覆蓋先註冊的
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
}
//IoC容器中沒有已經註冊同名的Bean,按正常註冊流程註冊
else {
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
this.beanDefinitionMap.put(beanName, beanDefinition);
//重置所有已經註冊過的BeanDefinition的快取
resetBeanDefinition(beanName);
}
}
進行到這裡,基本完成了IOC容器的初始化,下面進入依賴注入部分。
7、IOC依賴注入具體流程
(1)依賴注入的時間點
依賴注入主要有兩個時間點,第一個時間點是使用者通過getBean方法向IOC容器請求Bean會觸發依賴注入,第二個時間點是用於在<Bean>中配置了lazy-init屬性,這時一旦進行預例項化就會注入。
getBeans相關程式碼:
//獲取IoC容器中指定名稱的Bean
public Object getBean(String name) throws BeansException {
//doGetBean才是真正向IoC容器獲取被管理Bean的過程
return doGetBean(name, null, null, false);
}
//獲取IoC容器中指定名稱和型別的Bean
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
//doGetBean才是真正向IoC容器獲取被管理Bean的過程
return doGetBean(name, requiredType, null, false);
}
//獲取IoC容器中指定名稱和引數的Bean
public Object getBean(String name, Object... args) throws BeansException {
//doGetBean才是真正向IoC容器獲取被管理Bean的過程
return doGetBean(name, null, args, false);
}
//獲取IoC容器中指定名稱、型別和引數的Bean
public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
//doGetBean才是真正向IoC容器獲取被管理Bean的過程
return doGetBean(name, requiredType, args, false);
}
//真正實現向IoC容器獲取Bean的功能,也是觸發依賴注入功能的地方
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
//根據指定的名稱獲取被管理Bean的名稱,剝離指定名稱中對容器的相關依賴
//如果指定的是別名,將別名轉換為規範的Bean名稱
final String beanName = transformedBeanName(name);
Object bean;
//先從快取中取是否已經有被建立過的單態型別的Bean,對於單態模式的Bean整
//個IoC容器中只建立一次,不需要重複建立
Object sharedInstance = getSingleton(beanName);
//IoC容器建立單態模式Bean例項物件
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
//如果指定名稱的Bean在容器中已有單態模式的Bean被建立,直接返回
//已經建立的Bean
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
//獲取給定Bean的例項物件,主要是完成FactoryBean的相關處理
//注意:BeanFactory是管理容器中Bean的工廠,而FactoryBean是
//建立建立物件的工廠Bean,兩者之間有區別
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {//快取沒有正在建立的單態模式Bean
//快取中已經有已經建立的原型模式Bean,但是由於迴圈引用的問題導致實
//例化物件失敗
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//對IoC容器中是否存在指定名稱的BeanDefinition進行檢查,首先檢查是否
//能在當前的BeanFactory中獲取的所需要的Bean,如果不能則委託當前容器
//的父級容器去查詢,如果還是找不到則沿著容器的繼承體系向父級容器查詢
BeanFactory parentBeanFactory = getParentBeanFactory();
//當前容器的父級容器存在,且當前容器中不存在指定名稱的Bean
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
//解析指定Bean名稱的原始名稱
String nameToLookup = originalBeanName(name);
if (args != null) {
//委派父級容器根據指定名稱和顯式的引數查詢
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
//委派父級容器根據指定名稱和型別查詢
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
//建立的Bean是否需要進行型別驗證,一般不需要
if (!typeCheckOnly) {
//向容器標記指定的Bean已經被建立
markBeanAsCreated(beanName);
}
//根據指定Bean名稱獲取其父級的Bean定義,主要解決Bean繼承時子類
//合併父類公共屬性問題
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
//獲取當前Bean所有依賴Bean的名稱
String[] dependsOn = mbd.getDependsOn();
//如果當前Bean有依賴Bean
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
//遞迴呼叫getBean方法,獲取當前Bean的依賴Bean
getBean(dependsOnBean);
//把被依賴Bean註冊給當前依賴的Bean
registerDependentBean(dependsOnBean, beanName);
}
}
//建立單態模式Bean的例項物件
if (mbd.isSingleton()) {
//這裡使用了一個匿名內部類,建立Bean例項物件,並且註冊給所依賴的物件
sharedInstance = getSingleton(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
try {
//建立一個指定Bean例項物件,如果有父級繼承,則合併子//類和父類的定義
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
//顯式地從容器單態模式Bean快取中清除例項物件
destroySingleton(beanName);
throw ex;
}
}
});
//獲取給定Bean的例項物件
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//IoC容器建立原型模式Bean例項物件
else if (mbd.isPrototype()) {
//原型模式(Prototype)是每次都會建立一個新的物件
Object prototypeInstance = null;
try {
//回撥beforePrototypeCreation方法,預設的功能是註冊當前創//建的原型物件
beforePrototypeCreation(beanName);
//建立指定Bean物件例項
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
//回撥afterPrototypeCreation方法,預設的功能告訴IoC容器指//定Bean的原型物件不再建立了
afterPrototypeCreation(beanName);
}
//獲取給定Bean的例項物件
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
//要建立的Bean既不是單態模式,也不是原型模式,則根據Bean定義資源中
//配置的生命週期範圍,選擇例項化Bean的合適方法,這種在Web應用程式中
//比較常用,如:request、session、application等生命週期
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
//Bean定義資源中沒有配置生命週期範圍,則Bean定義不合法
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
//這裡又使用了一個匿名內部類,獲取一個指定生命週期範圍的例項
Object scopedInstance = scope.get(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
//獲取給定Bean的例項物件
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; " +
"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}