之前 探索SpringBoot
系列也是到了 探索SpringBoot-一起看看Spring源码之Resource(十)
。之前有提到过 Spring
容器最重要的阶段分为三个,分别是 Bean的发现,读取,注册
。今天我们来看看 Bean
的注册。
因为有了之前文章的铺垫,直接看 XmlBeanDefinitionReader#registerBenDefinitions
//对之前在准备工作中得到的Document和Resource进行解析和注册Bean
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException{
//使用默认的DefaultBeanDefinitionDocumentReader来实例化BeanDefinitionDocumentReader
// Read document based on new BeanDefinitionDocumentReader SPI.
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//在初始化XmlBeanDefinitionReader的时候,会默认将BeanFactory传递进入成为BeanDefinitionRegistry
int countBefore = getRegistry().getBeanDefinitionCount();
//加载及注册Bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//记录本次加载的BeanDefinition的个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
复制代码
经过了一系列的准备工作之后,我们终于进入到了实际加载和注册 Bean
的阶段。让我们继续进入到 DefaultDocumentBeanDefinitionDocumentReader#registerBeanDefinitions(Document,XmlReaderContext)
中。
//进入到DefaultDocumentReader中对BeanDefinition进行解析
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
//将readerContext上下文赋值给自己的属性
this.readerContext = readerContext;
//打印一个日志
logger.debug("Loading bean definitions");
//获得root
Element root = doc.getDocumentElement();
//获得专门的解析类
BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
//解析前处理,留给子类进行处理
preProcessXml(root);
parseBeanDefinitions(root, delegate);
//解析后处理,留给子类进行处理
postProcessXml(root);
}
复制代码
在这个函数中,主要做了两件事情。
preProcessXml(root)
和 postProcessXml(root)
方法都留给子类进行实现,如果子类需要在处理过程的前面和后面需要做一些处理。对设计模式熟悉一些,那么可以知道这里使用了 模板模式
。
继续看看 parseBeanDefinitions(Element,BeanDefinitionParserDelegate)
//解析root元素
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//如果root是默认的元素
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 {
//如果root是定制的元素
delegate.parseCustomElement(root);
}
}
复制代码
在 XML
配置里面有两大类声明,一个是默认的如
<bean id="test" class="test.TestBean"> 复制代码
另一类就是自定义的,如
<tx:annotation-driven/> 复制代码
Spring对于默认的标签和自定义的标签的解析有很大的不同,下面重点先解析一下默认的标签。进入到 DefaultBeanDefinitionDocumentReader#parseDefaultElement
中。
//解析默认的元素
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);
}
}
复制代码
我们先看 bean
元素的解析,毕竟这是使用的最多的元素,也是 IoC
容器的核心过程。让我们进入到 DefaultBeanDefinitionDocumentReader#processBeanDefinition(Element,BeanDefinitionParserDelegate)
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//使用委托类解析元素为bdHolder
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//装饰bdHolder
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//委托给BeanDefinitionReaderUtils注册BeanDefinition到registry中
// Register the final decorated instance.
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));
}
}
复制代码
上述代码大致做了这么几件事情。
id
, name
配置信息 BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element)
。 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
复制代码
继续进入都函数内部。 代码可能会有点长。但是,已经到了最关键的时候了,我们要有点耐心。有耐心,才能理解别人不能理解的东西。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
//获取Id
String id = ele.getAttribute(ID_ATTRIBUTE);
//获取name
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//将name转化放入到aliases的别名数组中
List<String> aliases = new ArrayList<String>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
//id就是beanName
String beanName = id;
//对id为空的情况,做一些默认的补救措施。逻辑是aliase去掉第一个元素,将aliase的第一个元素作为beanName
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);
}
//继续深入解析该元素的其他属性
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
//不为null,表示正常解析
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
//自己生成beanName
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);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
复制代码
上面函数主要做了下面几个事情。
id
和 name
属性 BeanDefinition
中 beanName
,那么默认生成一个 beanName
BeanDefinitionHolder
的实例中
在上面的代码注释中,我稍微分析了下得到 id
和 name
属性的过程,然后让我们继续深入看一下第2步的函数去。让我们看看到底是怎么得到一个 beanDefinition
的。 public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
//如果有class的值,那么直接将className赋值
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
//判断父类的节点
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
//实例化BeanDefinition对象
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//解析基本的BeanDefinition属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//解析描述符
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//解析meta元素
parseMetaElements(ele, bd);
//解析look-up子元素
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//解析replaceMethod子元素
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析构造器元素
parseConstructorArgElements(ele, bd);
//解析property元素
parsePropertyElements(ele, bd);
//解析Qulifier元素
parseQualifierElements(ele, bd);
//设置资源
bd.setResource(this.readerContext.getResource());
//设置source
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;
}
复制代码
可以看到上述代码好了好多事情,能够清楚的看到自己在 XML
中配置的元素是如何被解析的。
className
属性值 BeanDefinition
并将相关的属性赋值上去 BeanDefinition
是一个接口,定义了 <bean>
标签中所有属性值的操作。可以认为 BeanDifinition
是 <bean>
标签在 Spring
中的抽象。 Spring
将配置文件中所有的 bean
标签都抽象为 BeanDefinition
,然后注册到 BeanDefinitionRegistry
中。
我们继续看看到底是怎么初始化一个 BeanDefinition
的实例的。
protected AbstractBeanDefinition createBeanDefinition(String className, String parentName)
throws ClassNotFoundException {
//可以看到需要的参数是parentName,className,classLoader
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}
复制代码
可以看到需要的参数是 parentName,className,classLoader
。关键因素是之后的 className
和 classLoader
。 Spring
在这里又使用 BeanDefinitionReaderUtils
来实例化 BeanDefinition
对象。我们继续看 BeanDefinitionReaderUtils#createBeandefinition()
。
public static AbstractBeanDefinition createBeanDefinition(
String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {
//还是使用的new关键字来对对象实例化,而且使用的是GenericBeanDefinition
GenericBeanDefinition bd = new GenericBeanDefinition();
//设置父节点名称
bd.setParentName(parentName);
//只有在className不为空的时候,才使用classLoader来加载className
if (className != null) {
//还得判断下classLoader是不是空,不为空,才加载
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
//如果有className就设置className
bd.setBeanClassName(className);
}
}
return bd;
}
复制代码
可以看到最关键的一点是还是使用的 new
关键字来初始话 GenericBeanDefinition
对象。可以在 GenericBeanDefinition
的注释上面看到, BeanDefinition
总共有三个实现类。但是,最后自从 Spring2.5
之后,不再使用 Root
和 child
了,而是使用 Generic
更加通用。
发现没有,越过千山万水,我们终于达到了真正解析 bean
标签的真正的属性的函数了。让我们揭开她的神秘的面纱。看一看 BeanDelegete#parseBeanDefinitionAttributes
。
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
BeanDefinition containingBean, AbstractBeanDefinition bd) {
//scope属性
if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
// Spring 2.x "scope" attribute
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Specify either 'scope' or 'singleton', not both", ele);
}
}
//singleton属性
else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
// Spring 1.x "singleton" attribute
bd.setScope(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)) ?
BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE);
}
else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
bd.setScope(containingBean.getScope());
}
//abstract属性
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
//lazy_init属性
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (DEFAULT_VALUE.equals(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE);
bd.setDependencyCheck(getDependencyCheck(dependencyCheck));
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, BEAN_NAME_DELIMITERS));
}
.........
return bd;
}
复制代码
可以看到经常被提到的 scope
属性, lazy-init
属性,还是很多我们不太熟悉的属性。这些属性被不断地放入到 BeanDefinition
的实例中。
在将这些所有的属性都再次一一解析之后,放入到 BeanDefinition
中,至此,我们就完成了从 XML
的 <bean>
标签到 Spring
内部的 BeanDefinition
的过程了。
在好好消化消化,告一个段落。
以后这里每天都会写一篇文章,题材不限,内容不限,字数不限。尽量把自己每天的思考都放入其中。
如果这篇文章给你带来了一些帮助,可以动动手指点个赞,顺便关注一波就更好了。
如果上面都没有,那么写下读完之后最想说的话?有效的反馈和你的鼓励是对我最大的帮助。
另外打算把博客给重新捡起来了。欢迎大家来访问吃西瓜。
我是shane。今天是2019年8月24日。百天写作计划的第三十一天,31/100。