初始化IoC容器(Spring源码阅读)

初始化IoC容器(Spring源码阅读)

我们到底能走多远系列(31)

扯淡:

  有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的。未婚的你们,你们是怎么分配工资的?

  毕竟,对自己的收入的分配差不多体现了自己的现状,以及自己对自己未来有什么样的期许~

  

主题:

  本人在阅读源码基本参考了《Spring技术内幕:深入解析 Spring架构与设计原理》,很不错的书籍,建议大家阅读。

  初始化IoC容器:1,resource定位  2,BeanDefinition载入  3,把BeanDefinition载入到IoC

重要的解释:

  Spring中的IoC容器,所谓的容器核心就是个hashMap,准确的说是ConcurrentHashMap,键值对就是:<String, BeanDefinition> key是bean的name。

  那么BeanDefinition就是对外界bean描述的抽象,比如你写的<bean>标签,最会被抽象成一个BeanDefinition放进这个Map中去。

  所以一个完整的IoC容器来说,只要提供对这个Map的必要操作就可以了。一般DefaultListableBeanFactory 作为一个默认功能完整的IoC容器来使用。

  上面的解释已经很清楚了,IoC容器的初始化就可以理解成,我们有个xml描述了一些bean的属性,Spring把它读进来,按照自己的规则解析一边,把其中的Bean描述转换成一个个BeanDefinition放进Map中去就完成初始化了。

  拿FileSystemXmlApplicationContext为例子看一下源码中是如何实现的:(希望有兴趣的先看下Spring中BeanFactory下面的继承结构)

  我们会写类似下面的代码开始我们的Spring之旅:

初始化IoC容器(Spring源码阅读)
ApplicationContext ct = new FileSystemXmlApplicationContext("src/service/service.xml");
初始化IoC容器(Spring源码阅读)


FileSystemXmlApplicationContext
的构造函数:

初始化IoC容器(Spring源码阅读)
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException { super(parent);
setConfigLocations(configLocations);
if (refresh) {
// 起点
refresh();
}
}
初始化IoC容器(Spring源码阅读)

调用的refresh()方法是AbstractApplicationContext的方法:(这个方法就像执行顺序图,描述好了先做什么,后做什么)

初始化IoC容器(Spring源码阅读)
初始化IoC容器(Spring源码阅读)

obtainFreshBeanFactory()方法:

初始化IoC容器(Spring源码阅读)
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
     // 这个方法是在AbstractApplicationContext定义没有实现的abstract方法,这就是所谓的模板模式吧,后面很多地方这样实现的,可以学习下。
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}   protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
初始化IoC容器(Spring源码阅读)

refreshBeanFactory()方法是谁实现的呢?子类去实现。AbstractRefreshableApplicationContext实现的:

初始化IoC容器(Spring源码阅读)
    protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
       // 这里我们就看到了,通过这个方法调用来初始化的,都使用了DefaultListableBeanFactory作为IoC容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
       // 这个方法就去完成load Bean啦,不过它也使用了子类的实现
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
初始化IoC容器(Spring源码阅读)

AbstractXmlApplicationContext实现的loadBeanDefinitions(DefaultListableBeanFactory beanFactory):

初始化IoC容器(Spring源码阅读)
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
     // 更深一层后,这里就开始定义了Reader是什么了。我们要读取的xml文件,这个Reader可以帮助我们
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
     // 调用下面的的方法
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
       // 把工作交给XmlBeanDefinitionReader完成
reader.loadBeanDefinitions(configLocations);
}
}
初始化IoC容器(Spring源码阅读)

通过XmlBeanDefinitionReader的父类AbstractBeanDefinitionReader的方法把String configLocations封装成Recource,然后调用XmlBeanDefinitionReader实现的loadBeanDefinitions(resource)方法。

下面是父类的调用方法:

初始化IoC容器(Spring源码阅读)
    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
     // 这里的for就可以理解多个xml文件的情况了
     // 从外面看,一个bean首先是放在多个xml中的一个,然后是一个xml文件中的多个bean描述中的一个,这个也是定位到一个bean要走的路。
