spring 自定義標籤(component-scan)的詳細解析 二

上一篇我們講解了spring.xml 和默認標籤的解析過程,今天我們重點講解自定義標籤 (以註解掃描標籤component-scan爲例),詳細講解掃描標籤的解析過程並把有註解的類,封裝成BeanDefinition對象。

1、從DefaultBeanDefinitionDocumentReader 裏開始

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 {
               // 今天主要講解  自定義標籤解析,點擊進入 
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      delegate.parseCustomElement(root);
   }
}

2、進入BeanDefinitionParserDelegate

@Nullable
public BeanDefinition parseCustomElement(Element ele) {
   return parseCustomElement(ele, null);//繼續點擊
}
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
//獲取標籤的uri 例如註解標籤的uri:http\://www.springframework.org/schema/context
   String namespaceUri = getNamespaceURI(ele);
   if (namespaceUri == null) {
      return null;
   }
//spi思想,通過uri獲取對應的解析類如:org.springframework.context.config.ContextNamespaceHandler,
//詳細看一下這個類的獲取方式,在resolve 上ctrl+t進入
   NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
   if (handler == null) {
      error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
      return null;
   }//之後再點擊進入核心方法,
   return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

3、DefaultNamespaceHandlerResolver 中

@Override
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
   //獲取spring中所有jar包裏面所有的 "META-INF/spring.handlers"文件,並且建立映射關係
   Map<String, Object> handlerMappings = getHandlerMappings();
   //根據namespaceUri:http://www.springframework.org/schema/p,獲取到這個命名空間的處理類
   Object handlerOrClassName = handlerMappings.get(namespaceUri);
   if (handlerOrClassName == null) {
      return null;
   }
   else if (handlerOrClassName instanceof NamespaceHandler) {
      return (NamespaceHandler) handlerOrClassName;
   }
   else {
      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");
         }//通過單例獲取一個
         NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
         //調用處理類的init方法,在init方法中完成標籤元素解析類的註冊,這裏很重要,一會還有用到,可以自己看一下
         namespaceHandler.init();
         handlerMappings.put(namespaceUri, namespaceHandler);//通過map形式保存起來
         return namespaceHandler;
      }
       //刪除暫時無用的
   }
}

4、點擊handler.parse 方法 進入 NamespaceHandlerSupport implements NamespaceHandler  這裏

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
//這裏去map中去解析類
   BeanDefinitionParser parser = findParserForElement(element, parserContext);
//然後ctrl+t進入 
   return (parser != null ? parser.parse(element, parserContext) : null);
}
@Nullable
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
   String localName = parserContext.getDelegate().getLocalName(element);
//這裏通過 "component-scan",取到 new ComponentScanBeanDefinitionParser() 這個類
   BeanDefinitionParser parser = this.parsers.get(localName);
   if (parser == null) {
      parserContext.getReaderContext().fatal(
            "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
   }
   return parser;
}

5、點擊parser.parse 方法 進入ComponentScanBeanDefinitionParser implements BeanDefinitionParser 類中

1、掃描路徑。.class後綴的文件、要判斷類上是否有註解、如果有註解完成beanDefinition註冊

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
   //獲取basePackage屬性  例如:base-package="com.nandao.test"
   String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
   basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
   //可以用逗號分開
   String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
         ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
   //創建註解掃描器
   ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
   //掃描並把掃描的類封裝成beanDefinition對象  核心方法,點擊進入
   Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
   registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
   return null;
}

6、進入 ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider 類

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
   for (String basePackage : basePackages) {
      //1、循環掃描到有註解的類並封裝成BeanDefinition對象,點擊進入
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      for (BeanDefinition candidate : candidates) {
         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
         candidate.setScope(scopeMetadata.getScopeName());
         String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
         if (candidate instanceof AbstractBeanDefinition) {
            postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
         }
         if (candidate instanceof AnnotatedBeanDefinition) {
            //支持了@Lazy @DependOn註解
            AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
         }
         if (checkCandidate(beanName, candidate)) {
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
            //這裏不看
            definitionHolder =
                  AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            beanDefinitions.add(definitionHolder);

            //2、BeanDefinition註冊,核心方法 點擊進入
            registerBeanDefinition(definitionHolder, this.registry);
         }
      }
   }
   return beanDefinitions;
}
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
//點擊進入 BeanDefinitionReaderUtils 類
   BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}

