【Spring源碼解析】—— 結合SpringMVC過程理解IOC容器初始化之註解部分探究

前面的文章寫了xml中直接配置bean進行IOC的過程解析,接下來會針對註解進行IOC容器初始化的過程解析node

由於會與以前的內容存在部分重疊,所以會針對相同的部分簡略帶過,針對不一樣的部分作重點說明:spring

 

1、Xml的配置和代碼中的註解配置:

applicationContext.xml配置添加:數組

<context:component-scan base-package="cn.lx.controller" />app

 

代碼中配置註解修改:函數

@Controller

public class TestController {

    @RequestMapping("/test.form")

    public void execute(){

        return ;

    }

}

2、詳解:

入口部分:ContextLoaderListener類中的contextInitialized,進入到ContextLoader類的initWebApplicationContextpost

方法中,該方法中執行的關鍵方法是:configureAndRefreshWebApplicationContext()進入到ConfigurableWebApplicationContextui

類的實例wac.refresh()調用中,至此進入到具體接下來的load階段了this

load過程:

進入到AbstractApplicationContext類的refresh()類中,以後進入到ObtainFreshBeanFactory()方法中,一路往下跟進到實現方法,以後進入到:AbstractRefreshableApplicationContext類中的refreshBeanFactory()方法,在此方法中,進行CreateFactory()會獲得DefaultListableBeanFactory類的一個實例beanFactory,以後會做爲方法參數傳入到loadBeanDefinitions(beanFactory)中,這裏其實就是能明顯看到有load字眼啦,繼續一步步往下跟進,進入到真正作事情的方法就是doLoadBeanDefinitions中,這裏會生成一個BeanDefinitionDocumentReader類的實例,以後經過該實例調用方法registerBeanDefinitions,依然是要進入到真正作事的doRegisterBeanDefinitions方法中,至此就立刻到了process的部分了,在這個部分會針對傳入的元素進行解析前、中、後的處理,咱們進入到解析中的方法:parseBeanDefinitions(root, this.delegate),在解析的方法中,會判斷若是是bean相關namespace的,則會parseDefaultElement,由於這裏是註解的形式,所以其nameSpace不爲默認的bean相關的,而是Context的,所以進入到:delegate.parseCustomElement(ele)中,接下來就是具體基於註解進行解析的部分了,即process過程spa

具體可見下方代碼:代理

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 {
               //註解定義的Context的nameSpace進入到這個分支中
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      delegate.parseCustomElement(root);
   }
}

 

process過程:

具體process過程作了哪些事情呢?可分爲兩個步驟來講明,首先根據ele的定義獲得key,經過key返回對應的namespaceUri,以後根據namespaceUri的解析獲得一個NameSpaceHandler的實例handler,以後由具體實現了NameSpaceHandler接口的類NameSpaceHandlerSupport類進行的方法調用,即parse方法的調用,即離具體的解析更進一步啦~

可見下方代碼註釋部分:

//常規解析方法
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
   //獲取namespaceUri,例如xml中配置的若是是<context:componet-scan base-packages:xxx>
   //這裏的ele會獲得component-scan,而且值爲null,namespaceUri爲 http://www.springframework.org/schema/context
   String namespaceUri = getNamespaceURI(ele);
   if (namespaceUri == null) {
      return null;
   }
   //基於namespaceUri獲得handler,獲得handler以後,不一樣的handler實現了parse方法,到具體的parse去進行調用和處理
   //由於Handler爲:org.springframework.context.config.ContextNamespaceHandler
   //在resolve的init操做中直接將component-scan的key和對應的ComponentScanBeanDefinitionParser的實例放入到了parser的map中
   NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
   if (handler == null) {
      error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
      return null;
   }
   //NameSpaceHandler是接口,具體這裏調用的就是獲得的handler的實際類型,經過它進行parse調用
   //實現該Handler的類是:NamespaceHandlerSupport
   return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

下面繼續說明一下經過namespaceUri的resolve具體是如何實現的的?接下來是對這裏的詳細說明:

