上文,我們看了IOC設計要點和設計結構;緊接著這篇,我們可以看下原始碼的實現了:Spring如何實現將資源設定(以xml設定為例)通過載入,解析,生成BeanDefination並註冊到IoC容器中的。@pdai
上文,我們看了IOC設計要點和設計結構;緊接著這篇,我們可以看下原始碼的實現了:Spring如何實現將資源設定(以xml設定為例)通過載入,解析,生成BeanDefination並註冊到IoC容器中的(就是我們圈出來的部分)
本文的目標就是分析Spring如何實現將資源設定(以xml設定為例)通過載入,解析,生成BeanDefination並註冊到IoC容器中的。
對於xml設定的Spring應用,在main()方法中範例化ClasspathXmlApplicationContext即可建立一個IoC容器。我們可以從這個構造方法開始,探究一下IoC容器的初始化過程。
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("aspects.xml", "daos.xml", "services.xml");
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, (ApplicationContext)null);
}
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
// 設定Bean資源載入器
super(parent);
// 設定設定路徑
this.setConfigLocations(configLocations);
// 初始化容器
if (refresh) {
this.refresh();
}
}
呼叫父類別容器AbstractApplicationContext的構造方法(super(parent)
方法)為容器設定好Bean資源載入器
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
// 預設建構函式初始化容器id, name, 狀態 以及 資源解析器
this();
// 將父容器的Environment合併到當前容器
this.setParent(parent);
}
通過AbstractApplicationContext預設建構函式初始化容器id, name, 狀態 以及 資源解析器
public AbstractApplicationContext() {
this.logger = LogFactory.getLog(this.getClass());
this.id = ObjectUtils.identityToString(this);
this.displayName = ObjectUtils.identityToString(this);
this.beanFactoryPostProcessors = new ArrayList();
this.active = new AtomicBoolean();
this.closed = new AtomicBoolean();
this.startupShutdownMonitor = new Object();
this.applicationStartup = ApplicationStartup.DEFAULT;
this.applicationListeners = new LinkedHashSet();
this.resourcePatternResolver = this.getResourcePatternResolver();
}
// Spring資源載入器
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
通過AbstractApplicationContext的setParent(parent)
方法將父容器的Environment合併到當前容器
public void setParent(@Nullable ApplicationContext parent) {
this.parent = parent;
if (parent != null) {
Environment parentEnvironment = parent.getEnvironment();
if (parentEnvironment instanceof ConfigurableEnvironment) {
this.getEnvironment().merge((ConfigurableEnvironment)parentEnvironment);
}
}
}
在設定容器的資源載入器之後,接下來FileSystemXmlApplicationContet執行setConfigLocations方法通過呼叫其父類別AbstractRefreshableConfigApplicationContext的方法進行對Bean定義資原始檔的定位
public void setConfigLocations(@Nullable 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) {
// 解析設定路徑
this.configLocations[i] = this.resolvePath(locations[i]).trim();
}
} else {
this.configLocations = null;
}
}
protected String resolvePath(String path) {
// 從上一步Environment中解析
return this.getEnvironment().resolveRequiredPlaceholders(path);
}
Spring IoC容器對Bean定義資源的載入是從refresh()函數開始的,refresh()是一個模板方法,refresh()方法的作用是:在建立IoC容器前,如果已經有容器存在,則需要把已有的容器銷燬和關閉,以保證在refresh之後使用的是新建立起來的IoC容器。refresh的作用類似於對IoC容器的重啟,在新建立好的容器中對容器進行初始化,對Bean定義資源進行載入。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}
這裡的設計上是一個非常典型的資源類載入處理型的思路,頭腦中需要形成如下圖的頂層思路(而不是隻停留在流水式的方法上面):
AbstractApplicationContext的obtainFreshBeanFactory()方法呼叫子類容器的refreshBeanFactory()方法,啟動容器載入Bean定義資原始檔的過程,程式碼如下:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 這裡使用了委派設計模式,父類別定義了抽象的refreshBeanFactory()方法,具體實現呼叫子類容器的refreshBeanFactory()方法
refreshBeanFactory();
return getBeanFactory();
}
AbstractApplicationContext類中只抽象定義了refreshBeanFactory()方法,容器真正呼叫的是其子類AbstractRefreshableApplicationContext實現的refreshBeanFactory()方法;
在建立IoC容器前,如果已經有容器存在,則需要把已有的容器銷燬和關閉,以保證在refresh之後使用的是新建立起來的IoC容器。方法的原始碼如下:
protected final void refreshBeanFactory() throws BeansException {
// 如果已經有容器存在,則需要把已有的容器銷燬和關閉,以保證在refresh之後使用的是新建立起來的IoC容器
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 建立DefaultListableBeanFactory,並呼叫loadBeanDefinitions(beanFactory)裝載bean定義
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory); // 對IoC容器進行客製化化,如設定啟動引數,開啟註解的自動裝配等
loadBeanDefinitions(beanFactory); // 呼叫載入Bean定義的方法,主要這裡又使用了一個委派模式,在當前類中只定義了抽象的loadBeanDefinitions方法,具體的實現呼叫子類容器
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
AbstractRefreshableApplicationContext中只定義了抽象的loadBeanDefinitions方法,容器真正呼叫的是其子類AbstractXmlApplicationContext對該方法的實現,AbstractXmlApplicationContext的主要原始碼如下:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 建立XmlBeanDefinitionReader,即建立Bean讀取器,並通過回撥設定到容器中去,容器使用該讀取器讀取Bean定義資源
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 設定上下文的環境,資源載入器、解析器
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 為Bean讀取器設定SAX xml解析器
// 允許子類自行初始化(比如校驗機制),並提供真正的載入方法
initBeanDefinitionReader(beanDefinitionReader); // 當Bean讀取器讀取Bean定義的Xml資原始檔時,啟用Xml的校驗機制
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
// 載入XML設定方式裡的Bean定義的資源
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
// 載入建構函式裡設定的Bean組態檔,即{"aspects.xml", "daos.xml", "services.xml"}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
Xml Bean讀取器(XmlBeanDefinitionReader)呼叫其父類別AbstractBeanDefinitionReader的 reader.loadBeanDefinitions方法讀取Bean定義資源。
由於我們使用ClassPathXmlApplicationContext作為例子分析,因此getConfigResources的返回值為null,因此程式執行reader.loadBeanDefinitions(configLocations)分支。
AbstractBeanDefinitionReader的loadBeanDefinitions方法原始碼如下:
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
}
// 模式匹配型別的解析器,這種方式是載入多個滿足匹配條件的資源
if (resourceLoader instanceof ResourcePatternResolver) {
try {
// 獲取到要載入的資源
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources); // 委派呼叫其子類XmlBeanDefinitionReader的方法,實現載入功能
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
}
return count;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// 只能通過絕對路徑URL載入單個資源.
Resource resource = resourceLoader.getResource(location);
int count = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
}
return count;
}
}
從對AbstractBeanDefinitionReader的loadBeanDefinitions方法原始碼分析可以看出該方法做了以下兩件事:
繼續看子類XmlBeanDefinitionReader的loadBeanDefinitions(Resource …)方法看到代表bean檔案的資源定義以後的載入過程。
/**
* 本質上是載入XML設定的Bean。
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource); // 將Bean定義資源轉換成Document物件
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
// 使用設定的DocumentLoader載入XML定義檔案為Document.
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
通過原始碼分析,載入Bean定義資原始檔的最後一步是將Bean定義資源轉換為Document物件,該過程由documentLoader實現
DocumentLoader將Bean定義資源轉換成Document物件的原始碼如下:
// 使用標準的JAXP將載入的Bean定義資源轉換成document物件
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
// 建立檔案解析器工廠
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}
// 建立檔案解析器
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
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) {
// Enforce namespace aware for 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;
}
該解析過程呼叫JavaEE標準的JAXP標準進行處理。
至此Spring IoC容器根據定位的Bean定義資原始檔,將其載入讀入並轉換成為Document物件過程完成。
接下來我們要繼續分析Spring IoC容器將載入的Bean定義資原始檔轉換為Document物件之後,是如何將其解析為Spring IoC管理的Bean物件並將其註冊到容器中的。
XmlBeanDefinitionReader類中的doLoadBeanDefinitions方法是從特定XML檔案中實際載入Bean定義資源的方法,該方法在載入Bean定義資源之後將其轉換為Document物件,接下來呼叫registerBeanDefinitions啟動Spring IoC容器對Bean定義的解析過程,registerBeanDefinitions方法原始碼如下:
// 按照Spring的Bean語意要求將Bean定義資源解析並轉換為容器內部資料結構
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// 解析過程入口,這裡使用了委派模式,具體的解析實現過程有實現類DefaultBeanDefinitionDocumentReader完成
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore; // 返回此次解析了多少個物件
}
// 建立BeanDefinitionDocumentReader物件,解析Document物件
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanUtils.instantiateClass(this.documentReaderClass);
}
/**
* Create the {@link XmlReaderContext} to pass over to the document reader.
*/
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
Bean定義資源的載入解析分為以下兩個過程:
按照Spring的Bean規則對Document物件解析的過程是在介面BeanDefinitionDocumentReader的實現類DefaultBeanDefinitionDocumentReader中實現的。
BeanDefinitionDocumentReader介面通過registerBeanDefinitions方法呼叫其實現類DefaultBeanDefinitionDocumentReader對Document物件進行解析,解析的程式碼如下:
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
// 註冊<beans/>設定的Beans
@SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...)
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
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);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
preProcessXml(root);
parseBeanDefinitions(root, this.delegate); // 從Document的根元素開始進行Bean定義的Document物件
postProcessXml(root);
this.delegate = parent;
}
/**
* 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) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
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);
}
}
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);
}
// 如果元素節點<Beans>元素,即它是巢狀型別的
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// 遞迴解析
doRegisterBeanDefinitions(ele);
}
}
解析Bean生成BeanDefinitionHolder的方法
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 註冊最終的裝飾範例
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
(這裡就不展開了,無非就是解析XML各種元素,來生成BeanDefinition)
Document物件的解析後得到封裝BeanDefinition的BeanDefinitionHold物件,然後呼叫BeanDefinitionReaderUtils的registerBeanDefinition方法向IoC容器註冊解析的Bean,BeanDefinitionReaderUtils的註冊的原始碼如下:
// 通過BeanDefinitionRegistry將BeanDefinitionHolder註冊到BeanFactory
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
當呼叫BeanDefinitionReaderUtils向IoC容器註冊解析的BeanDefinition時,真正完成註冊功能的是DefaultListableBeanFactory。
IOC容器本質上就是一個beanDefinitionMap, 註冊即將BeanDefinition put到map中
/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
/** Map from bean name to merged BeanDefinitionHolder. */
private final Map<String, BeanDefinitionHolder> mergedBeanDefinitionHolders = new ConcurrentHashMap<>(256);
@Override
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);
}
//重置所有已經註冊過的BeanDefinition的快取
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
至此,Bean定義資原始檔中設定的Bean被解析過後,已經註冊到IoC容器中,被容器管理起來,真正完成了IoC容器初始化所做的全部工作。現 在IoC容器中已經建立了整個Bean的設定資訊,這些BeanDefinition資訊已經可以使用,並且可以被檢索,IoC容器的作用就是對這些註冊的Bean定義資訊進行處理和維護。這些的註冊的Bean定義資訊是IoC容器控制反轉的基礎,正是有了這些註冊的資料,容器才可以進行依賴注入。
現在通過上面的程式碼,總結一下IOC容器初始化的基本步驟:
初始化的入口在容器實現中的 refresh()呼叫來完成
對 bean 定義載入 IOC 容器使用的方法是 loadBeanDefinition,其中的大致過程如下:
然後我們就可以通過 BeanFactory 和 ApplicationContext 來享受到 Spring IOC 的服務了,在使用 IOC 容器的時候,我們注意到除了少量粘合程式碼,絕大多數以正確 IoC 風格編寫的應用程式程式碼完全不用關心如何到達工廠,因為容器將把這些物件與容器管理的其他物件鉤在一起。基本的策略是把工廠放到已知的地方,最好是放在對預期使用的上下文有意義的地方,以及程式碼將實際需要存取工廠的地方。 Spring 本身提供了對宣告式載入 web 應用程式用法的應用程式上下文,並將其儲存在ServletContext 中的框架實現。
https://blog.csdn.net/qq_36212439/article/details/82749963
https://juejin.cn/post/6973884466171215908
https://juejin.cn/post/6844903838743265294
https://blog.csdn.net/hjing123/article/details/104867343
https://www.cnblogs.com/wl20200316/p/12522993.html
首先, 從Spring框架的整體架構和組成對整體框架有個認知。
其次,通過案例引出Spring的核心(IoC和AOP),同時對IoC和AOP進行案例使用分析。
基於Spring框架和IOC,AOP的基礎,為構建上層web應用,需要進一步學習SpringMVC。
Spring進階 - IoC,AOP以及SpringMVC的原始碼分析
ConcurrentHashMap<String, Object>
;並且BeanDefinition介面中包含了這個類的Class資訊以及是否是單例等。那麼如何從BeanDefinition中範例化Bean物件呢,這是本文主要研究的內容?