前言
最近學習了<<Spring原始碼深度解析>>受益匪淺,本部落格是對學習內容的一個總結、分享,方便日後自己複習或與一同學習的小夥伴一起探討之用.
建議與原始碼配合使用,效果更嘉,使用的spring版本為5.0.x: 官方原始碼下載 新增中文註解版原始碼
下面正文開始.
1. 容器的實現
本文要分享的內容就是從讀取配置檔案到註冊BeanDefinition的過程中spring到底做了怎樣的處理.
下面是一個具體的程式碼示例,首先建立一個物件
public class MyTestBean {
private String testStr = "testStr";
public String getTestStr() {
return testStr;
}
public void setTestStr(String testStr) {
this.testStr = testStr;
}
}
第二步建立配置檔案
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="myTestBean" class="bean.MyTestBean">
</beans>
第三步建立測試用例
public class BeanFactoryTest (
@Test
public void testSirnpleLoad() {
BeanFactory bf = new XmlBeanFactory (new ClassPathResource ("beanFactoryTest.xml"));
MyTestBean bean=(MyTestBean) bf.getBean("myTestBean");
System.out.println(bean.getTestStr());
}
}
執行後會正確輸出testStr.
本篇部落格的重點就是關注下列程式碼到底做了什麼
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
2. 原始碼分析
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
第一步通過ClassPathResource將xml檔案轉換為Resource物件.
new ClassPathResource("beanFactoryTest.xml")
這裡不再贅述,直接進入XmlBeanFactory這個類.
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
/**
* Create a new XmlBeanFactory with the given resource,
* which must be parsable using DOM.
* @param resource XML resource to load bean definitions from
* @throws BeansException in case of loading or parsing errors
*/
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
/**
* Create a new XmlBeanFactory with the given input stream,
* which must be parsable using DOM.
* @param resource XML resource to load bean definitions from
* @param parentBeanFactory parent bean factory
* @throws BeansException in case of loading or parsing errors
*/
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
}
這個類很簡單繼承了DefaultListableBeanFactory,有一個私有的成員變數XmlBeanDefinitionReader 和兩個構造方法.
可以看出XmlBeanFactory類本身並沒有複寫父類的方法,XmlBeanFactory與其父類的區別就在於使用私有成員變數XmlBeanDefinitionReader去讀取資原始檔.
進入XmlBeanFactory的構造方法
/**
* Create a new XmlBeanFactory with the given input stream,
* which must be parsable using DOM.
* @param resource XML resource to load bean definitions from
* @param parentBeanFactory parent bean factory
* @throws BeansException in case of loading or parsing errors
*/
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
有一點需要關注一下,就是在執行this.reader.loadBeanDefinitions(resource)前先呼叫了super(parentBeanFactory);
追蹤這行程式碼進入了XmlBeanFactory的父類DefaultListableBeanFactory
/**
* Create a new DefaultListableBeanFactory with the given parent.
* @param parentBeanFactory the parent BeanFactory
*/
public DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
super(parentBeanFactory);
}
發現又是一個super(parentBeanFactory),繼續追,又進入了DefaultListableBeanFactory的父類AbstractAutowireCapableBeanFactory
/**
* Create a new AbstractAutowireCapableBeanFactory with the given parent.
* @param parentBeanFactory parent bean factory, or {@code null} if none
*/
public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
this();
setParentBeanFactory(parentBeanFactory);
}
現在追進去this()方法,我們需要關注的東西就是裡面的ignoreDependencyInterface()方法
/**
* Create a new AbstractAutowireCapableBeanFactory.
*/
public AbstractAutowireCapableBeanFactory() {
super();
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
}
那麼這個方法是做什麼用的呢?
2.1 ignoreDependencyInterface()
首先先明確一下這個方法實現的功能是什麼,然後我們再追蹤一下程式碼
2.1.1 ignoreDependencyInterface()實現的功能
ignoreDependencyInterface的主要功能是忽略給定介面的自動裝配功能.
舉例來說當A中有屬性B.那麼當Spring在獲取A的Bean的時候如果其屬性B還沒有初始化,那麼Spring 會自動初始化B,這也是Spring中提供的一個重要特性。但是,某些情況
下,B不會被初始化,其中的一種情況就是B實現了BeanNameAware介面
2.1.2 原始碼部分
接下來看看原始碼部分是如何實現上述功能的
實現分為三個步驟:
- 存入(將需要過濾的介面存入集合)
- 過濾Bean的所有屬性(只要Bean的屬性實現了第一步存入的介面進行剔除,返回一個過濾後的屬性的集合)
- 對過濾後的屬性進行初始化
第一步存入原始碼,直接追蹤ignoreDependencyInterface()
/**
* Dependency interfaces to ignore on dependency check and autowire, as Set of
* Class objects. By default, only the BeanFactory interface is ignored.
*/
// set 專門存入需要需要過濾的類
private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<>();
/**
* Ignore the given dependency interface for autowiring.
* <p>This will typically be used by application contexts to register
* dependencies that are resolved in other ways, like BeanFactory through
* BeanFactoryAware or ApplicationContext through ApplicationContextAware.
* <p>By default, only the BeanFactoryAware interface is ignored.
* For further types to ignore, invoke this method for each type.
* @see org.springframework.beans.factory.BeanFactoryAware
* @see org.springframework.context.ApplicationContextAware
*/
public void ignoreDependencyInterface(Class<?> ifc) {
//放入集合
this.ignoredDependencyInterfaces.add(ifc);
}
將傳入的值方法一個set集合(對就是ignoredDependencyInterfaces這個集合).
第二步過濾部分的原始碼
/**
* Return an array of non-simple bean properties that are unsatisfied.
* These are probably unsatisfied references to other beans in the
* factory. Does not include simple properties like primitives or Strings.
* @param mbd the merged bean definition the bean was created with
* @param bw the BeanWrapper the bean was created with
* @return an array of bean property names
* @see org.springframework.beans.BeanUtils#isSimpleProperty
*/
protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
// 方法的第一個引數AbstractBeanDefinition mbd 可以理解為2.1.1舉的例子類A
// 宣告瞭一個集合
Set<String> result = new TreeSet<>();
// 拿到beanDefinition的所有屬性
PropertyValues pvs = mbd.getPropertyValues();
// 得到屬性的所有描述
PropertyDescriptor[] pds = bw.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
// 這裡遍歷所有屬性,可以理解為2.1.1舉的例子屬性B
// 過濾屬性的方法在!isExcludedFromDependencyCheck(pd)
if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
!BeanUtils.isSimpleProperty(pd.getPropertyType())) {
result.add(pd.getName());
}
}
// 最後返回過濾後的屬性
return StringUtils.toStringArray(result);
}
通過原始碼我們知道了具體過濾的邏輯 !isExcludedFromDependencyCheck(pd) 在這個方法中,繼續追蹤
/**
* Determine whether the given bean property is excluded from dependency checks.
* <p>This implementation excludes properties defined by CGLIB and
* properties whose type matches an ignored dependency type or which
* are defined by an ignored dependency interface.
* @param pd the PropertyDescriptor of the bean property
* @return whether the bean property is excluded
* @see #ignoreDependencyType(Class)
* @see #ignoreDependencyInterface(Class)
*/
protected boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {
return (AutowireUtils.isExcludedFromDependencyCheck(pd) ||
this.ignoredDependencyTypes.contains(pd.getPropertyType()) ||
// 這裡使用了第一步存入的集合
AutowireUtils.isSetterDefinedInInterface(pd, this.ignoredDependencyInterfaces));
}
看條件 AutowireUtils.isSetterDefinedInInterface(pd, this.ignoredDependencyInterfaces),這裡使用了我們第一步存入的集合,接近真相了,繼續追蹤
/**
* Return whether the setter method of the given bean property is defined
* in any of the given interfaces.
* @param pd the PropertyDescriptor of the bean property pd就是某個物件的一個屬性
* @param interfaces the Set of interfaces (Class objects)
* @return whether the setter method is defined by an interface
*/
public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set<Class<?>> interfaces) {
// 這裡得到B的set方法
Method setter = pd.getWriteMethod();
// 如果set方法不為空
if (setter != null) {
/*
* getDeclaringClass
* 該方法返回一個Class物件,返回當前class物件的宣告物件class,一般針對內部類的情況,比如A類有內部類B,那麼通過B.class.getDeclaringClass()方法將獲取到A的Class物件.
* 在使用反射物件時比如Method和Field的getDeclaringClass方法將獲取到所屬類物件
* */
// 所以這個targetClass 為所屬類物件
// 這裡得到了屬性B的class
Class<?> targetClass = setter.getDeclaringClass();
for (Class<?> ifc : interfaces) {
// 判斷 ifc 是否是屬性B的父類(這裡的ifc 則是第一步存入的介面)
// 如果 屬性B繼承了第一步存入的介面 並且 存入的介面也有相同的set方法,就會被過濾
if (ifc.isAssignableFrom(targetClass) &&
// 判斷類是否有指定的public方法;
ClassUtils.hasMethod(ifc, setter.getName(), setter.getParameterTypes())) {
return true;
}
}
}
return false;
}
看到上面的程式碼我們明白了過濾屬性的步驟:
- 得到屬性B的class
- 遍歷第一步存入的介面的集合
- 如果屬性B繼承了第一步存入的介面,並且存入的介面也有相同的set方法,就會被過濾
第二部分忽略的邏輯就完了
最後是第三部分使用,使用過濾後的屬性進行初始化
protected void autowireByType(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
// 過濾後的屬性是在這裡使用的
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
try {
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
// Don't try autowiring by type for type Object: never makes sense,
// even if it technically is a unsatisfied, non-simple property.
if (Object.class != pd.getPropertyType()) {
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
// Do not allow eager init for type matching in case of a prioritized post-processor.
boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
if (autowiredArgument != null) {
pvs.add(propertyName, autowiredArgument);
}
for (String autowiredBeanName : autowiredBeanNames) {
registerDependentBean(autowiredBeanName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +
propertyName + "' to bean named '" + autowiredBeanName + "'");
}
}
autowiredBeanNames.clear();
}
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
}
}
}
protected void autowireByName(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
// 過濾後的屬性是在這裡使用的
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
if (containsBean(propertyName)) {
Object bean = getBean(propertyName);
pvs.add(propertyName, bean);
registerDependentBean(propertyName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Added autowiring by name from bean name '" + beanName +
"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
"' by name: no matching bean found");
}
}
}
}
第二步過濾後的屬性是用來注入屬性的無論是通過name還是通過type注入. 經過一系列的追蹤我們證實了2.1.1節的功能實現.
2.2 XmlBeanDefinitionReader
構造方法解釋完畢了,進入正題,xmlBeanFactory使用私有成員變數XmlBeanDefinitionReader去載入資源
/**
* Create a new XmlBeanFactory with the given input stream,
* which must be parsable using DOM.
* @param resource XML resource to load bean definitions from
* @param parentBeanFactory parent bean factory
* @throws BeansException in case of loading or parsing errors
*/
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
進入this.reader.loadBeanDefinitions(),發現建立了一個EncodedResource物件,傳入loadBeanDefinitions()方法
/**
* Load bean definitions from the specified XML file.
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
進入loadBeanDefinitions(new EncodedResource(resource))方法
/**
* Load bean definitions from the specified XML file.
* @param encodedResource the resource descriptor for the XML file,
* allowing to specify an encoding to use for parsing the file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource);
}
// 獲得當前正在載入的資源
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
// 如果當前正在載入的資源已經擁有該元素,報迴圈載入的錯誤
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());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
這個方法主要做了兩個操作: 1. 檢測資源是否迴圈載入,如果是則丟擲異常 2. 對inputSource設定編碼.
繼續往下追蹤,進入doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法.
/**
* Actually load bean definitions from the specified XML file.
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
* @see #doLoadDocument
* @see #registerBeanDefinitions
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 通過inputSource, resource獲得文件物件
Document doc = doLoadDocument(inputSource, resource);
// 獲取到document後註冊BeanDefinition
return registerBeanDefinitions(doc, resource);
}
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);
}
}
doLoadBeanDefinitions(InputSource inputSource, Resource resource)方法通過inputSource、 resource獲得了Document這個方法就不展開解釋了,繼續往下追蹤進入了registerBeanDefinitions(doc, resource)
/**
* Register the bean definitions contained in the given DOM document.
* Called by {@code loadBeanDefinitions}.
* <p>Creates a new instance of the parser class and invokes
* {@code registerBeanDefinitions} on it.
* @param doc the DOM document
* @param resource the resource descriptor (for context information)
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of parsing errors
* @see #loadBeanDefinitions
* @see #setDocumentReaderClass
* @see BeanDefinitionDocumentReader#registerBeanDefinitions
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//1. 生成BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 返回註冊列表中註冊的bean的數量
int countBefore = getRegistry().getBeanDefinitionCount();
//2. 通過生成的BeanDefinitionDocumentReader註冊beanDefinition
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 這裡返回的是新註冊的bean的數量
//3. 返回最新的註冊的bean的數量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
registerBeanDefinitions(Document doc, Resource resource)主要做了三件事:
- 生成BeanDefinitionDocumentReader
private Class<? extends BeanDefinitionDocumentReader> documentReaderClass =
DefaultBeanDefinitionDocumentReader.class;
/**
* Create the {@link BeanDefinitionDocumentReader} to use for actually
* reading bean definitions from an XML document.
* <p>The default implementation instantiates the specified "documentReaderClass".
* @see #setDocumentReaderClass
*/
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanUtils.instantiateClass(this.documentReaderClass);
}
可以看出這裡生成的BeanDefinitionDocumentReader實際上是DefaultBeanDefinitionDocumentReader型別的
- 通過生成的BeanDefinitionDocumentReader註冊beanDefinition(也就是我們下一節要分析的主角)
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
這裡要留意一下createReaderContext(resource)方法
/**
* 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());
}
注意第五個引數this,相當於把自己(XmlBeanDefinitionReader)也傳遞給了BeanDefinitionDocumentReader,方便後續呼叫
- 返回最新的註冊的bean的數量
return getRegistry().getBeanDefinitionCount() - countBefore;
這裡的getRegistry()是哪裡來的,當時有點懵,找了一會才明白... 是這麼來的
public class XmlBeanFactory extends DefaultListableBeanFactory {
// 看XmlBeanDefinitionReader構造方法的引數this
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
/**
* Create a new XmlBeanFactory with the given resource,
* which must be parsable using DOM.
* @param resource XML resource to load bean definitions from
* @throws BeansException in case of loading or parsing errors
*/
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
/**
* Create a new XmlBeanFactory with the given input stream,
* which must be parsable using DOM.
* @param resource XML resource to load bean definitions from
* @param parentBeanFactory parent bean factory
* @throws BeansException in case of loading or parsing errors
*/
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
}
嗯... 答案就在XmlBeanDefinitionReader的構造方法中
/**
* Create new XmlBeanDefinitionReader for the given bean factory.
* @param registry the BeanFactory to load bean definitions into,
* in the form of a BeanDefinitionRegistry
*/
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}
這裡劃重點,因為後續得到的BeanDefinition還需要使用registry(實際上是XmlBeanFactory)去註冊BeanDefinition
XmlBeanDefinitionReader實現的功能到這裡告一段落了, 進入下一個類DefaultBeanDefinitionDocumentReader
2.3 DefaultBeanDefinitionDocumentReader
/**
* This implementation parses bean definitions according to the "spring-beans" XSD
* (or DTD, historically).
* <p>Opens a DOM Document; then initializes the default settings
* specified at the {@code <beans/>} level; then parses the contained bean definitions.
*/
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
這個很簡單沒什麼說的,通過document得到了Element,然後呼叫doRegisterBeanDefinitions(root)
/**
* Register each bean definition within the given root {@code <beans/>} element.
*/
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);
// 主要邏輯就是判斷這個配置檔案是否是允許存在,不存在則 return,不再浪費時間解析了
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
/*解析前處理,該方法為空實現*/
preProcessXml(root);
/*解析bean的定義*/
parseBeanDefinitions(root, this.delegate);
/*解析後處理,該方法為空實現*/
postProcessXml(root);
this.delegate = parent;
}
上述程式碼做了三件事:
- 方法中生成了一個BeanDefinitionParserDelegate,這個delegate有大用處,主要用來將Element解析成BeanDefinition(什麼是BeanDefinition? 可以理解為將xml中的 bean標籤 解析成一個物件,bean標籤的屬性與BeanDefinition的屬性一一對應).
- 對配置檔案的profile屬性的校驗
舉個例子,有多個profile(多個環境)的情況,如果 param-value標籤和profile的值能對應的情況才往下解析,否則就return了.
<!-- 開發環境配置檔案 -->
<beans profile="development">
<context:property-placeholder location="/WEB-INF/test-orm.properties" />
</beans>
<!-- 本地環境配置檔案 -->
<beans profile="test">
<context:property-placeholder location="/WEB-INF/local-orm.properties" />
</beans>
<!--在web.xml中新增-->
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>development</param-value>
</context-param>
- 有三個方法解析前處理、解析bean的定義、解析後處理,其中解析前處理和解析後處理兩個方法為空實現,如果需要做一些特殊的操作可以自行實現.
重點關注解析bean的定義parseBeanDefinitions(root, this.delegate)方法
/**
* 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);
}
}
進入解析bean的定義parseBeanDefinitions(root, this.delegate)方法,可以看到這裡進行了一個判斷,判斷這個element究竟是自定義標籤還是預設標籤,並且如果是預設標籤的情況還會進行遍歷,再次判斷是自定義標籤還是預設標籤,說明了預設標籤中有可能包含自定義標籤.
那什麼是預設標籤? 什麼是自定義標籤?
預設標籤是spring自己定義的,比如:
<bean id="test" class="test.TestBean">
自定義標籤:
<tx:annotation-driven>
這兩種標籤的解析方式有很大的不同. 我們先追蹤解析預設標籤的方法
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);
}
//beans標籤
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
可以看到有四種預設標籤,分別是: import標籤(匯入其他配置檔案)、alias標籤(設定別名)、bean標籤、beans標籤(巢狀的bean). 限於篇幅及重要程度,我們重點關注processBeanDefinition(ele, delegate)方法,理解了bean標籤的解析相信其他三類標籤也可以觸類旁通.
進入processBeanDefinition(ele, delegate)方法
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
/*
* 首先委託BeanDefinitionDelegate 類的parseBeanDefinitionElement 方法進行元素解析,
返回BeanDefinitionHolder 型別的例項bdHolder ,經過這個方法後,bdHolder 例項已經包含我
們配置檔案中配置的各種屬性了,例如class 、name、id 、alias 之類的屬性
* */
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
/*
* 當返回的bdHolder 不為空的情況下若存在預設標籤的子節點下再有自定義屬性,還需要
再次對自定義標籤進行解析
* */
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
/*
* 解析完成後,需要對解析後的bdHolder 進行註冊,同樣,註冊操作委託給了
* BeanDefinitionReaderUtils的registerBeanDefinition方法
* */
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));
}
}
這個方法主要做了四件事:
- 使用delegate解析Element生成BeanDefinitionHolder物件
- 當返回的bdHolder 不為空的情況下若存在預設標籤的子節點下再有自定義屬性,還需要再次對自定義標籤進行解析
- 解析完成後,需要對解析後的bdHolder 進行註冊
- 傳送註冊事件
接下來詳細分析一下這四個步驟
2.3.1 生成BeanDefinitionHolder物件
我們來看看BeanDefinitionHolder物件的生成過程
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
追蹤進去:
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
繼續:
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 1. 提取元素中的id 以及name 屬性。
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
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");
}
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 2. 進一步解析其他所有屬性並統一封裝至GenericBeanDefinition 型別的例項中。
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
//3 . 如果檢測到bean 沒有指定beanName ,那麼使用預設規則為此Bean 生成beanName
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
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);
// 4 . 將獲取到的資訊封裝到BeanDefinitionHolder 的例項中
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
這裡又做了四件事,具體執行的地方已經新增到上面原始碼的註釋中了:
- 提取元素中的id 以及name 屬性。
- 進一步解析其他所有屬性並統一封裝至GenericBeanDefinition 型別的例項中。
- 如果檢測到bean 沒有指定beanName ,那麼使用預設規則為此Bean 生成beanName 。
- 將獲取到的資訊封裝到BeanDefinitionHolder 的例項中
這裡我們重點關注第二步的程式碼
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
這一步實現的功能就是將xml配置檔案中bean標籤的屬性都放到GenericBeanDefinition 型別的例項中。
進入 parseBeanDefinitionElement(ele, beanName, containingBean)方法:
/**
* 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.
*/
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
// 解析class 屬性
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
//解析parent 屬性
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 創始用於承載屬性的AbstractBeanDefinition 型別的GenericBeanDefintiion
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 硬編碼解析預設bean的各種屬性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 解析後設資料
parseMetaElements(ele, bd);
// 解析lookup-method屬性
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析replace-method 屬性
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析建構函式引數
parseConstructorArgElements(ele, bd);
// 解析property子元素
parsePropertyElements(ele, bd);
//解析Qualifier子元素
parseQualifierElements(ele, bd);
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();
}
return null;
}
可以看到先生成了AbstractBeanDefinition物件(實際上是GenericBeanDefinition)
// 1. 生成AbstractBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 2. 追蹤createBeanDefinition(className, parent);
/**
* Create a bean definition for the given class name and parent name.
* @param className the name of the bean class
* @param parentName the name of the bean's parent bean
* @return the newly created bean definition
* @throws ClassNotFoundException if bean class resolution was attempted but failed
*/
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}
// 3. 繼續追蹤BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
/**
* Create a new GenericBeanDefinition for the given parent name and class name,
* eagerly loading the bean class if a ClassLoader has been specified.
* @param parentName the name of the parent bean, if any
* @param className the name of the bean class, if any
* @param classLoader the ClassLoader to use for loading bean classes
* (can be {@code null} to just register bean classes by name)
* @return the bean definition
* @throws ClassNotFoundException if the bean class could not be loaded
*/
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
// 真相了,實際上是GenericBeanDefinition
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
生成了AbstractBeanDefinition物件之後,就該解析bean標籤的各種屬性到AbstractBeanDefinition,例如: meta、replace-method、lookup-method、constructor-arg等等,這裡我們就以解析建構函式引數(constructor-arg)方法為例,看看spring到底是如何解析bean的屬性的
//1.分析的示例, 解析建構函式
parseConstructorArgElements(ele, bd);
// 2.追蹤進去
/**
* Parse constructor-arg sub-elements of the given bean element.
*/
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
// 得到<bean>標籤的所有節點
NodeList nl = beanEle.getChildNodes();
// 遍歷所有節點
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 第一個條件 node是Element型別並且(node是預設標籤或node的父結點不是預設標籤)
// 第二個條件這個節點是constructor-arg
if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
// 進入預設標籤解析
parseConstructorArgElement((Element) node, bd);
}
}
}
//3. isCandidateElement(node) 條件追蹤
private boolean isCandidateElement(Node node) {
return (node instanceof Element && (isDefaultNamespace(node) || !isDefaultNamespace(node.getParentNode())));
}
進一步閱讀原始碼前,我們先簡單瞭解一下constructor-arg的使用,方便我們更好的理解原始碼,示例:
<bean id="provider" class="com.apress.prospring.ch4.ConfigurableMessageProvider">
<!--index代表構造方法引數的順序, 這裡畫重點-->
<constructor-arg index="0">
<value>first parameter</value>
</constructor-arg>
<constructor-arg index="1">
<value>second parameter</value>
</constructor-arg>
</bean>
當判斷Element是constructor-arg正式進入parseConstructorArgElement((Element) node, bd)構造方法解析:
/**
* Parse a constructor-arg element.
*/
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
// 得到index屬性
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
// 得到type屬性
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
// 得到name屬性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 當index屬性存在
if (StringUtils.hasLength(indexAttr)) {
try {
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
}
else {
try {
this.parseState.push(new ConstructorArgumentEntry(index));
//解析ele 對應的屬性元素
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
///不允許重複指定相同引數
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
error("Ambiguous constructor-arg entries for index " + index, ele);
}
else {
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
}
}
finally {
this.parseState.pop();
}
}
}
catch (NumberFormatException ex) {
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
}
}
else {
// index屬性不存在,自動尋找
try {
this.parseState.push(new ConstructorArgumentEntry());
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
上述程式碼會使用ConstructorArgumentValues.ValueHolder 型別來封裝解析出來的元素,將type 、name 和index(上面if/esle的判斷區別在這,如果有index屬性會一起封裝進去,沒有會忽略) 屬性一併封裝在ConstructorArgumentValues.ValueHolder 型別中並新增至當前BeanDefinition 的constructorArgumentValues的indexedArgumentValues屬性中
再關注一下上述程式碼中解析constructor-arg的屬性值的方法
/**
* Get the value of a property element. May be a list etc.
* Also used for constructor arguments, "propertyName" being null in this case.
*/
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// Should only have one child element: ref, value, list, etc.
// 一個屬性只能對應一種型別:ref 、value 、list 等
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
// 這個node 是constructor-arg標籤的子標籤如<value>、<list>、<map>等
Node node = nl.item(i);
// /對應description 或者meta 不處理
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
// Child element is what we're looking for.
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {
subElement = (Element) node;
}
}
}
// 解析constructor-arg上的ref屬性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
/*
*
1. 同時既有ref屬性又有value屬性
2 . 存在ref 屬性或者value 屬性且又有子元素
* 報錯
* */
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
if (hasRefAttribute) {
// ref 屬性的處理,使用RuntimeBeanReference 封裝對應的ref 名稱
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
else if (hasValueAttribute) {
// value 屬性的封裝,使用TypedStringValue 封裝
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
else if (subElement != null) {
// 解析子元素
return parsePropertySubElement(subElement, bd);
}
else {
// Neither child element nor "ref" or "value" attribute found.
// 既沒有ref 也沒有value 也沒有子元素,Spring 蒙圈了
error(elementName + " must specify a ref or value", ele);
return null;
}
}
從程式碼上來看,對建構函式中屬性元素的解析,經歷了以下幾個過程。
-
略過description 或者meta 。
-
提取constructor-arg 上的ref和 value 屬性,以便於根據規則驗證正確性,其規則為在
constructor-arg 上不存在以下情況。
a. 同時既有ref 屬性又有value 屬性。
b. 存在ref 屬性或者value 屬性且又有子元素。 -
ref 屬性的處理。使用RuntimeBeanReference 封裝對應的ref名稱,如:
<constructor-arg ref="a">
- value 屬性的處理。使用TypedStringValue 封裝,如:
<constructor-arg value="a">
- 子元素的處理
<constructor-arg>
<map>
<entry key="key" value="value"/>
</map
</constructor-arg>
解析建構函式引數的方法到這裡就結束了.bean的其他屬性的解析也是大同小異的.
這裡再提一下replace-method和lookup-method屬性的解析(不明白這兩個屬性幹什麼用的,請自行百度一下)
以lookup-method屬性為例
// 解析lookup-method屬性
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
/**
* Parse lookup-override sub-elements of the given bean element.
*/
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
Element ele = (Element) node;
// 獲取要要修飾的方法
String methodName = ele.getAttribute(NAME_ATTRIBUTE);
// 獲取配置返回的bean
String beanRef = ele.getAttribute(BEAN_ELEMENT);
LookupOverride override = new LookupOverride(methodName, beanRef);
override.setSource(extractSource(ele));
// 注意這行程式碼,將override新增到了AbstractBeanDefinition 中的methodOverrides 屬性中
// 後續需要對這個override進行校驗
overrides.addOverride(override);
}
}
}
replace-method和lookup-method屬性解析後獲得LookupOverride,然後將override新增到了AbstractBeanDefinition 中的methodOverrides屬性中,後續需要對這個override進行校驗.
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
上述程式碼中的重要邏輯,已經分析完了,再回憶一下delegate.parseBeanDefinitionElement(ele)都做了那四件事:
- 提取元素中的id 以及name 屬性。
- 進一步解析其他所有屬性並統一封裝至GenericBeanDefinition 型別的例項中。
- 如果檢測到bean 沒有指定beanName ,那麼使用預設規則為此Bean 生成beanName 。
- 將獲取到的資訊封裝到BeanDefinitionHolder 的例項中
現在已經生成並返回了BeanDefinitionHolder, 接下來回到processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)方法的第二步: 當返回的bdHolder 不為空的情況下若存在預設標籤的子節點下再有自定義屬性,還需要再次對自定義標籤進行解析
2.3.2 BeanDefinitionHolder的自定義標籤解析
預設標籤中含有自定義標籤的示例:
<bean id="test" class="testMyClass">
<mybean:user username=""aaa" />
<lbean>
重新看看processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)方法
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
/*
* 首先委託BeanDefinitionDelegate 類的parseBeanDefinitionElement 方法進行元素解析,
返回BeanDefinitionHolder 型別的例項bdHolder ,經過這個方法後,bdHolder 例項已經包含我
們配置檔案中配置的各種屬性了,例如class 、name、id 、alias 之類的屬性
* */
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
/*
* 當返回的bdHolder 不為空的情況下若存在預設標籤的子節點下再有自定義屬性,還需要
再次對自定義標籤進行解析
* */
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
/*
* 解析完成後,需要對解析後的bdHolder 進行註冊,同樣,註冊操作委託給了
* BeanDefinitionReaderUtils的registerBeanDefinition方法
* */
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));
}
}
我們接下來要分析的方法就是
/*
* 當返回的bdHolder 不為空的情況下若存在預設標籤的子節點下再有自定義屬性,還需要
再次對自定義標籤進行解析
* */
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
追蹤:
/**
* Decorate the given bean definition through a namespace handler, if applicable.
* @param ele the current element
* @param originalDef the current bean definition
* @return the decorated bean definition
*/
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
/*
* 這裡將函式中第三個引數設定為空,那麼第三個引數是做什麼用的呢?什麼情況下不為空
呢?其實這第三個引數是父類bean ,當對某個巢狀配置進行分析時,這裡需要傳遞父類
beanDefinition 。分析原始碼得知這裡傳遞的引數其實是為了使用父類的scope 屬性,以備子類若
沒有設定scope 時預設使用父類的屬性,這裡分析的是頂層配置,所以傳遞null
* */
return decorateBeanDefinitionIfRequired(ele, originalDef, null);
}
繼續:
/**
* Decorate the given bean definition through a namespace handler, if applicable.
* @param ele the current element
* @param originalDef the current bean definition
* @param containingBd the containing bean definition (if any)
* @return the decorated bean definition
*/
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
BeanDefinitionHolder finalDefinition = originalDef;
// Decorate based on custom attributes first.
//遍歷所有的屬性,看看是否有適用於修飾的屬性
NamedNodeMap attributes = ele.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
// Decorate based on custom nested elements.
// 邊歷所有的子節點,看看是否有適用於修飾的子元素
NodeList children = ele.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
}
return finalDefinition;
}
看原始碼,這裡進行了兩次遍歷裝飾,第一次是對所有屬性進行遍歷,第二次是對所有子節點進行裝飾.
進入裝飾的方法decorateIfRequired(node, finalDefinition, containingBd):
/**
* Decorate the given bean definition through a namespace handler,
* if applicable.
* @param node the current child node
* @param originalDef the current bean definition
* @param containingBd the containing bean definition (if any)
* @return the decorated bean definition
*/
public BeanDefinitionHolder decorateIfRequired(
Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
// 根據節點獲取到namespaceUri
String namespaceUri = getNamespaceURI(node);
// 如果namespaceUri不為空 同時不是預設的名稱空間
if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
// 獲得名稱空間解析器解析namespaceUri 得到NamespaceHandler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
// 如果處理器不為空
if (handler != null) {
// 進行裝飾
BeanDefinitionHolder decorated =
handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
if (decorated != null) {
return decorated;
}
}
else if (namespaceUri.startsWith("http://www.springframework.org/")) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
}
else {
// A custom namespace, not to be handled by Spring - maybe "xml:...".
if (logger.isDebugEnabled()) {
logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
}
}
}
// 最後返回(裝飾好的)BeanDefinitionHolder
return originalDef;
}
該方法的大致流程就是:
- 先獲取到namespaceUri
- 根據名稱空間解析器解析namespaceUri獲取到NamespaceHandler
- 使用NamespaceHandler對BeanDefinitionHolder進行裝飾
- 返回裝飾後的BeanDefinitionHolder
現在我們也明白了自定義標籤的解析流程了
2.3.3 註冊BeanDefinition
到了到了,我們已經獲取到了BeanDefinitionHolder,並且如果BeanDefinitionHolder中含有自定義標籤的話又進行了一次解析.現在已經得到了最終的BeanDefinitionHolder,接下來就是要將BeanDefinitionHolder註冊了(快要結束了).
// Register the final decorated instance.
/*
* 解析完成後,需要對解析後的bdHolder 進行註冊,同樣,註冊操作委託給了
* BeanDefinitionReaderUtils的registerBeanDefinition方法
* */
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
/**
* Register the given bean definition with the given bean factory.
* @param definitionHolder the bean definition including name and aliases
* @param registry the bean factory to register with
* @throws BeanDefinitionStoreException if registration failed
*/
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
// beanName註冊
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);
}
}
}
registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法有兩個引數,第一個引數是我們剛剛獲得到BeanDefinitionHolder,而第二個引數這個 getReaderContext().getRegistry() 實際上就是XMLBeanFactory本身.
registerBeanDefinition方法本身有兩種註冊方式,第一種是通過beanName註冊,第二種是通過別名註冊
通過BenName註冊
// 實現方法是DefaultListableBeanFactory類的registerBeanDefinition
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
@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 {
/*
* 1. 校驗: 註冊前的最後一次校驗,這裡的校驗不同於之前的XML檔案校驗,
* 主要是對於AbstractBeanDefinition屬性中的methodOverrides校驗,
* 校驗methodsOverrides是否與工廠方法並存或者methodsOverrides對應的方法根本不存在
* */
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// 2. 嘗試獲取BeanDefinition
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
// 3. 對beanName 已經註冊的情況的處理。如果設定了不允許bean 的覆蓋,則需要丟擲
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + existingDefinition + "] bound.");
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isWarnEnabled()) {
logger.warn("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.isInfoEnabled()) {
logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
// 註冊bean就是將它放到下面的map裡
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// 4. 如果從快取中得到的BeanDefinition為null,判斷是否已經開始建立,如果是則給快取加上鎖新增到快取,否則直接新增到快取
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
// 因為beanDefinitionMap 是全域性變數,這裡會存在併發訪問的情況
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;
// this.manualSingletonNames 手工註冊的單例物件的名稱列表,按註冊順序排列
// 從this.manualSingletonNames中移除BeanName
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
// 5. 清除解析之前留下的對應beanName的快取
if (existingDefinition != null || containsSingleton(beanName)) {
// 重置beanName的快取
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
上面的程式碼中我們看到,在對於bean 的註冊處理方式上,主要進行了幾個步驟。
- 對AbstractBeanDefinition的屬性methodOverrides的校驗.
- 通過beanName從快取中獲取BeanDefinition
- 如果從快取中得到的BeanDefinition不為null, 對beanName 已經註冊的情況的處理。如果設定了不允許bean 的覆蓋,則需要丟擲異常
- 如果從快取中得到的BeanDefinition為null,判斷是否已經開始建立,如果是則給快取加上鎖新增到快取,否則直接新增到快取
- 清除解析之前留下的對應beanName的快取
接下來是通過別名註冊
registry.registerAlias(beanName, alias);
@Override
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
synchronized (this.aliasMap) {
///如果beanName 與alias 相同的話不記錄alias ,並刪除對應的alias
if (alias.equals(name)) {
this.aliasMap.remove(alias);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
}
else {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
// 如果alias 不允許被被蓋則丟擲異常
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
if (logger.isInfoEnabled()) {
logger.info("Overriding alias '" + alias + "' definition for registered name '" +
registeredName + "' with new target name '" + name + "'");
}
}
// 當A->B 存在時,若再次出現 A->C->B時則會丟擲異常
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
由以上程式碼中可以得知,註冊alias的步驟如下:
I. alias與beanName相同情況處理。若alias與beanName 名稱相同則不需要處理並刪
除掉原有alias。
2. alias覆蓋處理。若aliasName已經使用井已經指向了另一beanName則需要使用者的設定
進行處理。
3. alias迴圈檢查。當 A-> B 存在時,若再次出現A->C->B時候則會丟擲異常。
4. 註冊alias
註冊完成後就是最後一步了通知監昕器解析及註冊完成:
通過程式碼getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder))
完成此工作,這裡的實現只為擴充套件,當程式開發人員需要對註冊BeanDefinition 事件進行監聽時可以通過註冊監聽器的方式並將處理邏輯寫入監聽器中,目前在Spring 中並沒有對此事件做任何邏輯處理.
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
這一行程式碼確實做了很多很多的事情,我們追尋著原始碼對整個流程有了一個大概的瞭解,接下來做個小小的總結
3.總結
經過了一系列的操作,終於將BeanDefinition註冊好了,在這段旅程中我們會究竟使用了哪些類?每個類的功能是什麼?現在做一個總結
1 XmlBeanFactory
- 在XmlBeanFactory的構造方法中呼叫了ignoreDependencyInterface方法
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
保證了通過XMLBeanFactory獲得的Bean如果有屬性實現了BeanNameAware.class、BeanFactoryAware.class、BeanClassLoaderAware.class的話,該屬性不會被初始化.而統一有框架處理.
- 然後就是獲得BeanDefinitionHolder之後還需要通過XmlBeanFactory去註冊
2. XmlBeanDefinitionReader
通過XmlBeanDefinitionReader載入Resource,對Resource進行編碼,生成Document物件.
3. DefaultBeanDefinitionDocumentReader
- 通過Document物件獲取Element,對Element進行校驗(profile屬性的校驗)
- 解析前處理、解析後處理(空實現,如有需要自行實現)
- 解析處理
A. 預設標籤的處理
a. 四類預設標籤的處理(import 、alias、bean、beans)
i. bean標籤的處理 -> 使用BeanDefinitionParserDelegate解析Element得到BeanDefinitionHolder
ii. 如果BeanDefinitionHolder還有自定義標籤還需要進行自定義標籤的解析
B. 自定義標籤的處理
4. BeanDefinitionReaderUtils
生成好最終的BeanDefinitionHolder呼叫BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())方法註冊beanDefinition.程式碼的實現實際上就是呼叫XMLBeanFactory進行註冊.
4. 下回預告
BeanFactory bf = new XmlBeanFactory (new ClassPathResource ("beanFactoryTest.xml"));
MyTestBean bean=(MyTestBean) bf.getBean("myTestBean");
解析及註冊beanDefinition的到這裡就告一段落了,下一篇Bean的載入,讓我們拭目以待!