在對refreshBeanFactory()詳細解析以前,先來看看spring的applicationContext的繼承樹: java
ResourceLoader並不能將其當作是Spring獨有的功能,spring Ioc只是藉助於ResourceLoader來實現資源加載。也提供了各類各樣的資源加載方式:node
DefaultResourceLoader 首先檢查資源路徑是否以classpath:前綴打頭,若是是,則嘗試構造ClassPathResource類 型資源並返回。不然, 嘗試經過URL,根據資源路徑來定位資源spring
FileSystemResourceLoader 它繼承自Default-ResourceLoader,但覆寫了getResourceByPath(String)方法,使之從文件系統加載資源並以 FileSystemResource類型返回數組
spring與ResourceLoader之間的關係緩存
全部ApplicationContext的具體實現類都會直接或者間接地實現AbstractApplicationContext,AbstactApplicationContext 依賴了了DeffaultResourceLoader, ApplicationContext 繼承了ResourcePatternResolver,所到頭來ApplicationContext的具體實現類都會具備DefaultResourceLoader 和 PathMatchingResourcePatterResolver的功能。這也就是會什麼ApplicationContext能夠實現統一資源定位。bash
接下來進入正題: refreshBeanFactory() 刷新beanfactory // AbstractRefreshableApplicationContext.java - 120app
@Override
protected final void refreshBeanFactory() throws BeansException {
// 若是 ApplicationContext 中已經加載過 BeanFactory 了,銷燬全部 Bean,關閉 BeanFactory
// 注意,應用中 BeanFactory原本就是能夠多個的,這裏可不是說應用全局是否有 BeanFactory,而是當前ApplicationContext 是否有 BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 初始化一個 DefaultListableBeanFactory,爲何用這個,咱們立刻說。
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 用於 BeanFactory 的序列化
beanFactory.setSerializationId(getId());
// 設置 BeanFactory 的兩個配置屬性:是否容許 Bean定義覆蓋、是否容許bean之間的循環引用
//bean定義覆蓋的屬性默認是true,重複了會直接覆蓋
customizeBeanFactory(beanFactory);
// 加載 Bean 到 BeanFactory 中
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
} catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
複製代碼
整個過程主要分爲了三個步驟:ide
1.resource定位
2.beanDefinition的載入。
3.向註冊中心註冊beanDefinition的過程(將beanDefinition放入緩存)。
複製代碼
// AbstractXmlApplicationContext.java L80post
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 給這個 BeanFactory 實例化一個 XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 初始化 BeanDefinitionReader,其實這個是提供給子類覆寫的,
initBeanDefinitionReader(beanDefinitionReader);
// 重點來了,繼續往下
loadBeanDefinitions(beanDefinitionReader);
}
複製代碼
讀取配置的操做在 XmlBeanDefinitionReader 中,其負責加載配置、解析。ui
// AbstractXmlApplicationContext.java 120
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//若是調用的構造器是new ClassPathXmlApplicationContext(String path, Class<?> clazz),獲取其路徑下的資源
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
//若是構造器調用的的是 new ClassPathXmlApplicationContext(String configLocation),獲取其資源
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
複製代碼
這裏兩個分支都是在加載bean實例,過程相似,只分析reader.loadBeanDefinitions(configResources);
// AbstractBeanDefinitionReader.java L177
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
for (Resource resource : resources) {
//這裏用了典型的模板模式,實際調用的是XmlBeanDefinitionReader#loadBeanDefinitions
counter += loadBeanDefinitions(resource);
}
return counter;
}
複製代碼
// XmlBeanDefinitionReader.java L314
/**
* Load bean definitions from the specified XML file.
* 從xml里加載bean實例
*/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
...
//從threadlocal中獲取資源set
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
//將resource放入set中
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
//獲取當前
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//此處調用388行的doLoadBeanDefinitions()
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
//此處省略處理異常、清楚鏈表和threadload相關代碼
...
}
複製代碼
接下來繼續看//XmlBeanDefinitionReader.java L388的實現
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//校驗xml文件的是xsd 仍是 dtd格式的,而後調用 DefaultDocumentLoader#doLoadDocument將流解析成document對象
Document doc = doLoadDocument(inputSource, resource);
//將document和資源封裝成bean實例
return registerBeanDefinitions(doc, resource);
}
//省略若干異常處理
...
}
複製代碼
// XmlBeanDefinitionReader.java L505
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//構建DefaultBeanDefinitionDocumentReader的實例由documentReader負責解析xml並封裝到beandefinition裏
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//BeanDefinitionRegistry 負責將bean實例註冊到bean工廠
int countBefore = getRegistry().getBeanDefinitionCount();
//下面會詳細展開說明
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
複製代碼
通過艱難險阻,磕磕絆絆,咱們終於到了核心邏輯的底部doRegisterBeanD巳finitions(root) , 至少咱們在這個方法中看到了但願。 若是說之前一直是XML 加載解析的準備階段,那麼doRegisterBeanDefinitions 算是真正地 開始進行解析了,咱們期待的核心部分真正開始了:
//DefaultBeanDefinitionDocumentReader.java L116
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
//處理profile
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}
}
//模板方法模式,讓子類去實現,利於用戶本身拓展
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
//模板方法模式,讓子類去實現,利於用戶本身拓展
postProcessXml(root);
this.delegate = parent;
}
複製代碼
繼續跟蹤parseBeanDefinitions(root, this.delegate); //DefaultBeanDefinitionDocumentReader.java L157
/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
* @param root the DOM root element of the document
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//判斷當前元素是不是beans節點
if (delegate.isDefaultNamespace(root)) {
//beans
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
//循環beans下面的節點
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
//自定義節點
delegate.parseCustomElement(ele);
}
}
}
}
else {
//自定義的元素
delegate.parseCustomElement(root);
}
}
複製代碼
從上面的代碼,咱們能夠看到,對於每一個配置來講,分別進入到 parseDefaultElement(ele, delegate); 和 delegate.parseCustomElement(ele); 這兩個分支了。
parseDefaultElement(ele, delegate) 表明解析的節點是 、、、 這幾個。 這裏的四個標籤之因此是 default 的,是由於它們是處於這個 namespace 下定義的:www.springframework.org/schema/bean…
而對於其餘的標籤,將進入到 delegate.parseCustomElement(element) 這個分支。如咱們常常會使用到的 、、、等。
回過神來,看看處理 default 標籤的方法:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
//import
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
//alias
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
//bean 建立bean的入口
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// beans
doRegisterBeanDefinitions(ele);
}
}
複製代碼
這裏咱們不會對每個標籤進行解析,僅僅對最重要的進行跟蹤 解析的入口: //DefaultBeanDefinitionDocumentReader.java L294
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 將 <bean /> 節點中的信息提取出來,而後封裝到一個 BeanDefinitionHolder 中,細節往下看
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//對自定義屬性進行解析。忽略
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 註冊bean
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// bean註冊完成後發送事件,可是這裏是空實現,留給用戶擴展
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
複製代碼
// BeanDefinitionParserDelegate L437
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<String>();
//將 name 屬性的定義按照 」逗號、分號、空格「切分,造成一個別名列表數組,
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
//// 若是沒有指定id, 那麼用別名列表的第一個名字做爲beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 根據 <bean ...>...</bean> 中的配置建立 BeanDefinition,而後把配置中的信息都設置到實例中,
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
// 到這裏,整個 <bean /> 標籤就算解析結束了,一個 BeanDefinition 就造成了。
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
// 若是都沒有設置 id 和 name,那麼此時的 beanName 就會爲 null,進入下面這塊代碼產生生成相應的別名,能夠忽略
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
// 返回 BeanDefinitionHolder
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
複製代碼
==// BeanDefinitionParserDelegate.java#parseBeanDefinitionElement L522==
/**
* Parse the bean definition itself, without regard to name or aliases. May return
* {@code null} if problems occurred during the parsing of the bean definition.
*/
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
//parseState裏維護了一個棧,保證先進後出
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
//取出class屬性
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
//取出parent屬性
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
//用class 和parent建立一個GenericBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 設置 BeanDefinition 的一堆屬性,這些屬性定義在 AbstractBeanDefinition 中
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
/**
* 下面的一堆是解析 <bean>......</bean> 內部的子元素,
* 解析出來之後的信息都放到 bd 的屬性中
*/
// 解析 <meta />
parseMetaElements(ele, bd);
// 解析 <lookup-method />
parseLookupOverrideSubElements(ele,bd.getMethodOverrides());
// 解析 <replaced-method />
parseReplacedMethodSubElements(ele,bd.getMethodOverrides());
// 解析 <constructor-arg />
parseConstructorArgElements(ele, bd);
// 解析 <property />
parsePropertyElements(ele, bd);
// 解析 <qualifier />
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
finally {
this.parseState.pop();
}
return null;
}
複製代碼
到這裏,咱們已經完成了根據 配置建立了一個 BeanDefinitionHolder 實例。 咱們回到解析的入口: //DefaultBeanDefinitionDocumentReader.java L294
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 將 <bean /> 節點中的信息提取出來,而後封裝到一個 BeanDefinitionHolder 中,細節往下看
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//對自定義屬性進行解析。忽略
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 註冊bean
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// bean註冊完成後發送事件,可是這裏是空實現,留給用戶擴展
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
複製代碼
接下來是註冊bean實例的過程——
//BeanDefinitionReaderUtils#registerBeanDefinition L143
/**
* Register the given bean definition with the given bean factory.
*/
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
String beanName = definitionHolder.getBeanName();
//調用BeanDefinitionRegistry實現類註冊以beanName爲key,bean實例做爲value存入緩存中
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 若是還有別名的話,也要根據別名通通註冊一遍,否則根據別名就找不到 Bean 了
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
// alias -> beanName 保存它們的別名信息,這個很簡單,用一個 map 保存一下就能夠了,
// 獲取的時候,會先將 alias 轉換爲 beanName,而後再查找
registry.registerAlias(beanName, alias);
}
}
}
複製代碼
這裏BeanDefinitionRegistry的實現類用DefaultListableBeanFactory 方法比較長,爲了便於閱讀,我將一些異常處理等非核心方法省略 //DefaultListableBeanFactory#registerBeanDefinition L793
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
...
BeanDefinition oldBeanDefinition;
// 從beanDefinitionMap緩存中取,以後會看到,全部的 Bean 註冊後會放入這個 beanDefinitionMap 中
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
//allowBeanDefinitionOverriding customizeBeanFactory(beanFactory) 裏能夠設置這個屬性,默認是true,
if (!isAllowBeanDefinitionOverriding()) {
//不容許覆蓋,拋異常
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
...打印相關日誌
//將beanName做爲key,beanDefinition做爲value放入map中,覆蓋舊值
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
//該beanName未被建立
//判斷alreadyCreated鏈表是否爲空===判斷是否已經有其餘的 Bean 開始初始化
// 注意,"註冊Bean" 這個動做結束,Bean 依然尚未初始化,咱們後面會有大篇幅說初始化過程,
// 在 Spring 容器啓動的最後,會 預初始化 全部的 singleton beans
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
//爲了集合迭代的穩定性,不能直接操做元集合(不能增長和刪除元素個數)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
//新建一個臨時集合來實現新增的beanname
//beanDefinitionNames 預註冊的bean實例的beanName集合
List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
//manualSingletonNames 手動註冊單例的beanName集合
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// 這是正常的開始
// 將 BeanDefinition 放到這個 map 中,這個 map 保存了全部的 BeanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
// 這是個 ArrayList,因此會按照 bean 配置的順序保存每個註冊的 Bean 的名字
this.beanDefinitionNames.add(beanName);
// 這是個 LinkedHashSet,表明的是手動註冊的 singleton bean,
// 注意這裏是 remove 方法,到這裏的 Bean 固然不是手動註冊的
// 手動指的是經過調用如下方法註冊的 bean :registerSingleton(String beanName, Object singletonObject)
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
複製代碼
總結一下,到這裏已經初始化了 Bean 容器, 配置也相應的轉換爲了一個個 BeanDefinition,而後註冊了各個 BeanDefinition 到了註冊中心