隨筆 - 12, 文章 - 0, 評論 - 22, 引用 - 0
          數據加載中……

          Spring加載資源分析

          說明:
              這篇文檔是對Spring加載和解析xml文件過程的分析,下面看一個spring解析xml文件的測試例子。
              @Test
              public void test3BeanDefinitionReader(){
                  DefaultResourceLoader loader = new DefaultResourceLoader();
                  Resource resource = loader.getResource("/ioc/application.xml");
                  BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
                  BeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
                  
                  int count = reader.loadBeanDefinitions(resource);
                  String[] beanDefinitionNames = reader.getRegistry().getBeanDefinitionNames();
                  System.out.println("----------------------");
                  for (String name : beanDefinitionNames) {
                      System.out.println(name);
                  }
              }

          針對以上例子,得出Spring解析xml文件的流程圖和結構圖:






          Spring解析配置文件中用到的一些關鍵類的介紹:
          • Resource:各種資源的抽象接口,包括xml文件,網絡上的資源等。
          • BeanDefinitionRegistry:用于注冊BeanDefinitionRegistry。
          • BeanDefinitionReader:用于讀取解析Resource的抽象接口。
          • DefaultBeanDefinitionDocumentReader:實現了BeanDefinitionDocumentReader接口,DefaultBeanDefinitionDocumentReader并不負責任何具體的bean解析,它面向的是xml Document對象,根據其元素的命名空間和名稱,起一個類似路由的作用((不過,命名空間的判斷,也是委托給delegate來做的),它跟BeanDefinitionParserDelegate協同合作,把解析任務交接BeanDefinitionParserDelegate來做。
          • BeanDefinitionParserDelegate:完成具體Bean的解析(比如<bean>、<import>、<alias>標簽),對于擴展的標簽會交給不同的NamespaceHandler跟BeanDefinitionParser來解析。
          • BeanDefinitionParser:解析配置文件成相應的BeanDefinition(<context:component-scan>,<aop:config>等標簽都是又不同的BeanDefinitionParser來解析),一般在NamespaceHandler中使用。Spring也為自定義BeanDefinitionParser提供了很多支持,在一些抽象類的基礎上添加少量功能即可滿足大部分需求。
          • NamespaceHandler:要解析自定義的bean就要通過自己所實現的NamespaceHandler來進行解析。比如定義了http\://www.springframework.org/schema/osgi=org.springframework.osgi.config.OsgiNamespaceHandler,那么在碰到osgi的scheme的時候就會去調用OsgiNamespaceHandler來進行解析; 在對于普通的擴展需求來說,只要讓自己的Handler繼承NamespaceHandlerSupport并實現 init()方法 就好了,對于特殊的擴展需求 則可以自己 來實現NamespaceHandler。

          下面來分析一下源代碼:
          源碼1:加載xml文件
          XmlBeanDefinitionReader:
              public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
                  //加載xml資源
                  return loadBeanDefinitions(new EncodedResource(resource));
              }
           
              public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
                      
                      InputStream inputStream = encodedResource.getResource().getInputStream();
                      try {
                          InputSource inputSource = new InputSource(inputStream);
                          if (encodedResource.getEncoding() != null) {
                              //設置字符集
                              inputSource.setEncoding(encodedResource.getEncoding());
                          }
                          return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                      }

              }

              protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
                      throws BeanDefinitionStoreException {
                  try {
                      int validationMode = getValidationModeForResource(resource);
                      //根據xml文件,得到標準的Document對象
                      Document doc = this.documentLoader.loadDocument(
                              inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
                      return registerBeanDefinitions(doc, resource);

              }
           
              public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
                  //BeanDefinitionDocumentReader實際解析doc文檔
                  BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
                  int countBefore = getRegistry().getBeanDefinitionCount();
                  documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
                  return getRegistry().getBeanDefinitionCount() - countBefore;
              }
           
          DefaultBeanDefinitionDocumentReader:
              public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {

                  Element root = doc.getDocumentElement();
                  //元素解析的代理類,主要的bean解析,以及一些自定義元素的解析
                  BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
                  //默認為空操作
                  preProcessXml(root);
                  //解析元素
                  parseBeanDefinitions(root, delegate);
                  postProcessXml(root);
              }
           
              protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
                  //默認命名空間為http://www.springframework.org/schema/beans,也就是xml文件中<bean>的標簽
                  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)) {
                                  //處理默認<bean>標簽
                                  parseDefaultElement(ele, delegate);
                              }
                              else {
                                  //處理自定義標簽 (i.e.  AOP,CONTENT,JDBC)
                                  delegate.parseCustomElement(ele);
                              }
                          }
                      }
                  }
                  else {//處理自定義標簽
                      delegate.parseCustomElement(root);
                  }
              }

          源碼2:基本元素解析(i.e.  <bean>、<import>......)
          DefaultBeanDefinitionDocumentReader:
              private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
                  if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
                      //解析<import>標簽
                      importBeanDefinitionResource(ele);
                  }
                  else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
                      //解析<alias>標簽
                      processAliasRegistration(ele);
                  }
                  else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
                      //解析<bean>標簽
                      processBeanDefinition(ele, delegate);
                  }
              }
           
              protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
                  //解析element,得到BeanDefinition,BeanDefinitionHolder中包含了BeanDefinition
                  BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

                          //注冊BeanDefinition
                          BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

              }
           
          BeanDefinitionParserDelegate:
              public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
                  return parseBeanDefinitionElement(ele, null);
              }
           
          public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
                  String id = ele.getAttribute(ID_ATTRIBUTE);
                  String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
                  List<String> aliases = new ArrayList<String>();
                  if (StringUtils.hasLength(nameAttr)) {
                      String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);
                      aliases.addAll(Arrays.asList(nameArr));
                  }
                  String beanName = id;

                  if (containingBean == null) {
                      //檢查bean name的唯一性
                      checkNameUniqueness(beanName, aliases, ele);
                  }
                  //解析Element
                  AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
                  if (beanDefinition != null) {

                      String[] aliasesArray = StringUtils.toStringArray(aliases);
                      return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
                  }
                  return null;
              }
           
          //真正解析<bean>的地方
          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();
                  }

                      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;

                  return null;
              }

          源碼3:處理擴展或自定義標簽:
          BeanDefinitionParserDelegate:
              public BeanDefinition parseCustomElement(Element ele) {
                  return parseCustomElement(ele, null);
              }

              public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
                  String namespaceUri = getNamespaceURI(ele);
                  //根據元素的命名空間得到NamespaceHandler
                  NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);

                  //使用合適的NamespaceHandler解析元素
                  return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
              }

          NamespaceHandlerSupport:
              public BeanDefinition parse(Element element, ParserContext parserContext) {
                  //得到真正具體處理自定義的BeanDefinitionParser,解析element得到BeanDefinition
                  return findParserForElement(element, parserContext).parse(element, parserContext);
              }


              private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
                  String localName = parserContext.getDelegate().getLocalName(element);
                  //parsers中包含了具體處理自定義標簽的類
                  BeanDefinitionParser parser = this.parsers.get(localName);

                  return parser;
              }


          代碼3.2:在源碼3.1中代碼:{NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri)}這步中,展示了對于擴展或自定義的標簽的處理類是從哪里加載的
             public NamespaceHandler resolve(String namespaceUri) {
                  //得到Map<Element名稱,NamespaceHandler處理類>
                  Map<String, Object> handlerMappings = getHandlerMappings();
                  //根據namespaceUri得到匹配的NamespaceHandler
                  Object handlerOrClassName = handlerMappings.get(namespaceUri);
                  if (handlerOrClassName == null) {
                      return null;
                  }
                  else if (handlerOrClassName instanceof NamespaceHandler) {
                      return (NamespaceHandler) handlerOrClassName;
                  }else{

                          }
              }

              private Map<String, Object> getHandlerMappings() {

          //handlerMappingsLocation默認值:META-INF/spring.handlers
          //
          spring就會從jar包中的Meta-INF/spring.handlers文件中得到處理各種不同命名空間元素的類
                                  Properties mappings =
                                          PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);

                                  Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>();
                                  CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
                                  this.handlerMappings = handlerMappings;

                  return this.handlerMappings;
              }

                當初一直在想Spring是如何解析自定義元素(i.e. <context:component-scan>,<aop:config> )。通過源碼分析看出spring在解析的過程中,會去收集spring.*.jar/META-INF下的spring.handers,spring.schemas文件,這2個文件就是指明了解析spring中自定義標簽的Namespace類。如果自己開發Spring組件,需要增加新的標簽,也可以按照這個機制。

          參考:
          http://kyfxbl.iteye.com/blog/1610255 (小讀spring ioc源碼)
          http://blog.csdn.net/cutesource/article/details/5864562 (基于Spring可擴展Schema提供自定義配置支持)

          posted on 2013-10-28 00:12 heavensay 閱讀(11419) 評論(2)  編輯  收藏 所屬分類: java spring

          評論

          # re: Spring加載資源分析  回復  更多評論   

          謝謝博主分享
          2013-10-28 12:44 | 魏五鎖業

          # re: Spring加載資源分析  回復  更多評論   

          是春天來了嗎
          2013-10-29 12:48 | 太陽城娛樂城

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 邵阳市| 秦皇岛市| 青州市| 宣化县| 伽师县| 辛集市| 聂拉木县| 淅川县| 沙洋县| 南阳市| 石嘴山市| 涪陵区| 龙海市| 咸丰县| 北海市| 新绛县| 盖州市| 文昌市| 汉川市| 阆中市| 阿拉尔市| 根河市| 廉江市| 偃师市| 枣阳市| 德化县| 林口县| 西丰县| 阳泉市| 申扎县| 小金县| 永川市| 肥城市| 济宁市| 安阳县| 仙居县| 会泽县| 阿坝| 江孜县| 古丈县| 拉萨市|