7、來到ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
   if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
      return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
   }
   else {//此方法循環遞歸掃描 文件夾裏的文件並生成beanDefinition,點擊進入
      return scanCandidateComponents(basePackage);
   }
}
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
   Set<BeanDefinition> candidates = new LinkedHashSet<>();
   try {
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;

      //這裏遞歸尋找文件
      Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
      boolean traceEnabled = logger.isTraceEnabled();
      boolean debugEnabled = logger.isDebugEnabled();
      for (Resource resource : resources) {
         if (traceEnabled) {
            logger.trace("Scanning " + resource);
         }
         if (resource.isReadable()) {
            try {
               //包裝了類的基本信息的對象
               MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
               //如果類上面有includeFilters註解,就生成ScannedGenericBeanDefinition
               if (isCandidateComponent(metadataReader)) {
                  ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                  sbd.setResource(resource);
                  sbd.setSource(resource);
                  if (isCandidateComponent(sbd)) {
                     if (debugEnabled) {
                        logger.debug("Identified candidate component class: " + resource);
                     }
                     candidates.add(sbd);//統一放到BeanDefinition集合裏
                  }
         //刪除不重要的代碼,節約空間
   }
    
   return candidates;
}

8、BeanDefinitionReaderUtils 工具類

public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {

   // Register bean definition under primary name.
   String beanName = definitionHolder.getBeanName();

   //完成BeanDefinition的註冊,重點看,進去後你就有似曾相識的感覺!ctrl+t 進入
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   //建立別名和 id的映射,這樣就可以根據別名獲取到id
   // Register aliases for bean name, if any.
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String alias : aliases) {
         registry.registerAlias(beanName, alias);
      }
   }
}

9、來到  DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable 類,在上一篇解析spring 默認標籤的時候,最終也是進入到這個方法處理beanDefinition 

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {

   Assert.hasText(beanName, "Bean name must not be empty");
   Assert.notNull(beanDefinition, "BeanDefinition must not be null");

   if (beanDefinition instanceof AbstractBeanDefinition) {
      try {
         ((AbstractBeanDefinition) beanDefinition).validate();
      }
     

   //先判斷BeanDefinition是否已經註冊
   BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
   if (existingDefinition != null) {
      if (!isAllowBeanDefinitionOverriding()) {
         throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
      }
     //不重要的暫時刪除
   else {
      if (hasBeanCreationStarted()) {
         // Cannot modify startup-time collection elements anymore (for stable iteration)
         synchronized (this.beanDefinitionMap) {
            this.beanDefinitionMap.put(beanName, beanDefinition);
            List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
            updatedDefinitions.addAll(this.beanDefinitionNames);
            updatedDefinitions.add(beanName);
            this.beanDefinitionNames = updatedDefinitions;
            if (this.manualSingletonNames.contains(beanName)) {
               Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
               updatedSingletons.remove(beanName);
               this.manualSingletonNames = updatedSingletons;
            }
         }
      }
      else {
         //把beanDefinition緩存到map中
         // Still in startup registration phase
         this.beanDefinitionMap.put(beanName, beanDefinition);

         //把beanName放到beanDefinitionNames list中,這個list着重記住,bean實例化的時候需要用到
         this.beanDefinitionNames.add(beanName);
         this.manualSingletonNames.remove(beanName);
      }
      this.frozenBeanDefinitionNames = null;
   }
   if (existingDefinition != null || containsSingleton(beanName)) {
      resetBeanDefinition(beanName);
   }
}

10、到這裏我們完全解析了spring 的自定義標籤,核心流程做了大概講解,由於篇幅問題,沒有面面俱到。如果小夥伴看到這裏有任何不明白的地方,隨時留意。下篇內容更精彩,敬請期待!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章