1. 回顾
上一文写到,容器初始化的核心方法refreshBeanFactory做了容器的初始化、资源文件的配置以及bean加载。
首先,我们看一下容器初始化时用的默认容器类DefaultListableBeanFactory,此类是BeanFactory的一个子类,提供了bean管理的工厂方法,通过查找,我们可以看到在此类中如何存储bean。
2. 源码分析
2.1. bean是如何存储的?
看代码之前,自己也可以想一下原理,spring中通过id或类型来获取一个bean对象,最简单的实现肯定是定义一个Map对象,key为id或class,value为对象。
Map<String, Object> beans = new HashMap<String, Object>();
Map<Class, Object> classBeans = new HashMap<String, Object>();
类似上面这样,我们看一下DefaultListableBeanFactory类,找到了如下属性:
/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);
/** Map of singleton and non-singleton bean names, keyed by dependency type */
private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>(64);
/** Map of singleton-only bean names, keyed by dependency type */
private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>(64);
分析上面三个Map对象:
beanDefinitionMap保存了所有bean的name和定义(BeanDefinition)的k-v对,allBeanNamesByType保存的是类型与bean名称之间键值对,Class-BeanNamesingletonBeanNamesByType保存的则是所有单例对象的类型与名称键值对;
下面我们验证一下:
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("appContext.xml");
int beanDefinitionCount = applicationContext.getBeanDefinitionCount();
System.out.println("beanDefinitionCount:"+beanDefinitionCount);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
if(beanDefinitionNames!=null){
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("beanDefinitionName:" + beanDefinitionName);
}
}
由于我们的应用中(参考入门应用)只定义了一个简单的bean,结果和我们猜想的一样:
beanDefinitionCount:1
beanDefinitionName:user
2.2. BeanDefinition接口
上面的Map中,保存的是名称和BeanDefinition的键值对,这里我们有必须研究一下BeanDefinition接口,这个接口是Spring中的一个顶层接口,从名字上可以看出来,此接口定义了一个bean的表现形式,包括id、class、singoton、lazy-init等xml配置中的所有配置;
简单点来说,==可以认为xml配置文件中的一个bean节点就是一个BeanDefinition的具体表现,BeanDefinition从代码上定义了这个结构。==
理解了这一点之后,我们可以先跳过这个,继续上面的研究。
2.3. XmlBeanDefinitionReader
从名字就可以看出来,这个类用来从Xml文件中读取bean定义;
/**
* Bean definition reader for XML bean definitions.
* Delegates the actual XML document reading to an implementation
* of the {@link BeanDefinitionDocumentReader} interface.
*
* <p>Typically applied to a
* {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
* or a {@link org.springframework.context.support.GenericApplicationContext}.
*
* <p>This class loads a DOM document and applies the BeanDefinitionDocumentReader to it.
* The document reader will register each bean definition with the given bean factory,
* talking to the latter's implementation of the
* {@link org.springframework.beans.factory.support.BeanDefinitionRegistry} interface.
*/
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
......
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) {
...
}
finally {
...
}
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}catch (.....){
}
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
}
注释上讲了一个很重要的,此类是委托给BeanDefinitionDocumentReader接口的实现类来读取xml文件的。
从xml文件中加载BeanDefinition定义的整个过程其实不难理解:
- 从文件的输入流里,获取xml文档对象(
Document),这个过程比较复杂,具体细节有兴趣的可以去查看源码。 - 在容器中注册所有的
bean,此方法其实委托给了BeanDefinitionDocumentReader。所以,要研究的重要就是documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
public interface BeanDefinitionDocumentReader {
/**
* Read bean definitions from the given DOM document and
* register them with the registry in the given reader context.
*/
void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
throws BeanDefinitionStoreException;
}
此方法主要做了两件事:
- 从dom文档中读取
bean定义; - 把读取到的所有
bean注册到容器中;
2.4. BeanDefinitionDocumentReader的默认实现DefaultBeanDefinitionDocumentReader
BeanDefinitionDocumentReader接口的默认实现类是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);
}
继续跟踪下去,会发现,加载bean的最底层实现代码是下面的逻辑:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
这里有一个重要信息:spring支持自定义的xml格式?
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
此方法基本到了可以为我们解惑的地步了,分别处理import、alias、bean等节点;具体每个节点如何实现,不再一一来讲了,这里只看一下最关注的bean节点的处理:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 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));
}
}
从代码中看到,真正负责干活的其实是两个类:
- 负责解析xml的委托类
BeanDefinitionParserDelegate; - 负责注册
bean到容器中的BeanDefinitionReaderUtils;
下面可以分别大致看一下这两个类如何工作的。
2.5. BeanDefinitionParserDelegate
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
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);
}
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
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;
}
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
BeanDefinition containingBean, AbstractBeanDefinition bd) {
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}
else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
bd.setScope(containingBean.getScope());
}
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
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, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
}
else {
bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
}
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
if (!"".equals(initMethodName)) {
bd.setInitMethodName(initMethodName);
}
}
else {
if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
}
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
}
else {
if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
}
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}
2.6. BeanDefinitionReaderUtils
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
最终又回掉了容器的注册bean方法:
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition oldBeanDefinition;
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(oldBeanDefinition)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<String>(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;
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
最终,又回到了原点DefaultListableBeanFactory:
this.beanDefinitionMap.put(beanName, beanDefinition);
3. 总结
至此为止,我们已经基本搞清楚了下面几个问题:
- spring的ioc容器是如何管理
bean对象的?
BeanFactory为spring管理bean的工厂顶层接口,我们所熟悉的各种容器(ClassPathXmlApplicationContext、Web...)都是它针对不同用法的一个实现。
BeanDefinition是spring的bean的代码定义,也是非常关键的一个接口,xml配置中<bean/>的所有属性,都可以映射到此接口的属性;
bean工厂最底层的原理是使用一个Map来管理所有的name-BeanDefinition的键值对。spring容器加载的过程,即是从xml或其他资源文件中读取数据并解析成BeanDefinition,然后添加到容器工厂的Map的过程。
- spring是如何解析xml配置文件的?
通过阅读源码发现,spring解析xml没有那么神奇,和我们经常写的读取xml文件数据到Document文档中,然后分析Element的名称,根据不同的名称和属性来设置一个对象的参数没有什么不同。
4. 问题
通过今天的学习,基本已经懂得了spring容器初始化的整个过程,至于一些细节的问题,遇到了可以去查源码。
明天要写的文章应该是:从容器中获取一个bean的过程是什么样的?
明天如果搞定了这个问题,理解了整个Ioc容器的初始化过程和获取bean的过程,那么后天,就可以尝试自己来实现一个简单的Ioc容器了;