Spring IOC源代码具体解释之容器初始化

上篇介绍了Spring IOC的大致体系类图,先来看一段简短的代码,使用IOC比較典型的代码


ClassPathResource res = new ClassPathResource("beans.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res); 
上面使用创建IOC经历了四个过程

  • 创建IOC配置文件的抽象资源
  • 创建一个BeanFactory
  • 把读取配置信息的BeanDefinitionReader,这里是XmlBeanDefinitionReader配置给BeanFactory
  • 从定义好的资源位置读入配置信息,详细的解析过程由XmlBeanDefinitionReader来完毕,这样完毕整个加载bean定义的过程。
从上面能够看出,整个IOC初始化,大致分为资源定位、资源装载、资源解析、Bean生成,Bean注冊这几个过程

首先看资源的定位,一个常见的ApplicationContext

 ApplicationContext =new FileSystemXmlApplicationContext(xmlPath);

追踪FileSystemXmlApplicationContext。里面有一些构造函数,都指向以下这个

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)throws BeansException {super(parent); //有前面类图 可 以知道,终于 父类 为AbstractApplicationContextsetConfigLocations(configLocations);if (refresh) {refresh();}}
//追踪super,终于定位到 AbstractApplicationContext中public AbstractApplicationContext() {this.resourcePatternResolver = getResourcePatternResolver();}protected ResourcePatternResolver getResourcePatternResolver() {return new PathMatchingResourcePatternResolver(this);}
继续往下,追踪setConfigLocations方法

    public void setConfigLocations(String... locations) {if (locations != null) {Assert.noNullElements(locations, "Config locations must not be null");this.configLocations = new String[locations.length];// 该方法调用 SystemPropertyUtils.resolvePlaceholders(path) ;对 path 中的占位 符进行//  替换, eg : path 路径中含有 ${user.dir} ,则将替换为: System.getProperty(user.dir);for (int i = 0; i < locations.length; i++) {this.configLocations[i] = resolvePath(locations[i]).trim();}}else {this.configLocations = null;}}
     代码中 configLocations 为 AbstractRefreshableConfigApplicationContext 类中 string[] 类型的字段。至此应用程序传入路径保存在 AbstractRefreshableConfigApplicationContext 中 。
回到FileSystemXmlApplicationContext中继续往下。即最关键的Refash方法,refresh()-方法定义在类: AbstractApplicationContext ,由类图可知: FileSystemXmlApplicationContext 间接继承至: AbstractApplicationContext refresh() 方法例如以下:


public void refresh() throws BeansException, IllegalStateException {  synchronized (this.startupShutdownMonitor) {  //调用容器准备刷新的方法,获取 容器的当时时间,同一时候给容器设置同步标识  prepareRefresh();  //告诉子类启动refreshBeanFactory()方法。Bean定义资源文件的载入从  //子类的refreshBeanFactory()方法启动  ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  //为BeanFactory配置容器特性,比如类载入器、事件处理器等  prepareBeanFactory(beanFactory);  try {  //为容器的某些子类指定特殊的BeanPost事件处理器  postProcessBeanFactory(beanFactory);  //调用全部注冊的BeanFactoryPostProcessor的Bean  invokeBeanFactoryPostProcessors(beanFactory);  //为BeanFactory注冊BeanPost事件处理器.  //BeanPostProcessor是Bean后置处理器。用于监听容器触发的事件  registerBeanPostProcessors(beanFactory);  //初始化信息源,和国际化相关.  initMessageSource();  //初始化容器事件传播器.  initApplicationEventMulticaster();  //调用子类的某些特殊Bean初始化方法  onRefresh();  //为事件传播器注冊事件监听器.  registerListeners();  //初始化全部剩余的单态Bean.  finishBeanFactoryInitialization(beanFactory);  //初始化容器的生命周期事件处理器,并公布容器的生命周期事件  finishRefresh();  }  catch (BeansException ex) {  //销毁以创建的单态Bean  destroyBeans();  //取消refresh操作。重置容器的同步标识.  cancelRefresh(ex);  throw ex;  }  }  }
  • prepareRefresh(): 为刷新准备上下文环境

    • obtainFreshBeanFactory() :让子类刷新内部 bean 工厂。
进入obtainFreshBeanFatory()。关闭前面全部 bean 工厂,为新的上下文环境初始化一个新的 bean 工厂。这里须要子类来 协助完毕资源位置定义 ,bean 加载和向 IOC 容器注冊的过程

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {refreshBeanFactory();ConfigurableListableBeanFactory beanFactory = getBeanFactory();if (logger.isDebugEnabled()) {logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);}return beanFactory;}
上面代码能够看到refreshBeanFactory,AbstractApplicationContext子类中的方法 AbstractApplicationContext类中仅仅抽象定义了refreshBeanFactory()方法。容器真正调用的是其子类AbstractRefreshableApplicationContext实现的 refreshBeanFactory()方法,方法的源代码例如以下

protected final void refreshBeanFactory() throws BeansException {  if (hasBeanFactory()) {//假设已经有容器。销 毁 容器中的bean,关闭容器  destroyBeans();  closeBeanFactory();  }  try {  //创建IoC容器  DefaultListableBeanFactory beanFactory = createBeanFactory();  beanFactory.setSerializationId(getId());  //对IoC容器进行定制化,如设置启动參数,开启注解的自己主动装配等  customizeBeanFactory(beanFactory);  //调用加载Bean定义的方法,主要这里使用了一个委派模式。在当前类中仅仅定义了抽象的loadBeanDefinitions方法,详细的实现调用子类容器  loadBeanDefinitions(beanFactory);  synchronized (this.beanFactoryMonitor) {  this.beanFactory = beanFactory;  }  }  catch (IOException ex) {  throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);  }  }
    在这种方法中,先推断BeanFactory是否存在,假设存在则先销毁beans并关闭beanFactory,接着创建DefaultListableBeanFactory,并调用loadBeanDefinitions(beanFactory)装载bean
在这种方法中,先推断BeanFactory是否存在,假设存在则先销毁beans并关闭beanFactory,接着创建DefaultListableBeanFactory,并调用loadBeanDefinitions(beanFactory)装载bean定义。看上面的loadBeanDefinitions。在AbstractRefreshableApplicationContext子类。

相同。AbstractRefreshableApplicationContext中仅仅定义了抽象的loadBeanDefinitions方法,容器真正调用的是其子类AbstractXmlApplicationContext对该方法的实现,AbstractXmlApplicationContext的主要源代码例如以下:
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {// 这里使用XMLBeanDefinitionReader来加载bean定义信息的XML文件XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);//这里配置reader的环境,当中ResourceLoader是我们用来定位bean定义信息资源位置的///由于上下文本身实现了ResourceLoader接口,所以能够直接把上下文作为ResourceLoader传递给XmlBeanDefinitionReaderbeanDefinitionReader.setResourceLoader(this);beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));initBeanDefinitionReader(beanDefinitionReader);//这里转到定义好的XmlBeanDefinitionReader中对加载bean信息进行处理loadBeanDefinitions(beanDefinitionReader);}
继续,看到了最以下的loadBeanDefinitions(beanDefinitionReader);转到XmlBeanDefinitionReader中的loadBeanDefinitions。


protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {Resource[] configResources = getConfigResources();if (configResources != null) {//调用XmlBeanDefinitionReader来加载bean定义信息。 reader.loadBeanDefinitions(configResources);}String[] configLocations = getConfigLocations();if (configLocations != null) {reader.loadBeanDefinitions(configLocations);}}
    Xml Bean读取器(XmlBeanDefinitionReader)调用其父类AbstractBeanDefinitionReader的 reader.loadBeanDefinitions方法读取Bean定义资源。
因为我们使用FileSystemXmlApplicationContext作为样例分析,因此getConfigResources的返回值为null,因此程序运行reader.loadBeanDefinitions(configLocations)分支。

    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { //这里得到当前定义的ResourceLoader,默认的 我 们 使用DefaultResourceLoaderResourceLoader resourceLoader = getResourceLoader();.........//假设没有找到我们须要的ResourceLoader,直接抛出异常if (resourceLoader instanceof ResourcePatternResolver) {// 这里处理我们在定义位置时使用的各种pattern,须要ResourcePatternResolver来完毕try {Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);int loadCount = loadBeanDefinitions(resources);return loadCount;}........}else {// 这里通过ResourceLoader来完毕位置定位Resource resource = resourceLoader.getResource(location);// 这里已经把一个位置定义转化为Resource接口,能够供XmlBeanDefinitionReader来使用了int loadCount = loadBeanDefinitions(resource);return loadCount;}}

重载方法public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {Assert.notNull(locations, "Location array must not be null");int counter = 0;for (String location : locations) {counter += loadBeanDefinitions(location);}return counter;}
loadBeanDefinitions(Resource...resources)方法和上面分析的3个方法类似,相同也是调用XmlBeanDefinitionReader的loadBeanDefinitions方法。

从对AbstractBeanDefinitionReader的loadBeanDefinitions方法源代码分析能够看出该方法做了下面两件事:

  • 首先,调用资源载入器的获取资源方法resourceLoader.getResource(location)。获取到要载入的资源。

  • 其次,真正运行载入功能是其子类XmlBeanDefinitionReader的loadBeanDefinitions方法。

由上类图能够知道此时调用的是DefaultResourceLoader中的getSource()方法定位Resource。由于FileSystemXmlApplicationContext本身就是DefaultResourceLoader的实现类。

2、资源载入

XmlBeanDefinitionReader通过调用其父类DefaultResourceLoader的getResource方法获取要载入的资源,这样,就能够从文件系统路径上对IOC配置文件进行载入 - 当然能够依照这个逻辑从不论什么地方载入,在Spring中看到它提供的各种资源抽象。比方ClassPathResource, URLResource,FileSystemResource等来供我们使用。

//获取Resource的具 体实现方法  public Resource getResource(String location) {  Assert.notNull(location, "Location must not be null");  //假设是类路径的方式。那须要使用ClassPathResource 来得到bean 文件的资源对象  if (location.startsWith(CLASSPATH_URL_PREFIX)) {  return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());  }  try {  // 假设是URL 方式,使用UrlResource 作为bean 文件的资源对象  URL url = new URL(location);  return new UrlResource(url);  }  catch (MalformedURLException ex) { } //假设既不是classpath标识。又不是URL标识的Resource定位,则调用  //容器本身的getResourceByPath方法获取Resource  return getResourceByPath(location);  }
FileSystemXmlApplicationContext容器提供了getResourceByPath方法的实现。就是为了处理既不是classpath标识,又不是URL标识的Resource定位这样的情况

@Overrideprotected Resource getResourceByPath(String path) {if (path != null && path.startsWith("/")) {path = path.substring(1);}return new FileSystemResource(path);}
所以此时又回到了FileSystemXmlApplicationContext中来,提供了FileSystemResource来完毕从文件系统得到配置文件的资源定义。

Bean资源载入

继续XmlBeanDefinitionReader的loadBeanDefinitions(Resource …)


//XmlBeanDefinitionReader加载资源 的入口方法  public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {  //将读入的XML资源进行特殊编码处理  return loadBeanDefinitions(new EncodedResource(resource));  } //这里是加载XML形式Bean定义资源文件方法public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {    .......    try {    //将资源文件转为InputStream的IO流 InputStream inputStream = encodedResource.getResource().getInputStream();    try {    //从InputStream中得到XML的解析源    InputSource inputSource = new InputSource(inputStream);    if (encodedResource.getEncoding() != null) {    inputSource.setEncoding(encodedResource.getEncoding());    }    //这里是具体的读取过程    return doLoadBeanDefinitions(inputSource, encodedResource.getResource());    }    finally {    //关闭从Resource中得到的IO流    inputStream.close();    }    }    .........
26}    //从特定XML文件里实际加载Bean定义资源的方法 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)    throws BeanDefinitionStoreException {    try {    int validationMode = getValidationModeForResource(resource);    //将XML文件转换为DOM对象,解析过程由documentLoader实现    Document doc = this.documentLoader.loadDocument(    inputSource, this.entityResolver, this.errorHandler, validationMode, this.namespaceAware);    //这里是启动对Bean定义解析的具体过程,该解析过程会用到Spring的Bean配置规则return registerBeanDefinitions(doc, resource);    }    .......    }
源代码的最后。将XML文件转为DOM对象,即将Bean对象转成DOm资源。这是怎样完毕的呢?

3、资源解析

2中Bean定义的Resource得到了。最后还讲到ocumentLoader将Bean定义资源转换为Document对象:DocumentLoader将Bean定义资源转换成Document对象的源代码例如以下:

以下这句是XmlBeanDefinitionReader中关于DocumentLoader。用了DefaultDocumentLoaderprivate DocumentLoader documentLoader = new DefaultDocumentLoader();
上面的DefaultDocumentLoader肯定不陌生吧。在Spring AOP中多次出现了。找到DefaultDocumentLoader中的loadDocument()方法

//使用标准的JAXP 将 加载的B ean 定义 资源 转换成document对象  public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,  ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {  //创建文件解析器工厂  DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);  if (logger.isDebugEnabled()) {  logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");  }  //创建文档解析器  DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);  //解析Spring的Bean定义资源  return builder.parse(inputSource);  }  
继续找到createDocumentBuilder

    protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory, EntityResolver entityResolver, ErrorHandler errorHandler)throws ParserConfigurationException {DocumentBuilder docBuilder = factory.newDocumentBuilder();if (entityResolver != null) {docBuilder.setEntityResolver(entityResolver);}if (errorHandler != null) {docBuilder.setErrorHandler(errorHandler);}return docBuilder;}
上面调用了Javaee的JAXP标准。依据定位的Bean定义资源文件,载入读入并转换成为Document对象过程完毕

4、bean生成(依据DOM资源解析成bean)

回想3: XmlBeanDefinitionReader类中的doLoadBeanDefinitions方法是从特定XML文件里实际加载Bean定义资源的方法,该方法在加载Bean定义资源之后将其转换为Document对象。继续看doLoadBeanDefinitions,进入registerBeanDefinitions

 Document doc = this.documentLoader.loadDocument()后面。有一句return registerBeanDefinitions(doc, resource);
//依照Spring的Bean语义要求 将Bean 定 义  资源解析并转换为容器内部数据结构  public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {  //得到BeanDefinitionDocumentReader来对xml格式的BeanDefinition解析  BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();  //获得容器中注冊的Bean数量  int countBefore = getRegistry().getBeanDefinitionCount();  //解析过程入口。这里使用了委派模式,BeanDefinitionDocumentReader仅仅是个接口,//详细的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完毕  documentReader.registerBeanDefinitions(doc, createReaderContext(resource));  //统计解析的Bean数量  return getRegistry().getBeanDefinitionCount() - countBefore;  }
   //创建BeanDefinitionDocumentReader对象。 解 析 D ocument对象  protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {  return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));  }
Bean定义资源的加载解析分为下面两个过程:

  • 首先。通过调用XML解析器将Bean定义资源文件转换得到Document对象,可是这些Document对象并没有依照Spring的Bean规则进行解析。这一步是加载的过程

  • 其次,在完毕通用的XML解析之后,依照Spring的Bean规则对Document对象进行解析。

继续

documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 这里的documentReader为BeanDefinitionDocumentReader对象,BeanDefinitionDocumentReader接口通过registerBeanDefinitions方法调用事实上现类DefaultBeanDefinitionDocumentReader对Document对象进行解析
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;//获得XML  描 述 符  logger.debug("Loading bean definitions");Element root = doc.getDocumentElement();//获得Document的根元素  doRegisterBeanDefinitions(root);}
protected void doRegisterBeanDefinitions(Element root) {//BeanDefinitionParserDelegate中定义了Spring Bean 定 义 XML 文件的各种元素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);if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {return;}}}//在解析Bean定义之前,进行自己定义的解析,增强解析过程的可扩展性  preProcessXml(root);//从Document的根元素開始进行Bean定义的Document对象,见以下解析parseBeanDefinitions(root, this.delegate);//在解析Bean定义之后,进行自己定义的解析,添加解析过程的可扩展性  postProcessXml(root);this.delegate = parent;}
//上面的createDelegate()方法, 创建BeanDefinitionParserDelegate,用于完毕真正的解析过程
protected BeanDefinitionParserDelegate createDelegate(XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);//BeanDefinitionParserDelegate初始化Document根元素  delegate.initDefaults(root, parentDelegate);return delegate;}
//上面的parseBeanDefinitions// 使 用Spring的Bean规则从Document的根元素開始进行Bean定义的Document对象 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {//Bean定义的Document对象使用了Spring默认的XML命名空间        if (delegate.isDefaultNamespace(root)) {//获取Bean定义的Document对象根元素的全部子节点  NodeList nl = root.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {//获得Document节点是XML元素节点  Node node = nl.item(i); if (node instanceof Element) {Element ele = (Element) node;if (delegate.isDefaultNamespace(ele)) {//假设使用的是默认命名控件,使用Spring的Bean规则解析元素节点  parseDefaultElement(ele, delegate);}else {//没有使用Spring默认的XML命名空间。则使用用户自己定义的解//析规则解析元素节点 delegate.parseCustomElement(ele);}}}}else {//使用默认解析规则解析Document根节点  delegate.parseCustomElement(root);}}
//使用默 认 规 则 是个什么样子呢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);}else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {// recursedoRegisterBeanDefinitions(ele);}}
上面代码的三中Import、Alials、Bean我们主要来看Bean的解析,追踪processBeanDefinition(ele, delegate

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {//托付给BeanDefinitionParserDelegate来完毕对bean元素的 处 理  。这个类包括了详细的bean解析的过程。  // 把解析bean文件得到的信息放到BeanDefinition里,他是bean信息的主要载体。也是IOC容器的管理对象。

//BeanDefinitionHolder是对BeanDefinition的封装。即Bean定义的封装类 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) {//假设存在bean定义的话 //对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { //向Spring IoC容器注冊解析得到的Bean定义。这是Bean定义向IoC容器注冊的入口 。实际上是放到一个Map里面 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } //在完毕向Spring IoC容器注冊解析得到的Bean定义之后。发送注冊事件 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }

这里事实上还能够深入了解,bean的解析。追踪上面的这条

BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
能够进入parseBeanDefinitionElement方法中,里面具体讲了Bean元素是怎样解析的。

//解析<Bean>元素的入口  public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {  return parseBeanDefinitionElement(ele, null);  }  
   //解析Bean定义资源文件里的<Bean>元素。这个 方 法中主要处理<Bean>元素的id,name  //和别名属性  public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {  //获取<Bean>元素中的id属性值  String id = ele.getAttribute(ID_ATTRIBUTE);  //获取<Bean>元素中的name属性值  String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);  获取<Bean>元素中的alias属性值  List<String> aliases = new ArrayList<String>();  //将<Bean>元素中的全部name属性值存放到别名中  if (StringUtils.hasLength(nameAttr)) {  String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);  aliases.addAll(Arrays.asList(nameArr));  }  String beanName = id;  //假设<Bean>元素中没有配置id属性时,将别名中的第一个值赋值给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");  }  }  //检查<Bean>元素所配置的id或者name的唯一性,containingBean标识<Bean>  //元素中是否包括子<Bean>元素  if (containingBean == null) {  //检查<Bean>元素所配置的id、name或者别名是否反复  checkNameUniqueness(beanName, aliases, ele);  }  //具体对<Bean>元素中配置的Bean定义进行解析的地方  AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);  if (beanDefinition != null) {  if (!StringUtils.hasText(beanName)) {  try {  if (containingBean != null) {  //假设<Bean>元素中没有配置id、别名或者name。且没有包括子//<Bean>元素,为解析的Bean生成一个唯一beanName并注冊  beanName = BeanDefinitionReaderUtils.generateBeanName(  beanDefinition, this.readerContext.getRegistry(), true);  }  else {  //假设<Bean>元素中没有配置id、别名或者name,且包括了子//<Bean>元素,为解析的Bean使用别名向IoC容器注冊  beanName = this.readerContext.generateBeanName(beanDefinition);  //为解析的Bean使用别名注冊时,为了向后兼容                                    //Spring1.2/2.0。给别名加入类名后缀  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);  }  //当解析出错时,返回null  return null;  }  //具体对<Bean>元素中配置的Bean定义其它属性进行解析,因为上面的方法中已经对//Bean的id、name和别名等属性进行了处理,该方法中主要处理除这三个以外的其它属性数据  public AbstractBeanDefinition parseBeanDefinitionElement(  Element ele, String beanName, BeanDefinition containingBean) {  //记录解析的<Bean>  this.parseState.push(new BeanEntry(beanName));  //这里仅仅读取<Bean>元素中配置的class名字,然后加载到BeanDefinition中去  //仅仅是记录配置的class名字,不做实例化,对象的实例化在依赖注入时完毕  String className = null;  if (ele.hasAttribute(CLASS_ATTRIBUTE)) {  className = ele.getAttribute(CLASS_ATTRIBUTE).trim();  }  try {  String parent = null;  //假设<Bean>元素中配置了parent属性。则获取parent属性的值  if (ele.hasAttribute(PARENT_ATTRIBUTE)) {  parent = ele.getAttribute(PARENT_ATTRIBUTE);  }  //依据<Bean>元素配置的class名称和parent属性值创建BeanDefinition  //为加载Bean定义信息做准备  AbstractBeanDefinition bd = createBeanDefinition(className, parent);  //对当前的<Bean>元素中配置的一些属性进行解析和设置。如配置的单态(singleton)属性等  parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);  //为<Bean>元素解析的Bean设置description信息 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));  //对<Bean>元素的meta(元信息)属性解析  parseMetaElements(ele, bd);  //对<Bean>元素的lookup-method属性解析  parseLookupOverrideSubElements(ele, bd.getMethodOverrides());  //对<Bean>元素的replaced-method属性解析  parseReplacedMethodSubElements(ele, bd.getMethodOverrides());  //解析<Bean>元素的构造方法设置  parseConstructorArgElements(ele, bd);  //解析<Bean>元素的<property>设置  parsePropertyElements(ele, bd);  //解析<Bean>元素的qualifier属性  parseQualifierElements(ele, bd);  //为当前解析的Bean设置所需的资源和依赖对象  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();  }  //解析<Bean>元素出错时。返回null  return null;  }
上面方法中一些对一些配置如元信息(meta)、qualifier等的解析。我们在Spring中配置时使用的也不多。我们在使用Spring的元素时,配置最多的是property属性

    //解析<property>元素中ref,value或者集合等子元素  public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {  //假设<property>没有使用Spring默认的命名空间,则使用用户自己定义的规则解析//内嵌元素  if (!isDefaultNamespace(ele)) {  return parseNestedCustomElement(ele, bd);  }  //假设子元素是bean,则使用解析<Bean>元素的方法解析  else if (nodeNameEquals(ele, BEAN_ELEMENT)) {  BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);  if (nestedBd != null) {  nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);  }  return nestedBd;  }  //假设子元素是ref。ref中仅仅能有下面3个属性:bean、local、parent  else if (nodeNameEquals(ele, REF_ELEMENT)) {  //获取<property>元素中的bean属性值,引用其它解析的Bean的名称  //能够不再同一个Spring配置文件里。详细请參考Spring对ref的配置规则  String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);  boolean toParent = false;  if (!StringUtils.hasLength(refName)) {  //获取<property>元素中的local属性值。引用同一个Xml文件里配置  //的Bean的id,local和ref不同,local仅仅能引用同一个配置文件里的Bean  refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);  if (!StringUtils.hasLength(refName)) {  //获取<property>元素中parent属性值,引用父级容器中的Bean  refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);  toParent = true;  if (!StringUtils.hasLength(refName)) {  error("'bean', 'local' or 'parent' is required for <ref> element", ele);  return null;  }  }  }  //没有配置ref的目标属性值  if (!StringUtils.hasText(refName)) {  error("<ref> element contains empty target attribute", ele);  return null;  }  //创建ref类型数据。指向被引用的对象  RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);  //设置引用类型值是被当前子元素所引用  ref.setSource(extractSource(ele));  return ref;  }  //假设子元素是<idref>。使用解析ref元素的方法解析  else if (nodeNameEquals(ele, IDREF_ELEMENT)) {  return parseIdRefElement(ele);  }  //假设子元素是<value>,使用解析value元素的方法解析  else if (nodeNameEquals(ele, VALUE_ELEMENT)) {  return parseValueElement(ele, defaultValueType);  }  //假设子元素是null。为<property>设置一个封装null值的字符串数据  else if (nodeNameEquals(ele, NULL_ELEMENT)) {  TypedStringValue nullHolder = new TypedStringValue(null);  nullHolder.setSource(extractSource(ele));  return nullHolder;  }  //假设子元素是<array>,使用解析array集合子元素的方法解析  else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {  return parseArrayElement(ele, bd);  }  //假设子元素是<list>,使用解析list集合子元素的方法解析  else if (nodeNameEquals(ele, LIST_ELEMENT)) {  return parseListElement(ele, bd);  }  //假设子元素是<set>,使用解析set集合子元素的方法解析  else if (nodeNameEquals(ele, SET_ELEMENT)) {  return parseSetElement(ele, bd);  }  //假设子元素是<map>,使用解析map集合子元素的方法解析  else if (nodeNameEquals(ele, MAP_ELEMENT)) {  return parseMapElement(ele, bd);  }  //假设子元素是<props>,使用解析props集合子元素的方法解析  else if (nodeNameEquals(ele, PROPS_ELEMENT)) {  return parsePropsElement(ele);  }  //既不是ref,又不是value。也不是集合。则子元素配置错误,返回null  else {  error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);  return null;  }  }
上面代码中对property元素中配置的Array、List、Set、Map、Prop等各种集合子元素的都通过上述方法解析。生成相应的数据对象,比方ManagedList、ManagedArray、ManagedSet等,这些Managed类是Spring对象BeanDefiniton的数据封装。对集合数据类型的详细解析有各自的解析方法实现。假设想了解当中的一种集合是怎样解析的 ,进入相应的方法。

5、Bean是怎样在IOC容器中注冊的

以下看解析完的bean是如何在IOC容器中注冊的:接着4中的代码,进入

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
    public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {//得到须要 注冊 的 bean名字String beanName = definitionHolder.getBeanName();//開始注冊registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// 别名也是能够 通过IOC容器和bean联系起来的进行注冊 String[] aliases = definitionHolder.getAliases();if (aliases != null) {for (String alias : aliases) {registry.registerAlias(beanName, alias);}}}
上面的registry对象是BeanDefinitionRegistry,上一步是BeanDefinitionReaderUtils.registerBeanDefinition(),即当调用BeanDefinitionReaderUtils向IoC容器注冊解析的BeanDefinition时。真正完毕注冊功能的是DefaultListableBeanFactory。

那么DefaultListableBeanFactory怎么会和BeanDefinitionReaderUtils扯到一起呢?

事实上上面的类图还忽略了一点,就是

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {}DefaultListableBeanFactory 实现了BeanDefinitionRegistry接口。这下就豁然开朗,那么DefaultListableBeanFactory是怎样注冊的呢
DefaultListableBeanFactory中使用一个HashMap的集合对象存放IoC容器中注冊解析的BeanDefinition。

@Override//---------------------------------------------------------------------// 这里是IOC容器对BeanDefinitionRegistry接口的实现//---------------------------------------------------------------------public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {.....//这里省略了对BeanDefinition的验证过程//先看看在容器里是不是已经有了同名的bean,假设有抛出异常。Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);if (oldBeanDefinition != null) {if (!this.allowBeanDefinitionOverriding) {...........}else {//把bean的名字加到IOC容器中去this.beanDefinitionNames.add(beanName);}//这里把bean的名字和Bean定义联系起来放到一个HashMap中去,IOC容器通过这个Map来维护容器里的Bean定义信息。this.beanDefinitionMap.put(beanName, beanDefinition);removeSingleton(beanName);}
这样就完毕了Bean定义在IOC容器中的注冊。就可被IOC容器进行管理和使用了

总结IOC初始化过程

  • 1、setConfigLocations方法

  • 2、初始化的入口在容器实现中的 refresh()调用来完毕

  • 3、AbstractRefreshableApplicationContext实现的 refreshBeanFactory()方法

  • 4、创建DefaultListableBeanFactory,并调用loadBeanDefinitions(beanFactory)装载bean定义

  • 5、转到XmlBeanDefinitionReader中的loadBeanDefinitions。

  • 6、XmlBeanDefinitionReader通过调用其父类DefaultResourceLoader的getResource方法获取要载入的资源

  • 7 、DocumentLoader将Bean定义资源转换成Document对象

  • 8、doLoadBeanDefinitions中进入registerBeanDefinitions 解 析 D ocument对象

  • 9、DefaultListableBeanFactory中使用一个HashMap的集合对象存放IoC容器中注冊解析的BeanDefinition,

Spring IOC源代码具体解释之容器初始化相关推荐

  1. tomcat + spring mvc原理(二):tomcat容器初始化加载和启动

    tomcat + spring mvc原理(二):tomcat容器动态加载 容器通用生命周期标准 容器通用生命周期的实现 生命周期状态监听器的管理实现 生命周期方法实现 宏观来看各种容器生命周期的实际 ...

  2. Spring Ioc源码分析系列--容器实例化Bean的四种方法

  3. 关于spring IoC 学习

    What is IoC 简单来说: 控制:当前对象对其内部成员对象的控制权/获取组装对象的过程 反转:上述的过程/控制权,交由专门的第三方组件(容器或者说平台)来管理 这种从具体对象手中,交出控制的做 ...

  4. Spring IOC 核心流程浓缩

    一.基础概念 1.IoC 和 DI IoC (Inversion of Control),即控制反转.这不是一种新的技术,而是 Spring 的一种设计思想. 在传统的程序设计,我们直接在对象内部通过 ...

  5. spring IOC基本配置(xml配置和注解配置)

    目录 Spring IOC IOC是什么 IOC可以做什么 依赖注入 IOC和DI IOC容器 Bean 配置IOC容器 spring ioc 依赖 XML配置 实例化容器 使用容器 xml配置详解 ...

  6. AnnotationConfigApplicationContext容器初始化

    AnnotationConfigApplicationContext容器初始化目录 (Spring源码分析)AnnotationConfigApplicationContext容器初始化 this() ...

  7. Spring IoC(二)IoC容器的初始化过程

    (一)IoC 容器初始化过程概述 1.1简要概述初始化过程 IoC 容器的初始化过程是通过refresh() 方法来启动的,这个方法标识着IoC 容器正式启动.具体来说,这个启动过程包括:BeanDe ...

  8. 一步一步手绘Spring IOC运行时序图二(基于XML的IOC容器初始化)

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程) 一步一步手绘Spring IOC运行时序图二(基于XM ...

  9. Spring的IOC创建对象的方式和代码基本解释为什么要有ioc的思维以及Ioc容器和spring依赖注入的解释

    首先我们要知道 Ioc是个啥? ​ IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合.更优良的程序.传统应用程序都是由我们在类内部主动创建依赖对象,从而导致 ...

最新文章

  1. SAP QM 质量检验特殊业务需求之方案漫谈
  2. Spring Security OAuth 2开发者指南译
  3. Centos下Yum安装PHP5.5,5.6
  4. hdu 4923 Room and Moor (单调栈+思维)
  5. javaweb项目启动后自动查询数据库并刷新数值
  6. 修改Static控件的字体颜色
  7. Microsoft宣布为Power BI提供AI模型构建器,关键驱动程序分析和Azure机器学习集成...
  8. jquery层级原则器(匹配后代元素div)
  9. 存储树形数据_数据结构篇之顺序表的创建以及实现
  10. java 创建数组工具类_用Java创建数组工具类ArrayTool
  11. Silverlight中调用ClientBin下非xap内的xml的方法
  12. python_字符串常用方法
  13. 写, 读sdcard目录上的文件
  14. html 表格中单选框触发事件_HTML基础知识(网站同步更新)
  15. Mac 下安装pip,卸载pip方法
  16. Java实习日记(8)
  17. 基于Simulink模型的嵌入式代码生成与实际工程应用
  18. STM8S003国产替代 DP32G003 32 位微控制器芯片
  19. 在网页中加入MSN、QQ以实现即时通讯
  20. 谈谈制造企业如何制定敏捷的数字化转型策略

热门文章

  1. opencv 人脸识别_人工智能-OpenCV+Python实现人脸识别(视频人脸检测)
  2. 计算机启动慢 原因,电脑开机慢的原因
  3. pcie固态硬盘_主板2个M. 2接口,哪个m2插槽是与CPU直连?总结不得不说PCIE知识
  4. 【杂谈】为什么Pytorch这么好用我还苦口婆心推荐初学者也学习一下caffe?
  5. 【图像分割模型】多分辨率特征融合—RefineNet
  6. 中国虚拟电厂运行状况及竞争力分析报告2022-2028年版
  7. chrome浏览器使用技巧
  8. 2016级算法第一次练习赛-E.AlvinZH的儿时回忆——蛙声一片
  9. 圆环自带动画进度条ColorfulRingProgressView
  10. Spring MVC中Session的正确用法之我见02