首先this.readerContext.getNamespaceHandlerResolver()返回內容爲:

public final NamespaceHandlerResolver getNamespaceHandlerResolver() {
   return this.namespaceHandlerResolver;
}

注意這裏定義的是final,final的方法不能被重寫,由於返回的是NamespaceHandlerResolver,發現其是一個接口,所以直接找實現類,點擊resolve找到對應的實現類DefaultNamespaceHandler(注意這裏又體現了,最終真的去作事情的,不少都會被命名爲Defaultxxx類,或者Simplexxx類)對該方法的實現,resolve中所作事項就是斷定是否已有可用解析類,若無則進行初始化init操做,而且返回handler實例

public NamespaceHandler resolve(String namespaceUri) {
      //先經過handlerMappings的配置進行get獲取,針對傳入的namespaceUri是否存在handler可供使用
      Map<String, Object> handlerMappings = getHandlerMappings();
      //注意:這裏的從mapping中獲得的key爲Object的,由於這裏可能爲各類不一樣的具體Handeler,namespaceUri不一樣,則其value不一樣
      //這裏根據namespaceUri爲context的值:org.springframework.context.config.ContextNamespaceHandler,發現不爲null
      Object handlerOrClassName = handlerMappings.get(namespaceUri);
      //if判斷不爲空,跳過此邏輯
      if (handlerOrClassName == null) {
         return null;
      }
      //判斷不爲NamespaceHandler的實例
      else if (handlerOrClassName instanceof NamespaceHandler) {
         return (NamespaceHandler) handlerOrClassName;
      }
      else {
         //直接將前面的「org.springframework.context.config.ContextNamespaceHandler」轉成String類型的
         String className = (String) handlerOrClassName;
         try {
            //生成類
            Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
            if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
               throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                     "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
            }
            //類的實例化,若是是前一個函數中的<context:Component-scan>這裏獲得的Handler爲:
         //org.springframework.context.config.ContextNamespaceHandler
            NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
            //由於上面是ContextNamespaceHandler,將N種不一樣的key對應的具體的parser進行new以後做爲key放到parsers的map中
            //這裏針對key爲"component-scan",直接new的實例就是:ComponentScanBeanDefinitionParser
            namespaceHandler.init();
            handlerMappings.put(namespaceUri, namespaceHandler);
            return namespaceHandler;
         }
         catch (ClassNotFoundException ex) {
            throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
                  "] for namespace [" + namespaceUri + "]", ex);
         }
         catch (LinkageError err) {
            throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
                  className + "] for namespace [" + namespaceUri + "]", err);
         }
      }
   }

以後經過handler實例進行parse方法調用,實現類爲:NamespaceHandlerSupport,其中parse方法以下,就是找到真正的parser,從以前的handler的init操做所put的map中將須要解析的key對應的value即解析類實例取出,而後進行真正的解析操做,在parse中會將bean定義註冊代理給scanner類實例,以後經過scanner.doScan()方法調用真正完成bean的解析和註冊到容器中

public BeanDefinition parse(Element element, ParserContext parserContext) {
   //肯定是什麼parser,以前已經存儲在Handler的parsers的map中
   //find就是找到對應element對應的具體key的具體解析類
   BeanDefinitionParser parser = findParserForElement(element, parserContext);
   //根據具體解析類,直接進行對應的解析調用
   return (parser != null ? parser.parse(element, parserContext) : null);
}

就進入到parser.parse()部分,由於對應key爲Component-scan對應的解析類爲ComponentScanBeanDefinitionParser類,進入到此類的parse方法中:

public BeanDefinition parse(Element element, ParserContext parserContext) {
   //根據element爲「base=package」獲得基礎包路徑
   String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
   //解析basePacakge的String值,將佔位符先後綴都去掉
   basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
   //這裏其實就是對String的basePackage進行解析,最終獲得:
   //按照分隔符將多個路徑轉換成數組,並去掉空格以及換行符等
   String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
         ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

   // Actually scan for bean definitions and register them.
   //進行beandefinition的掃描並註冊
   //將bean定義註冊代理給scanner類處理
   ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
   //doScan的調用
   Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
   registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

   return null;
}

