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

 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM