zoukankan      html  css  js  c++  java
  • spring源码解析之IOC容器(二)------加载和注册

      上一篇跟踪了IOC容器对配置文件的定位,现在我们继续跟踪代码,看看IOC容器是怎么加载和注册配置文件中的信息的。开始之前,首先我们先来了解一下IOC容器所使用的数据结构-------BeanDefinition,它是一个上层接口,有很多实现类,分别对应不同的数据载体。我们平时开发的时候,也会定义很多pojo类,来作为获取数据的载体。最常见的就是,从数据库中获取数据之后,使用一个定义的pojo来装载,然后我们就可以在程序中使用这个pojo类来编写各种业务逻辑。同样,IOC容器首先会读取配置的XML中各个节点,即各个标签元素,然后根据不同的标签元素,使用不同的数据结构来装载该元素中的各种属性的值。比如我们最熟悉的<bean>标签,就是使用AbstractBeanDefinition这个数据结构,接下来的分析中我们可以看到。

      先回到上篇资源的定位那里,代码如下:

     1 public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
     2         ResourceLoader resourceLoader = getResourceLoader();
     3         if (resourceLoader == null) {
     4             throw new BeanDefinitionStoreException(
     5                     "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
     6         }
     7 
     8         if (resourceLoader instanceof ResourcePatternResolver) {
     9             // Resource pattern matching available.
    10             try {
    11                 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
    12                 int loadCount = loadBeanDefinitions(resources);
    13                 if (actualResources != null) {
    14                     for (Resource resource : resources) {
    15                         actualResources.add(resource);
    16                     }
    17                 }
    18                 if (logger.isDebugEnabled()) {
    19                     logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
    20                 }
    21                 return loadCount;
    22             }
    23             catch (IOException ex) {
    24                 throw new BeanDefinitionStoreException(
    25                         "Could not resolve bean definition resource pattern [" + location + "]", ex);
    26             }
    27         }
    28         else {
    29             // 定位到资源之后,封装成一个resource对象
    30             Resource resource = resourceLoader.getResource(location);
    31             int loadCount = loadBeanDefinitions(resource);
    32             if (actualResources != null) {
    33                 actualResources.add(resource);
    34             }
    35             if (logger.isDebugEnabled()) {
    36                 logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
    37             }
    38             return loadCount;
    39         }
    40     }

      进入loadBeanDefinitions(resource)方法,正式开始加载源码的跟踪:

    1         @Override
    2     public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    3         return loadBeanDefinitions(new EncodedResource(resource));
    4     }    
     1 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
     2         Assert.notNull(encodedResource, "EncodedResource must not be null");
     3         if (logger.isInfoEnabled()) {
     4             logger.info("Loading XML bean definitions from " + encodedResource);
     5         }
     6 
     7         Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
     8         if (currentResources == null) {
     9             currentResources = new HashSet<EncodedResource>(4);
    10             this.resourcesCurrentlyBeingLoaded.set(currentResources);
    11         }
    12         if (!currentResources.add(encodedResource)) {
    13             throw new BeanDefinitionStoreException(
    14                     "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    15         }
    16         try {
    17             InputStream inputStream = encodedResource.getResource().getInputStream();
    18             try {
    19                 InputSource inputSource = new InputSource(inputStream);
    20                 if (encodedResource.getEncoding() != null) {
    21                     inputSource.setEncoding(encodedResource.getEncoding());
    22                 }
    23                 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    24             }
    25             finally {
    26                 inputStream.close();
    27             }
    28         }
    29         catch (IOException ex) {
    30             throw new BeanDefinitionStoreException(
    31                     "IOException parsing XML document from " + encodedResource.getResource(), ex);
    32         }
    33         finally {
    34             currentResources.remove(encodedResource);
    35             if (currentResources.isEmpty()) {
    36                 this.resourcesCurrentlyBeingLoaded.remove();
    37             }
    38         }
    39     }

      进入doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法:

     1 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
     2             throws BeanDefinitionStoreException {
     3         try {
     4             Document doc = doLoadDocument(inputSource, resource);
     5             return registerBeanDefinitions(doc, resource);
     6         }
     7         catch (BeanDefinitionStoreException ex) {
     8             throw ex;
     9         }
    10         catch (SAXParseException ex) {
    11             throw new XmlBeanDefinitionStoreException(resource.getDescription(),
    12                     "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
    13         }
    14         catch (SAXException ex) {
    15             throw new XmlBeanDefinitionStoreException(resource.getDescription(),
    16                     "XML document from " + resource + " is invalid", ex);
    17         }
    18         catch (ParserConfigurationException ex) {
    19             throw new BeanDefinitionStoreException(resource.getDescription(),
    20                     "Parser configuration exception parsing XML from " + resource, ex);
    21         }
    22         catch (IOException ex) {
    23             throw new BeanDefinitionStoreException(resource.getDescription(),
    24                     "IOException parsing XML document from " + resource, ex);
    25         }
    26         catch (Throwable ex) {
    27             throw new BeanDefinitionStoreException(resource.getDescription(),
    28                     "Unexpected exception parsing XML document from " + resource, ex);
    29         }
    30     }

      继续进入registerBeanDefinitions(doc, resource)方法:

    1 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    2         //此时documentReader已经是DefaultBeanDefinitionDocumentReader类了
    3         BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    4         int countBefore = getRegistry().getBeanDefinitionCount();
    5         documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    6         //返回当前注册的beanDefinition的个数
    7         return getRegistry().getBeanDefinitionCount() - countBefore;
    8     }

      进入registerBeanDefinitions(doc, createReaderContext(resource))方法:

    1 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    2         this.readerContext = readerContext;
    3         logger.debug("Loading bean definitions");
    4         Element root = doc.getDocumentElement();
    5         doRegisterBeanDefinitions(root);
    6     }

      进入doRegisterBeanDefinitions(root)方法:

     1 protected void doRegisterBeanDefinitions(Element root) {
     2         // Any nested <beans> elements will cause recursion in this method. In
     3         // order to propagate and preserve <beans> default-* attributes correctly,
     4         // keep track of the current (parent) delegate, which may be null. Create
     5         // the new (child) delegate with a reference to the parent for fallback purposes,
     6         // then ultimately reset this.delegate back to its original (parent) reference.
     7         // this behavior emulates a stack of delegates without actually necessitating one.
     8         BeanDefinitionParserDelegate parent = this.delegate;
     9         this.delegate = createDelegate(getReaderContext(), root, parent);
    10 
    11         if (this.delegate.isDefaultNamespace(root)) {
    12             //profile属性平时使用非常少,该属性可以用于配置数据库的切换(常用),使用时,需要在web.xml中配置context-parm
    13             //<context-parm>
    14             //    <parm-name>Spring.profiles.active</parm-name>
    15             //    <parm-value>dev(在applicationContext.xml中配置的profile属性的beans的profile属性值)</parm-name>
    16             //</context-parm>
    17             //在applicationContext.xml中的配置
    18             //<beans profile="dev">    </beans>
    19             //<beans profile="produce">   </beans>
    20             String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
    21             if (StringUtils.hasText(profileSpec)) {
    22                 String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
    23                         profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
    24                 if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
    25                     if (logger.isInfoEnabled()) {
    26                         logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
    27                                 "] not matching: " + getReaderContext().getResource());
    28                     }
    29                     return;
    30                 }
    31             }
    32         }
    33 
    34         preProcessXml(root);
    35         parseBeanDefinitions(root, this.delegate);
    36         postProcessXml(root);
    37 
    38         this.delegate = parent;
    39     }

      这里也用到了模板方法,preProcessXml(root)和postProcessXml(root)这两个方法都是空实现,是留给客户来实现自己的逻辑的。重点研究一下parseBeanDefinitions(root, this.delegate)方法:

     1 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
     2         if (delegate.isDefaultNamespace(root)) {
     3             NodeList nl = root.getChildNodes();
     4             for (int i = 0; i < nl.getLength(); i++) {
     5                 Node node = nl.item(i);
     6                 if (node instanceof Element) {
     7                     Element ele = (Element) node;
     8                     if (delegate.isDefaultNamespace(ele)) {
     9                         parseDefaultElement(ele, delegate);
    10                     }
    11                     else {
    12                         delegate.parseCustomElement(ele);
    13                     }
    14                 }
    15             }
    16         }
    17         else {
    18             delegate.parseCustomElement(root);
    19         }
    20     }

      parseCustomElement(root)方法不需要怎么研究,我们平时几乎不会用到自定义的标签,所以只跟踪parseDefaultElement(ele, delegate)里面的代码:

    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);
            }
        }

      可以看到,对于不同的标签,spring采用不同的策略进行处理,重点跟踪一下处理bean标签的方法processBeanDefinition(ele, delegate):

     1 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
     2         //委托给delegate去进行各种标签的解析,parseBeanDefinitionElement方法中包含了各种标签元素的解析,
     3         //并将解析好的内容封装成BeanDefinitionHolder对象
     4         BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
     5         if (bdHolder != null) {
     6             bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
     7             try {
     8                 // Register the final decorated instance.
     9                 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
    10             }
    11             catch (BeanDefinitionStoreException ex) {
    12                 getReaderContext().error("Failed to register bean definition with name '" +
    13                         bdHolder.getBeanName() + "'", ele, ex);
    14             }
    15             // Send registration event.
    16             getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    17         }
    18     }

      在这个方法中,delegate.parseBeanDefinitionElement(ele)是解析bean元素中各种属性的方法,registerBeanDefinition(bdHolder, getReaderContext().getRegistry())是将封装好的数据进行存储的方法。先看一下解析的方法:

     1 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
     2         //获取bean标签的id属性的值
     3         String id = ele.getAttribute(ID_ATTRIBUTE);
     4         //获取bean标签上name属性的值
     5         String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
     6 
     7         List<String> aliases = new ArrayList<String>();
     8         if (StringUtils.hasLength(nameAttr)) {
     9             //将name的值进行分割,并将它们当作别名存到aliases中
    10             String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
    11             aliases.addAll(Arrays.asList(nameArr));
    12         }
    13 
    14         String beanName = id;
    15         //如果bean标签的id没有值,但是name属性有值,则将name属性的第一个值当作id的值,并从aliases中将第一个别名移除掉
    16         if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
    17             beanName = aliases.remove(0);
    18             if (logger.isDebugEnabled()) {
    19                 logger.debug("No XML 'id' specified - using '" + beanName +
    20                         "' as bean name and " + aliases + " as aliases");
    21             }
    22         }
    23 
    24         if (containingBean == null) {
    25             //检查bean的唯一性
    26             checkNameUniqueness(beanName, aliases, ele);
    27         }
    28 
    29         //这里已经是将XML中bean元素中的所有属性都封装到beanDefinition对象中了
    30         AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    31         if (beanDefinition != null) {
    32             if (!StringUtils.hasText(beanName)) {
    33                 try {
    34                     if (containingBean != null) {
    35                         beanName = BeanDefinitionReaderUtils.generateBeanName(
    36                                 beanDefinition, this.readerContext.getRegistry(), true);
    37                     }
    38                     else {
    39                         beanName = this.readerContext.generateBeanName(beanDefinition);
    40                         // Register an alias for the plain bean class name, if still possible,
    41                         // if the generator returned the class name plus a suffix.
    42                         // This is expected for Spring 1.2/2.0 backwards compatibility.
    43                         String beanClassName = beanDefinition.getBeanClassName();
    44                         if (beanClassName != null &&
    45                                 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
    46                                 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
    47                             aliases.add(beanClassName);
    48                         }
    49                     }
    50                     if (logger.isDebugEnabled()) {
    51                         logger.debug("Neither XML 'id' nor 'name' specified - " +
    52                                 "using generated bean name [" + beanName + "]");
    53                     }
    54                 }
    55                 catch (Exception ex) {
    56                     error(ex.getMessage(), ele);
    57                     return null;
    58                 }
    59             }
    60             String[] aliasesArray = StringUtils.toStringArray(aliases);
    61             //最后将封装好的beanDefinition、它的id、以及它的别名一起封装成BeanDefinitionHolder对象返回
    62             return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    63         }
    64 
    65         return null;
    66     }

      我们可以得到如下信息:

      1、获取bean标签的id属性和name属性的值;

      2、name属性是可以都有多个值的,以逗号或者分号分割;

      3、如果id没有赋值,则取name的第一个值作为id的值。所以,我们一般都会给id赋值,这样效率高一些;

      4、检查以这个id标识的bean是不是唯一的;

      5、进行其他属性的解析,并最终封装测AbstractBeanDefinition对象,也就是我们前文中提到的数据结构;

      6、最后封装成BeanDefinitionHolder对象之后返回。

      进入parseBeanDefinitionElement(ele, beanName, containingBean)方法,看一下其他元素的解析过程:

     1 public AbstractBeanDefinition parseBeanDefinitionElement(
     2             Element ele, String beanName, BeanDefinition containingBean) {
     3 
     4         this.parseState.push(new BeanEntry(beanName));
     5 
     6         String className = null;
     7         if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
     8             className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
     9         }
    10 
    11         try {
    12             String parent = null;
    13             if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
    14                 parent = ele.getAttribute(PARENT_ATTRIBUTE);
    15             }
    16             AbstractBeanDefinition bd = createBeanDefinition(className, parent);
    17 
    18             parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
    19             bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
    20 
    21             parseMetaElements(ele, bd);
    22             parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
    23             parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
    24 
    25             parseConstructorArgElements(ele, bd);
    26             parsePropertyElements(ele, bd);
    27             parseQualifierElements(ele, bd);
    28 
    29             bd.setResource(this.readerContext.getResource());
    30             bd.setSource(extractSource(ele));
    31 
    32             return bd;
    33         }
    34         catch (ClassNotFoundException ex) {
    35             error("Bean class [" + className + "] not found", ele, ex);
    36         }
    37         catch (NoClassDefFoundError err) {
    38             error("Class that bean class [" + className + "] depends on not found", ele, err);
    39         }
    40         catch (Throwable ex) {
    41             error("Unexpected failure during bean definition parsing", ele, ex);
    42         }
    43         finally {
    44             this.parseState.pop();
    45         }
    46 
    47         return null;
    48     }

      解析封装成BeanDefinitionHolder对象之后,就可以进行注册了,先回到之前的processBeanDefinition(ele, delegate):

     1 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
     2         //委托给delegate去进行各种标签的解析,parseBeanDefinitionElement方法中包含了各种标签元素的解析,
     3         //并将解析好的内容封装成BeanDefinitionHolder对象
     4         BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
     5         if (bdHolder != null) {
     6             bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
     7             try {
     8                 // Register the final decorated instance.
     9                 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
    10             }
    11             catch (BeanDefinitionStoreException ex) {
    12                 getReaderContext().error("Failed to register bean definition with name '" +
    13                         bdHolder.getBeanName() + "'", ele, ex);
    14             }
    15             // Send registration event.
    16             getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    17         }
    18     }

      现在进入BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())方法进行分析:

     1 public static void registerBeanDefinition(
     2             BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
     3             throws BeanDefinitionStoreException {
     4 
     5         // Register bean definition under primary name.
     6         String beanName = definitionHolder.getBeanName();
     7         registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
     8 
     9         // Register aliases for bean name, if any.
    10         String[] aliases = definitionHolder.getAliases();
    11         if (aliases != null) {
    12             for (String alias : aliases) {
    13                 registry.registerAlias(beanName, alias);
    14             }
    15         }
    16     }

      这里的beanName就是之前封装好的bean的id。这个方法中分别以id和别名作为key来注册bean,其实就是存储在map中。

      进入registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()),在其子类DefaultListableBeanFactory中有实现:

     1 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
     2             throws BeanDefinitionStoreException {
     3 
     4         Assert.hasText(beanName, "Bean name must not be empty");
     5         Assert.notNull(beanDefinition, "BeanDefinition must not be null");
     6 
     7         if (beanDefinition instanceof AbstractBeanDefinition) {
     8             try {
     9                 ((AbstractBeanDefinition) beanDefinition).validate();
    10             }
    11             catch (BeanDefinitionValidationException ex) {
    12                 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    13                         "Validation of bean definition failed", ex);
    14             }
    15         }
    16 
    17         BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    18         if (existingDefinition != null) {
    19             if (!isAllowBeanDefinitionOverriding()) {
    20                 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    21                         "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
    22                         "': There is already [" + existingDefinition + "] bound.");
    23             }
    24             else if (existingDefinition.getRole() < beanDefinition.getRole()) {
    25                 // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
    26                 if (logger.isWarnEnabled()) {
    27                     logger.warn("Overriding user-defined bean definition for bean '" + beanName +
    28                             "' with a framework-generated bean definition: replacing [" +
    29                             existingDefinition + "] with [" + beanDefinition + "]");
    30                 }
    31             }
    32             else if (!beanDefinition.equals(existingDefinition)) {
    33                 if (logger.isInfoEnabled()) {
    34                     logger.info("Overriding bean definition for bean '" + beanName +
    35                             "' with a different definition: replacing [" + existingDefinition +
    36                             "] with [" + beanDefinition + "]");
    37                 }
    38             }
    39             else {
    40                 if (logger.isDebugEnabled()) {
    41                     logger.debug("Overriding bean definition for bean '" + beanName +
    42                             "' with an equivalent definition: replacing [" + existingDefinition +
    43                             "] with [" + beanDefinition + "]");
    44                 }
    45             }
    46             this.beanDefinitionMap.put(beanName, beanDefinition);
    47         }
    48         else {
    49             if (hasBeanCreationStarted()) {
    50                 // Cannot modify startup-time collection elements anymore (for stable iteration)
    51                 synchronized (this.beanDefinitionMap) {
    52                     this.beanDefinitionMap.put(beanName, beanDefinition);
    53                     List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
    54                     updatedDefinitions.addAll(this.beanDefinitionNames);
    55                     updatedDefinitions.add(beanName);
    56                     this.beanDefinitionNames = updatedDefinitions;
    57                     if (this.manualSingletonNames.contains(beanName)) {
    58                         Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
    59                         updatedSingletons.remove(beanName);
    60                         this.manualSingletonNames = updatedSingletons;
    61                     }
    62                 }
    63             }
    64             else {
    65                 // Still in startup registration phase
    66                 this.beanDefinitionMap.put(beanName, beanDefinition);
    67                 this.beanDefinitionNames.add(beanName);
    68                 this.manualSingletonNames.remove(beanName);
    69             }
    70             this.frozenBeanDefinitionNames = null;
    71         }
    72 
    73         if (existingDefinition != null || containsSingleton(beanName)) {
    74             resetBeanDefinition(beanName);
    75         }
    76     }

      我们可以看到:这个beanDefinitionMap就是用来存储解析好的bean的,以id作为key。至此,就将所有的bean标签解析好之后封装成BeanDefinition注册到了IOC容器中。但是,到目前为止,IOC容器并没有为我们将这些解析好的数据生成一个一个bean实例,我们仍然不能就这样直接使用。下一篇接着跟踪。

  • 相关阅读:
    web中间件常见漏洞
    心脏滴血与利用
    mimikatz提取windows密码
    Linux文本编辑器
    Linux打包(归档 )压缩命令
    linux文件和目录命令
    SSL原理
    windows server 2008 安装步骤
    渗透测试术语
    centos 7 修改yum配置
  • 原文地址:https://www.cnblogs.com/helei123/p/11080191.html
Copyright © 2011-2022 走看看