for (Resource resource : resources) {
       // 交给子类实现
counter += loadBeanDefinitions(resource);
}
return counter;
}
初始化IoC容器(Spring源码阅读)
子类实现:
初始化IoC容器(Spring源码阅读)
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
} Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(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());
}
         // 我觉得到这里,看方法名就知道了,要开始do了,前面好像都没do嘛
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();
}
}
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
       // 先解析成Document
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
       // 按照Spring的规则开始解析,放进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);
}
}
初始化IoC容器(Spring源码阅读)

XmlBeanDefinitionReader的registerBeanDefinitions方法:

初始化IoC容器(Spring源码阅读)
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
documentReader.setEnvironment(this.getEnvironment());
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
初始化IoC容器(Spring源码阅读)

上面代码可以看到:继续交给DefaultBeanDefinitionDocumentReader来解析Document:

下面是详细的处理过程:

初始化IoC容器(Spring源码阅读)
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext; logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
Assert.state(this.environment != null, "environment property must not be null");
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!this.environment.acceptsProfiles(specifiedProfiles)) {
return;
}
} // 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 = createHelper(readerContext, root, parent); preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root); this.delegate = parent;
}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
       // 前面的注释中提到过bean定位时要从多个bean描述中一个个取,这个for循环就是了,
       //所以这个否循环里面的逻辑就是处理一个个bean的映射的,当然也有其他的标签,这里我们且认为只有bean吧
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);
}
}


初始化IoC容器(Spring源码阅读)

parseDefaultElement方法:

初始化IoC容器(Spring源码阅读)
    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 ==>注意这个beans就递归,有调用了前面的doRegisterBeanDefinitions方法
doRegisterBeanDefinitions(ele);
}
}
初始化IoC容器(Spring源码阅读)

processBeanDefinition方法:

初始化IoC容器(Spring源码阅读)
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
     // 这里就出现了BeanDefinitionHolde 它封装了BeanDefinition
    // BeanDefinitionParserDelegate 来完成最底层的按照spring的规则解析
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));
}
}
初始化IoC容器(Spring源码阅读)
parseBeanDefinitionElement方法的代码:
初始化IoC容器(Spring源码阅读)
初始化IoC容器(Spring源码阅读)

上面解析的代码中有调用的一个方法parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean)带我们去解析bean中更加详细的属性:

初始化IoC容器(Spring源码阅读)
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {// class属性
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
} try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {// parent属性
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
       // 下面parse方法解析其他的属性
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;
}
     // 下面的错误用spring有时候有看见过吧
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;
}
初始化IoC容器(Spring源码阅读)

以上就是解析好了,然后就是放进传说中的IoC容器里啦:

BeanDefinitionReaderUtils的registerBeanDefinition方法:

初始化IoC容器(Spring源码阅读)
    public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException { // Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
    // 调用了下BeanDefinitionRegistry 的方法,
    // 而这个registry就是DefaultListableBeanFactory,所有的BeanDefinition都要注册到它里面去呀...
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}
初始化IoC容器(Spring源码阅读)

DefaultListableBeanFactory的registerBeanDefinition:

初始化IoC容器(Spring源码阅读)
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);
}
}
synchronized (this.beanDefinitionMap) {
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
       // 保证加载bean唯一性
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
}
else {
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
       // 放到前面提到的ConcurrentHashMap里面去喽~
this.beanDefinitionMap.put(beanName, beanDefinition); resetBeanDefinition(beanName);
}
}
初始化IoC容器(Spring源码阅读)

以上就是初始化IoC容器的过程。各位有兴趣可以自己debug进去一步步看既可以了。

另外用maven的话直接用下面命令下载源码即可:

初始化IoC容器(Spring源码阅读)
mvn dependency:sources
初始化IoC容器(Spring源码阅读)

让我们继续前行

----------------------------------------------------------------------

努力不一定成功,但不努力肯定不会成功。
共勉。

 
 
分类: Java
上一篇:js四舍五入的bug和方法


下一篇:Bean实例化(Spring源码阅读)-我们到底能走多远系列(33)