進入到比較重要的部分:ClasspathBeanDefinitionScanner類的doScan()方法,該方法主要作的事情是:針對xml中所作的配置<context:component-scan base-package="cn.lx.controller" />,根據base-package的package路徑下的class文件,進行遍歷操做,根據其是否具有註解定義,獲得beanDefinition的候選集合,針對候選集中的每個beanDefinition,進行beanName的生成,而且針對是否屬於AbstractBeanDefinition和AnnotatedBeanDefinition,進行相應的屬性設置,以後會經過beanName獲取是否已經容器中是否已經存在此beanName,若無則直接返回true,表示須要進行後續註冊操做,即進入到了register的過程

其中doScan的主要方法及註釋以下:

//針對xml中所作的配置<context:component-scan base-package="cn.lx.controller" />
//根據base-package的package路徑下的class文件,進行遍歷操做
//根據其是否具有註解定義,獲得beanDefinition的候選集合
//針對候選集中的每個beanDefinition,進行beanName的生成
//並生成BeanDefinitionHolder,進行scopeProxyMode的設置,以後進行register操做
//和非註解形式的合併到一路上了,後續的註冊操做
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
   //針對basePackages中的每一項basePackage作循環
   for (String basePackage : basePackages) {
      //獲得候選BeanDefinition集合,注意這裏若是沒有@Controller等註解樣式的是不會被加入到候選集中
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      //針對集合中的每個候選項BeanDefinition進行詳細的解析和處理
      for (BeanDefinition candidate : candidates) {
         //這裏的生成內容會基於scope的字符進行解析,會獲得:
         //scopedName和scopedProxyMode,其中scopeName不單獨說明的話,則默認爲singleton,ProxyMode爲No
         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
         //基於解析得到scope的性質給candidate設值
         candidate.setScope(scopeMetadata.getScopeName());
         //獲取beanName,會走兩步判斷,有指定beanName的會直接賦值返回,不然會build默認的name,即shortName,例如:
         //cn.lx.controller.LoginController,則會取LoginController,而且會將首字母小寫,最終形式是:loginController
         String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
         //判斷beanDefinition的類型
         if (candidate instanceof AbstractBeanDefinition) {
            postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
         }
         //基於註解類型的BeanDefinition
         //其實沒有看AbstractBeanDefinition和AnnotatedBeanDefinition有何不一樣,寫具體文章的時候要看
         //AnnotatedBeanDefinition是接口
         //其中在candidates的生成方法findCandidateComponents()進行candidates的候選集的生成中
         //ScannedGenericBeanDefinition是實現了AnnotatedBeanDefinition接口的
         //返回的beanDefinition自己就是就是實現了這個接口的,所以必然知足instanceof
         if (candidate instanceof AnnotatedBeanDefinition) {
            AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
         }
         //對beanName和候選項的bean進行校驗,以肯定是否要進行註冊,仍是可能與已有bean存在衝突
         if (checkCandidate(beanName, candidate)) {
            //BeanDefinition的持有者
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
            //設置ScopeProxyMode,若爲設置,則默認爲No,直接返回beanDefinition
            definitionHolder =
              AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            beanDefinitions.add(definitionHolder);
            //真正標識着下一步就是register的操做了,只是要一步步走到DefaultListableBeanFactory類實例中的註冊方法的調用
            registerBeanDefinition(definitionHolder, this.registry);
         }
      }
   }
   return beanDefinitions;
}

register過程:

register過程無所謂是針對有無註解的狀況,都是相同的邏輯,經過ClassPathBeanDefinitionScanner類的registerBeanDefition方法,其實調用的是BeanDefinitionReaderUtils類的靜態方法registerBeanDefinition,以後再跟進到此靜態方法中,就進入到了DefaultListableBeanFactory類的register操做,就會進行真正的map操做了

protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
   BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}

 

3、過程時序圖(梳理主要調用邏輯,涉及類和方法,可經過下載大圖查看)

相關文章
相關標籤/搜索