Spring Framework
- 發(fā)布于2002年10月1日
強大的基于 JavaBeans 的采用控制反轉(zhuǎn)(Inversion of Control,IoC)原則的配置管理,使得應(yīng)用程序的組建更加簡易快捷东囚。
數(shù)據(jù)庫事務(wù)的一般化抽象層涝涤,允許聲明式(Declarative)事務(wù)管理器备恤,簡化事務(wù)的劃分使之與底層無關(guān)蚓耽。
內(nèi)建的針對 JTA 和單個 JDBC 數(shù)據(jù)源的一般化策略栅葡,使Spring的事務(wù)支持不要求 Java EE 環(huán)境,這與一般的 JTA 或者 EJB CMT 相反沧踏。
JDBC 抽象層提供了有針對性的異常等級(不再從 SQL 異常中提取原始代碼)歌逢,簡化了錯誤處理,大大減少了程序員的編碼量翘狱。再次利用 JDBC 時秘案,你無需再寫出另一個'終止'(finally)模塊。并且面向 JDBC 的異常與 Spring 通用數(shù)據(jù)訪問對象(Data Access Object)異常等級相一致潦匈。
以資源容器阱高,DAO 實現(xiàn)和事務(wù)策略等形式與 Hibernate,JDO 和 MyBatis 茬缩、SQL Maps 集成赤惊。利用控制反轉(zhuǎn)機制全面解決了許多典型的 Hibernate 集成問題。所有這些全部遵從 Spring 通用事務(wù)處理和通用數(shù)據(jù)訪問對象異常等級規(guī)范凰锡。
靈活的基于核心 Spring 功能的 MVC 網(wǎng)頁應(yīng)用程序框架未舟。開發(fā)者通過策略接口將擁有對該框架的高度控制,因而該框架將適應(yīng)于多種呈現(xiàn)(View)技術(shù)掂为,例如 JSP裕膀、FreeMarker、Velocity勇哗、Thymeleaf 等昼扛。值得注意的是,Spring 中間層可以輕易地結(jié)合于任何基于 MVC 框架的網(wǎng)頁層智绸,例如 Struts野揪、WebWork 或 Tapestry。
提供諸如事務(wù)管理等服務(wù)的AOP框架瞧栗。
在設(shè)計應(yīng)用程序 Model 時斯稳,MVC模式(例如 Struts)通常難于給出一個簡潔明了的框架結(jié)構(gòu)。Spring 卻具有能夠讓這部分工作變得簡單的能力迹恐。程序開發(fā)員們可以使用Spring的JDBC抽象層重新設(shè)計那些復(fù)雜的框架結(jié)構(gòu)挣惰。
上述是維基百科給出的Spring核心功能模塊定義,我們可以看出Spring做了什么有哪些用處。Spring是一個BeanFactory,負責(zé)加載Bean和管理Bean的生命周期與依賴關(guān)系這也就是IOC控制反轉(zhuǎn),它還支持AOP以便支持切面編程,Spring MVC給出了一個簡單的MVC模式范例很方便的支持日常用到的貧血模型,以及Spring Boot可以支持簡化配置殴边。
BeanFactory
既然知道了Spring中IOC的核心組件時BeanFactory,那么我們來了解一下什么是BeanFactory憎茂。見詞曉意一個組件工廠,用來生產(chǎn)組件和獲得組件。
public interface BeanFactory {
//用&MyFactory獲得工廠,&為表示工廠的前綴
String FACTORY_BEAN_PREFIX = "&";
//通過名字或別名獲取Bean
Object getBean(String name) throws BeansException;
//獲取時要匹配類型requiredType需要是超類或者繼承的接口
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
//args使用顯示參數(shù)創(chuàng)建Bean時需要用到的參數(shù)
Object getBean(String name, Object... args) throws BeansException;
//通過類型獲取Bean
<T> T getBean(Class<T> requiredType) throws BeansException;
//類型和參數(shù)
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
//返回一個Bean提供程序允許檢測可用性和唯一性支持懶加載,就是一個ObjectFactory
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
//根據(jù)name查找是否含有Bean定義和單例Bean,如果是分層的工廠則查找超類
boolean containsBean(String name);
//是否是單例的們也會遞歸查找 PS:單例是每次調(diào)用都可以返回相同實例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
//Spring支持的原型類型也就是每次調(diào)用都返回一個新的實例
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
//檢查是否可以返回一個給定意義的對象,ResolvableType中定義了類型
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
//Class版本的type
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
//拿到類型,對于FactoryBean返回工廠生產(chǎn)的Bean實例類型,默認會初始化工廠
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
//allow詢問要不要初始化工廠
Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
//返回別名,原始名稱為數(shù)組中的第一個元素
String[] getAliases(String name);
上述就是BeanFactory全部方法,先是一些重載的getBean各種類型如用name獲取,或者用requiredType獲取再是獲取ObjectProvider再者是檢測有沒有這個Bean,Bean的類型與生成規(guī)則,最后是拿到這個Bean的Type與別名锤岸∈#可以看出這個工廠是Spring最核心的組件。
什么是耦合呢?為什么使用new會造成耦合?
//一句簡單的泛型,接口持有子類引用
ProfileService profileService = new ProfileServiceImpl();
上述代碼為題在哪里呢?其實也沒啥問題,如果是寫個小項目完全沒有任何問題有時候我們需要替換Impl時直接Ctrl+F定位Impl然后正則替換!完結(jié)撒花,Spring結(jié)束!
開個玩笑,假設(shè)項目中有很多中這樣的impl很常見的,我們在替換時需要替換每一個地方的,但是有些地方有沒有什么可以快速替換的方式呢?有的
(XXXImpl)BeanFactroy.getBean(XXX);
好處時什么呢?我們可以直接在Bean工廠中替換Impl實現(xiàn),如我們采用注解的方式實現(xiàn)一個接口,在實現(xiàn)類Impl上標(biāo)明采用這個實現(xiàn)類,如果下一次想換一個那么把這個注解移到另一個類就可以,這樣子對代碼中侵入性修改就可以降低到最小是偷。
ApplicationContext 應(yīng)用上下文
從上圖中,我們可以很明顯看出Application繼承自BeanFactory,Listable是可以一次獲取多個Bean,BeanFactory的方法中都是獲取單個,Hierarchical,可以有繼承關(guān)系的BeanFactory拳氢。應(yīng)用程序中常見的兩個Appcontext有ClassPathXmlApplicationContext,AnnotationConfigApplicationContext
上述最要中的觀察點已經(jīng)標(biāo)識,程序中兩種最重要的上下文可以采用注解和Xml兩種形式,ApplicationContext啟動過程中會負責(zé)創(chuàng)建Bean與Bean的依賴注入。
分析ApplicationContext的啟動過程
我們從ClassPathXmlApplicationContext的源碼中分析context的啟動過程,進入源碼中發(fā)現(xiàn)只有200多行美滋滋蛋铆。
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
@Nullable
private Resource[] configResources;
public ClassPathXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
//處理配置文件數(shù)組
setConfigLocations(configLocations);
if (refresh) {
//核心方法刷新
refresh();
}
}
1.setConfigLocations
setConfigLocations是超類AbstractRefreshableConfigApplicationContext中的方法用于解析配置文件路徑,處理成配置文件數(shù)組馋评。也就是一個String[]configLocations,先判斷下非空再對Path進行trim去除空白符再resolvePath進行解析。
2.refresh()
這個方法也是由超類實現(xiàn)的值得一提的是為什么叫refresh而不是init那因為可以重新調(diào)用這個方法來進行銷毀重建,具體代碼如下
public void refresh() throws BeansException, IllegalStateException {
// 加鎖以防止refresh沒結(jié)束又進行一個刷新
synchronized (this.startupShutdownMonitor) {
// 準(zhǔn)備上下文用于刷新
prepareRefresh();
// 刷新內(nèi)部Bean工廠,解析配置文件生成Bean定義注冊到BeanFactory
// 沒有進行初始化Bean
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 設(shè)置BeanFactory的類加載器 添加幾個BeanPostProcessor手動注冊一些單例Bean
prepareBeanFactory(beanFactory);
try {
// 擴展點,如果類實現(xiàn)了這個這個方法可以再容器初始化后做些什么
postProcessBeanFactory(beanFactory);
// 調(diào)用processors
invokeBeanFactoryPostProcessors(beanFactory);
// 注冊BeanPostProcessors
// 如果Bean實現(xiàn)了接口的兩個方法postProcessBeforeInitialization和postProcessAfterInitialization
//那么在Bean初始化的過程之前和之后會進行調(diào)用
registerBeanPostProcessors(beanFactory);
// 初始化當(dāng)前context的信息源
initMessageSource();
// 初始化時間廣播器
initApplicationEventMulticaster();
// 初始化一些特殊的Bean
onRefresh();
// 注冊事件監(jiān)聽器
registerListeners();
// 初始化所有單例懶加載的除外
finishBeanFactoryInitialization(beanFactory);
// 最后一步廣播初始化完成事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 發(fā)生異常則銷毀創(chuàng)建的單例
destroyBeans();
// 重置active標(biāo)志位為false
cancelRefresh(ex);
// 異常拋出去
throw ex;
}
finally {
//設(shè)置內(nèi)省緩存,可能不在需要單例的元數(shù)據(jù)
resetCommonCaches();
}
}
}
這個refresh內(nèi)容蠻多的,具體概括一下就是刷新一下BeanFactory加載新的Bean定義再初始化一些特殊Bean與注冊事件監(jiān)聽器初始化單例,消除單例Bean的元數(shù)據(jù),下面我們簡單分析一下每個調(diào)用語句刺啦。
創(chuàng)建BeanFactory之前的準(zhǔn)備工作
protected void prepareRefresh() {
// 設(shè)置啟動時間與標(biāo)志位
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
// 初始化占位符屬性源
initPropertySources();
// 校驗標(biāo)記文件是否可以解析
getEnvironment().validateRequiredProperties();
// 存儲earlyApplicationListeners到applicationListeners,并新建一個
// Store pre-refresh ApplicationListeners...
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
}
創(chuàng)建Bean容器,加載Bean并注冊
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//刷新BeanFactroy如果有舊的就關(guān)閉再創(chuàng)建個新的
//加載Bean定義與注冊Bean
refreshBeanFactory();
//返回新的beanFactory
return getBeanFactory();
}
//在AbstractRefreshableApplicationContext有這個方法的實現(xiàn)
protected final void refreshBeanFactory() throws BeansException {
//如果加載過BeanFactory就銷毀掉所有Bean并關(guān)閉BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//創(chuàng)建一個DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
//設(shè)置序列化ID
beanFactory.setSerializationId(getId());
// 定制一下BeanFactory允許Bean的覆蓋與循環(huán)引用
customizeBeanFactory(beanFactory);
// 加載Bean定義
loadBeanDefinitions(beanFactory);
//設(shè)置beanFactory
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
方法很簡單主要是為什么BeanFactory要使用DefaultListableBeanFactory,這個類繼承了所有實現(xiàn),被設(shè)計成一個默認的BeanFactory
loadBeanDefinitions加載BeanDefinition
什么是BeanDefinition?Bean的定義保存了Bean的元信息指向哪個類,是否是單例要不要懶加載Bean依賴于哪些Bean
BeanDefinition定義
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
//默認提供兩種類型單例與原型
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
//Bean的角色
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
// Modifiable attributes
//設(shè)置父Bean也就是繼承父Bean的配置信息
void setParentName(@Nullable String parentName);
// 獲取父Bean
@Nullable
String getParentName();
// 設(shè)置Bean的類名
void setBeanClassName(@Nullable String beanClassName);
// 獲取Bean的類名
@Nullable
String getBeanClassName();
// 設(shè)置作用域
void setScope(@Nullable String scope);
// 獲取作用域
@Nullable
String getScope();
// 設(shè)置懶加載
void setLazyInit(boolean lazyInit);
boolean isLazyInit();
// 設(shè)置依賴的Bean 重點BeanFactory可以保證這些依賴的Bean可以在他前面初始化
void setDependsOn(@Nullable String... dependsOn);
// 返回依賴Bean名稱
@Nullable
String[] getDependsOn();
// 設(shè)置是否可以注入到其他Bean只影響類型注入
// 顯示的名稱注入依然有效
void setAutowireCandidate(boolean autowireCandidate);
boolean isAutowireCandidate();
// 設(shè)置Primary 同一個接口多個實現(xiàn)不指定名稱會選擇主要那個
void setPrimary(boolean primary);
boolean isPrimary();
// 如果是工廠模式生產(chǎn)的就設(shè)置一下工廠
void setFactoryBeanName(@Nullable String factoryBeanName);
@Nullable
String getFactoryBeanName();
// 用哪個工廠方法
void setFactoryMethodName(@Nullable String factoryMethodName);
@Nullable
String getFactoryMethodName();
// 構(gòu)造器參數(shù)值
ConstructorArgumentValues getConstructorArgumentValues();
// 構(gòu)造器是否是有參的
default boolean hasConstructorArgumentValues() {
return !getConstructorArgumentValues().isEmpty();
}
// Bean中的屬性值
MutablePropertyValues getPropertyValues();
default boolean hasPropertyValues() {
return !getPropertyValues().isEmpty();
}
// 設(shè)置初始化方法名稱
void setInitMethodName(@Nullable String initMethodName);
@Nullable
String getInitMethodName();
// 設(shè)置銷毀方法名稱
void setDestroyMethodName(@Nullable String destroyMethodName);
@Nullable
String getDestroyMethodName();
// 設(shè)置角色
void setRole(int role);
int getRole();
// 提供人可以讀的描述
void setDescription(@Nullable String description);
@Nullable
String getDescription();
// 只讀屬性
// 返回可解析類型
ResolvableType getResolvableType();
boolean isSingleton();
boolean isPrototype();
boolean isAbstract();
@Nullable
String getResourceDescription();
@Nullable
BeanDefinition getOriginatingBeanDefinition();
}
定義了Bean的作用域,構(gòu)造器,Bean生成規(guī)則,初始化和銷毀方法
回到refreshBeanFactory() 中剩下還有兩個重要方法
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
//是否允許Bean定義覆蓋
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//是否允許循環(huán)依賴
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
Spring的默認值是同一配置文件不允許覆蓋,不同則可以留特。而默認情況下Spring允許循環(huán)依賴,構(gòu)造方法的循環(huán)依賴不允許
加載BeanDefinition
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 實例化一個XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 允許子類提供BeanDefinitionReader的自定義初始化
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
loadBeanDefinitions 很重要的方法用是初始化的Reader開始加載Bean定義
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
// 使用其他重載的方法以進行加載
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int count = 0;
// 簡單的遍歷加載然后返回加載個數(shù)
for (Resource resource : resources) {
count += loadBeanDefinitions(resource);
}
return count;
}
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
// 用ThreadLocal來存放配置文件資源
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//核心加載方法
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//XML轉(zhuǎn)DOM
Document doc = doLoadDocument(inputSource, resource);
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);
}
}
// 具體的parse解析方法有很多種實現(xiàn),這里不展開了如果學(xué)了編譯原理應(yīng)該無壓力
public void parse(XMLInputSource source) throws XNIException, IOException {
if (fParseInProgress) {
// REVISIT - need to add new error message
throw new XNIException("FWK005 parse may not be called while parsing.");
}
fParseInProgress = true;
try {
setInputSource(source);
parse(true);
} catch (XNIException ex) {
if (PRINT_EXCEPTION_STACK_TRACE)
ex.printStackTrace();
throw ex;
} catch (IOException ex) {
if (PRINT_EXCEPTION_STACK_TRACE)
ex.printStackTrace();
throw ex;
} catch (RuntimeException ex) {
if (PRINT_EXCEPTION_STACK_TRACE)
ex.printStackTrace();
throw ex;
} catch (Exception ex) {
if (PRINT_EXCEPTION_STACK_TRACE)
ex.printStackTrace();
throw new XNIException(ex);
} finally {
fParseInProgress = false;
// close all streams opened by xerces
this.cleanup();
}
}
public boolean parse(boolean complete) throws XNIException, IOException {
//
// reset and configure pipeline and set InputSource.
if (fInputSource != null) {
try {
fValidationManager.reset();
fVersionDetector.reset(this);
fConfigUpdated = true;
resetSymbolTable();
resetCommon();
short version = fVersionDetector.determineDocVersion(fInputSource);
if (version == Constants.XML_VERSION_1_1) {
initXML11Components();
configureXML11Pipeline();
resetXML11();
} else {
configurePipeline();
reset();
}
// mark configuration as fixed
fConfigUpdated = false;
// resets and sets the pipeline.
fVersionDetector.startDocumentParsing((XMLEntityHandler) fCurrentScanner, version);
fInputSource = null;
} catch (IOException | RuntimeException ex) {
if (PRINT_EXCEPTION_STACK_TRACE)
ex.printStackTrace();
throw ex;
} catch (Exception ex) {
if (PRINT_EXCEPTION_STACK_TRACE)
ex.printStackTrace();
throw new XNIException(ex);
}
}
try {
return fCurrentScanner.scanDocument(complete);
} catch (IOException | RuntimeException ex) {
if (PRINT_EXCEPTION_STACK_TRACE)
ex.printStackTrace();
throw ex;
} catch (Exception ex) {
if (PRINT_EXCEPTION_STACK_TRACE)
ex.printStackTrace();
throw new XNIException(ex);
}
}
經(jīng)過如上的漫長方法,可以把配置文件轉(zhuǎn)換成DOM樹上面僅僅是轉(zhuǎn)換成DOM樹下面介紹下解析DOM樹
DefaultBeanDefinitionDocumentReader
protected void doRegisterBeanDefinitions(Element root) {
// 方法可以遞歸調(diào)用 在parseDefaultElement中調(diào)用了doRegisterBeanDefinitions
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
//判斷一下是否是根節(jié)點
//URL里是否含有BEANS_NAMESPACE_URI
if (this.delegate.isDefaultNamespace(root)) {
// 讀取beans的profile
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
//按照 ,;對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;
}
}
}
//鉤子函數(shù)
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
// 解析文檔中 import alias bean等元素
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)) {
//解析Default
parseDefaultElement(ele, delegate);
}
else {
//解析定制的
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
public boolean isDefaultNamespace(@Nullable String namespaceUri) {
return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri));
}
/**
* Determine whether the given node indicates the default namespace.
*/
public boolean isDefaultNamespace(Node node) {
return isDefaultNamespace(getNamespaceURI(node));
}
public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";
可以看出默認的Namespace也就是要測試namespaceUri是否是BEANS_NAMESPACE_URI
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byName" >
上述為一段典型的xml定義,上面有就使用到BEANS_NAMESPACE_URI,也就是解析XML中的各個節(jié)點,常見的有import,bean,aop,context,mvc,區(qū)別在于BEANS_NAMESPACE_URI中定義的是默認的其他的是定制的。
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/>
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//解析<nested/>
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
對importBeanDefinitionResource進行一個淺析,讀取一下元素再查看一下import里面有沒有resuouce然后對引入資源進行加載
<import resource = "xxxxx.xml">
public static final String RESOURCE_ATTRIBUTE = "resource";
//簡述
protected void importBeanDefinitionResource(Element ele) {
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
-----省略
}
總結(jié)
對今天的BeanFactory解析進行一個總結(jié),我們深入探討了BeanFactory的模型與ApplicationContext,知道了上下文的refresh方法用于生成Bean定義與銷毀重加載BeanFactory,對BeanDefinition進行了深入了解和解析XML文件,最后分析了一下XML文件生成的DOM樹如何解析并對一些簡單的如<import resuource = "">
進行了淺顯的解析已推出XML文件的解析大致流程玛瘸。
今天時間有限先對Spring框架做一個大致了解,知道什么是BeanFactory和ApplicationContext即可蜕青。朋友們走過路過不要忘記點贊,你的點贊是我更新的動力。