1. 程式人生 > >【Spring原始碼解析】—— 結合SpringMVC過程理解IOC容器初始化之註解部分探究

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

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

因為會與之前的內容存在部分重疊,因此會針對相同的部分簡略帶過,針對不同的部分做重點說明:

 

一、Xml的配置和程式碼中的註解配置:

applicationContext.xml配置新增:

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

 

程式碼中配置註解修改:

@Controller

public class TestController {

    @RequestMapping("/test.form")

    public void execute(){

        return ;

    }

}

二、詳解:

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

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

類的例項wac.refresh()呼叫中,至此進入到具體接下來的load階段了

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過程

具體可見下方程式碼:

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

 

三、過程時序圖(梳理主要呼叫邏輯,涉及類和方法,可通過下載大圖檢視)