前言
最近学习了<<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的加载,让我们拭目以待!