1. 程式人生 > >Spring原始碼之註解掃描Component-scan

Spring原始碼之註解掃描Component-scan

本文主要介紹Spring的component-scan標籤,瞭解spring是如何實現掃描註解進行bean的註冊,主要實現實在 NamespaceHandler, NamespaceHandlerSupport 和 BeanDefinitionParser 三個介面中,還需要配置spring.handlers檔案,在接下里的原始碼解析中會詳細解析,在本篇部落格中將使用ApplicationConntext作為起點,直接從差異開始講解,如果想了解ApplicationContext 原始碼的全流程請看上篇部落格。 > GItHub:https://github.com/lantaoGitHub/spring-framework.git > **這裡解析解釋一下他們之間的關係:** > > **NamespaceHandlerSupport 是 Abstract 修飾的抽象類 並 實現 NamespaceHandler 介面,繼而實現了 NamespaceHandler介面的parser和docreate方法,自定的NamespaceHandler需要繼承NamespaceHandlerSupport類並需要實現NamespaceHandler介面的init方法,init方法需要做解析類的註冊操作,程式碼如下:** > > ```java > package org.springframework.context.config; > > import org.springframework.beans.factory.xml.NamespaceHandlerSupport; > import org.springframework.context.annotation.AnnotationConfigBeanDefinitionParser; > import org.springframework.context.annotation.ComponentScanBeanDefinitionParser; > > /** > * {@link org.springframework.beans.factory.xml.NamespaceHandler} > * for the '{@code context}' namespace. > * > * @author Mark Fisher > * @author Juergen Hoeller > * @since 2.5 > */ > public class ContextNamespaceHandler extends NamespaceHandlerSupport { > > @Override > public void init() { > registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); > registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); > registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); > registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); > registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); > registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); > registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser()); > registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser()); > } > > } > ``` > > ```java > /** > * Subclasses can call this to register the supplied {@link BeanDefinitionParser} to > * handle the specified element. The element name is the local (non-namespace qualified) > * name. > */ > protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) { > this.parsers.put(elementName, parser); > } > ``` > > **BeanDefinitionParser類是解析類的頂層介面,自定義的解析類需要實現BeanDefinitionParser類的Parser方法,解析類的註冊就在NameSpaceHandler的init方法中進行;** - 還是先看一下測試類: ```java package lantao.scan; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class ScanTest { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-bean-scan.xml"); } } ``` **xml檔案中use-default-filters 屬性的預設值為 true,即使用預設的 Filter 進行包掃描,而預設的 Filter 對標有 @Service,@Controller,@Component和@Repository 的註解的類進行掃描 ,如果定位為false的話,就需要進行自定義include-filter。** ```java
``` 因為這裡使用[ApplicationContext,ApplicationContext](https://blog.csdn.net/qq_30257149/article/details/88224879)在上篇文章已經進行了原始碼解讀,接下來我們直接看**差異點。** - 差異程式碼在DefaultBeanDefinitionDocumentReader類中的parseBeanDefinitions方法中: ```java /** * Parse the elements at the root level in the document: * "import", "alias", "bean". * @param root the DOM root element of the document */ protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { //驗證xml namespace, BeanDefinitionParserDelegate.BEANS_NAMESPACE_URI 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)) { //對預設標籤處理 // 這裡只處理 nade namespace 為 http://www.springframework.org/schema/beans 的標籤 parseDefaultElement(ele, delegate); } else { //對自定義標籤處理 會解析
或者自定義 dubbo delegate.parseCustomElement(ele); } } } } else { //對自定義標籤處理 delegate.parseCustomElement(root); } } ``` **主要差異就在** **parseDefaultElement(ele, delegate) 和 delegate.parseCustomElement(ele) 方法上,parseDefaultElement方法僅僅會處理node的namespace是:**[http://www.springframework.org/schema/beans](http://www.springframework.org/schema/beans) 的標籤,**其他標籤**和 **自定義標籤**全部都是通過這個方法來解析的; - **delegate.parseCustomElement原始碼:** ```java @Nullable public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); } @Nullable public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { // 獲取node的 NameSpaceURI String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null) { return null; } // 解析自定義標籤 需要在 Meta-inf 檔案加 增加 spring.handlers 檔案 例如:http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler // 根據指定的 NameSpaceURI 獲取 NamespaceHandler handler可以參考spring.handlers檔案 // abstract NamespaceHandlerSupport 實現了 NamespaceHandler 介面,繼而實現了 NamespaceHandler 的兩個個方法(parser,docreate),自定義handler 需要實現 NamespaceHandlerSupport 類 // 進行 NamespaceHandler 類的 init 方法的 實現, 主要是做註冊 BeanDefinitionParser( registerBeanDefinitionParser ) , 需要自定義解析類 繼承 BeanDefinitionParser 類 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)); } ``` 這裡程式碼很簡單,只做了以下三件事情: 1:獲取Element的NamespaceUri; 2:通過名稱空間處理解析器(NamespaceHandlerResolver)的resolver方法進行NameSpaceHandler的處理; 3:通過NameSpaceHandler的Parse方法進行標籤解析; - 我們直接看 resolve方法: ```java /** * Locate the {@link NamespaceHandler} for the supplied namespace URI * from the configured mappings. * @param namespaceUri the relevant namespace URI * @return the located {@link NamespaceHandler}, or {@code null} if none found */ @Override @Nullable public NamespaceHandler resolve(String namespaceUri) { // 這裡獲取的是所有註冊到 handlerMappings 中的 NamespaceHandler , // 就是 resource/META-INF/spring.handler 中的類 key就是namespaceUri , // 這些類都繼承了 NamespaceHandlerSupport 實現了init方法 在init方法中進行 BeanDefinitionParse 的註冊 Map handlerMappings = getHandlerMappings(); // 通過 namespaceUri 在 handlerMappings 中獲取對應的處理器或者 className 如果是初始化過的就直接返回,反之進行類初始化工作 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); // 判斷例項化的類的超類或者超級介面 是否是 NamespaceHandler 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); // 註冊 自定義標籤所對應的 解析策略類 解析策略類都繼承了 BeanDefinitionParser ,比如 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); } } } ``` 這裡主要做了一件事情,就是獲取**nameSpaceUri**對應的**NameSpaceHandler**,首先會調動**getHandlerMappings**方法獲取全部的**NameSpaceHandler,**然後通過**namespaceUri**獲取對應的**NameSpaceHandler,**如果還未例項化則進行例項化操作執行init方法向parsers註冊解析類,反之直接返回;**getHandlerMappings**方法獲取的**NameSpaceHandler**是解析於**resource/META-INF/spring.handler 檔案下, key就是namespaceUri,value就是自定義的NameSpaceHandler;** - getHandlerMappings方法原始碼: ```java /** * Load the specified NamespaceHandler mappings lazily. */ private Map getHandlerMappings() { Map handlerMappings = this.handlerMappings; if (handlerMappings == null) { synchronized (this) { handlerMappings = this.handlerMappings; if (handlerMappings == null) { if (logger.isTraceEnabled()) { logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]"); } try { // 這裡的handlerMappingsLocation指定的地址就是 resources 中的 META-INF/spring.handlers Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (logger.isTraceEnabled()) { logger.trace("Loaded NamespaceHandler mappings: " + mappings); } handlerMappings = new ConcurrentHashMap<>(mappings.size()); CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); this.handlerMappings = handlerMappings; } catch (IOException ex) { throw new IllegalStateException( "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex); } } } } return handlerMappings; } ``` **getHandlerMappings就是解析spring.handler檔案和執行NameSpaceHandler的init方法並放入快取的操作,NameSpaceHandler獲取到了以後我們看一下init註冊的BeanDefinitionParser的parser方法;** - NameSpaceHandlerSupport的parse方法原始碼: ```java /** * Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is * registered for that {@link Element}. */ @Override @Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { // 在 NamespaceHandlerSupport 中的 parser 集合中獲取 BeanDefinitionParser 的實現類 進行 parser BeanDefinitionParser parser = findParserForElement(element, parserContext); return (parser != null ? parser.parse(element, parserContext) : null); } ``` **parse方法做了兩件事情:** **1:通過定義的標籤屬性(例如:****component-scan****)獲取對應的BeanDefinitionParser解析類,原始碼如下:** ```java /** * Locates the {@link BeanDefinitionParser} from the register implementations using * the local name of the supplied {@link Element}. */ @Nullable private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) { // 這裡判斷各種標籤的解析策略 獲取標籤名字 String localName = parserContext.getDelegate().getLocalName(element); // 從 parsers 中獲取對應的解析策略類 parsers 是在 NameSpaceHandler 的 init 方法是初始化的; BeanDefinitionParser parser = this.parsers.get(localName); if (parser == null) { parserContext.getReaderContext().fatal( "Cannot locate BeanDefinitionParser for element [" + localName + "]", element); } // 返回對應的策略類進行解析 return parser; } ``` **2:開始解析;** - **parse方法原始碼:** ```java @Override @Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { // 獲取 basePackage 的 路徑 String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE); // 解析給定文字中的${.},將其替換為由{@link #getProperty}解析的相應屬性值 就是可以使用 ${} 和 properties中的值對應 basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage); //我們這裡在設定 base-package 的值時, 可以通過上面指示的分隔符 ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS 進行多個package的指定. 可以使用”,” “;” “\t\n(回車符)”來分割多個包名 String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); // Actually scan for bean definitions and register them. // 下面的程式碼就是 實際掃描bean定義並註冊它們。 // 配置 ClassPathBeanDefinitionScanner ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); // 掃描 並 註冊 Set beanDefinitions = scanner.doScan(basePackages); // 處理 annotation-config registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; } ``` parse方法主要做了以下五件事情: 1:獲取basePackage的值,就是xml中配置的路徑地址; 2:basePackage可以配置多個,使用 ‘,’ ';' 或者回車符 進行分割; 3:初始化ClassPathBeanDefinitionScanner,後邊的解析操作有ClassPathBeanDefinitionScanner來完成; 4:掃描並註冊bean; 5:處理annotation-config(這個後續會詳細講解,這裡就不贅述了) - 先看一下configureScanner方法原始碼: ```java protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) { boolean useDefaultFilters = true; // 設定 use-default-filters 標籤 use-default-filters 屬性的預設值為 true,即使用預設的 Filter 進行包掃描,而預設的 Filter 對標有 @Service,@Controller和@Repository 的註解的類進行掃描 if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) { useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)); } // Delegate bean definition registration to scanner class. // 將註冊Bean的任務委託給ClassPathBeanDefinitionScanner類。初始化 ClassPathBeanDefinitionScanner ,ClassPathBeanDefinitionScanner 是解析conponentScanner 的類 ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters); scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults()); scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns()); // set RESOURCE_PATTERN_ATTRIBUTE 設定 掃描Resource(資源) 路徑 預設為 "**/*.class" if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) { scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE)); } try { // set name-generator // 初始化bean 名稱生成器 parseBeanNameGenerator(element, scanner); } catch (Exception ex) { parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause()); } try { // 設定bean作用域 parseScope(element, scanner); } catch (Exception ex) { parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause()); } // 設定掃描包含 和 排除的 註解 // 設定過濾器,即用於指定哪些類需要被處理,哪些類需要被忽略 // set INCLUDE_FILTER_ELEMENT and EXCLUDE_FILTER_ELEMENT parseTypeFilters(element, scanner, parserContext); return scanner; } ``` configureScanner方法主要做了以下五件事: 1:獲取並設定use-default-filters,use-default-filters 屬性的預設值為 true,即使用預設的 Filter 進行包掃描,而預設的 Filter 對標有 @Service,@Controller和@Repository 的註解的類進行掃描,如果設定為false,則需要自行對include-filter新增; 2:初始化ClassPathBeanDefinitionScanner,如果use-default-filters為true則對include-filter進行add操作; 3:初始化bean 名稱生成器; 4:設定bean作用域; 5:設定掃描包含 和 排除的 註解,include-filter和exclude-filter; 上述程式碼就不展現了,git上程式碼有對應的註釋; - 接下來看scanner.doScan方法: ```java /** * Perform a scan within the specified base packages, * returning the registered bean definitions. *

This method does not

register an annotation config processor * but rather leaves this up to the caller. * @param basePackages the packages to check for annotated classes * @return set of beans registered if any for tooling registration purposes (never {@code null}) */ protected Set doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set beanDefinitions = new LinkedHashSet<>(); // 迴圈掃描 for (String basePackage : basePackages) { // 獲取指定包下所有 BeanDefinition Set candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { // 獲取一個ScopeMetadata物件,預設為AnnotationScopeMetadataResolver // 如果目標類未被@Scope註解,則返回一個預設的ScopeMetadata ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); // 使用bean名稱生成器生成bean名稱,預設生成器為AnnotationBeanNameGenerator // 首先是以註解的value為bean名稱,如果註解的value沒有值,則使用預設的名稱 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { // 處理定義在目標類上的註解,包括@Lazy, @Primary, @DependsOn, @Role, @Description // 這裡會檢查和 設定 AnnotatedBeanDefinition 的 @Lazy(懶載入) @Primary(主要,https://www.cnblogs.com/liaojie970/p/7885106.html) @DependsOn(需要依賴但不需要持有) 註解 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } // 檢查beanName是否已經存在 BeanDefinitionRegistry 中存在。 if (checkCandidate(beanName, candidate)) { //beanName 還沒使用過 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); // 如果有必要,則建立作用域代理 // 如果建立了代理,則返回表示代理物件的BeanDefinitionHolder definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); // 註冊Bean registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; } ``` 在doScan方法中都做了什麼: 1:獲取指定包下(指定的basePackage)所有 BeanDefinition; 2:獲取一個ScopeMetadata物件,預設為AnnotationScopeMetadataResolver,如果目標類未被@Scope註解,則返回一個預設的ScopeMetadata; 3:使用bean名稱生成器生成bean名稱,預設生成器為AnnotationBeanNameGenerator,如果註解上的value值是null,則需要生成; 4:設定AutowireCandidate autowire-candidate="false" 表示該物件不參與自動注入,借鑑:https://blog.csdn.net/shangboerds/article/details/72758095 5:處理定義在目標類上的註解,包括@Lazy, @Primary, @DependsOn, @Role, @Description,這裡會檢查和設定 AnnotatedBeanDefinition 的 @Lazy(懶載入) @Primary(主要,https://www.cnblogs.com/liaojie970/p/7885106.html) @DependsOn(需要依賴但不需要持有) 註解; 6:檢查beanName是否已經存在 beanDefinitionMap 中存在; 7:如果設定了scopedProxyMode,則需要建立代理類和註冊代理類; 8:呼叫registerBeanDefinition註冊bean,就是put到beanDefinitionMap中; - 這裡只說核心的scanCandidateComponents方法,其他的方法都很簡單,讀者自行通過debug來做就可以了: ```java private Set scanCandidateComponents(String basePackage) { Set candidates = new LinkedHashSet<>(); try { // ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX = "classpath*:"; // 通過觀察resolveBasePackage()方法的實現, 我們可以在設定basePackage時, 使用形如${}的佔位符, Spring會在這裡進行替換 // this.resourcePattern 預設為 "**/*.class" resourcePattern 可以在xml中配置 String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; // 使用上面拼接出的形如 "classpath*:xx/yyy/zzz/**/*.class", 將其檢索為Spring內建的Resource物件(這樣就統一化了資源的差異) // 使用ResourcePatternResolver的getResources方法獲取 路徑下全部 比如:classpath*:lantao/scan/**/*.class Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); for (Resource resource : resources) { if (traceEnabled) { logger.trace("Scanning " + resource); } // file是否可讀 if (resource.isReadable()) { try { // 獲取元資料 元資料就是用來定義資料的資料 就是定義 class 的 屬性 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); // 根據鍋爐器來判斷是否符合要求 做 includeFilters excludeFilters 的判斷 if (isCandidateComponent(metadataReader)) { // 例項化 ScannedGenericBeanDefinition 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); } else { if (debugEnabled) { logger.debug("Ignored because not a concrete top-level class: " + resource); } } } else { if (traceEnabled) { logger.trace("Ignored because not matching any filter: " + resource); } } } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to read candidate component class: " + resource, ex); } } else { if (traceEnabled) { logger.trace("Ignored because not readable: " + resource); } } } } catch (IOException ex) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); } return candidates; } ``` 這裡主要就是做了通過ResourcePatternResolver的getResource獲取指定路徑的資原始檔,再通過資原始檔Resource獲取MetadataReader (元資料就是用來定義資料的資料 就是定義 class 的 屬性),接下來通過isCandidateComponent方法來做核心處理,因為通過路徑獲取的資源是全部的,不是想要的,通過isCandidateComponent方法來做 ncludeFilters excludeFilters 的判斷,再通過isCandidateComponent(sbd)判斷BeanDefinition必須是一個實現類,不可以是介面等; - 我們看一下核心判斷方法isCandidateComponent: ```java /** * Determine whether the given class does not match any exclude filter * and does match at least one include filter. * @param metadataReader the ASM ClassReader for the class * @return whether the class qualifies as a candidate component */ protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { // 判斷 excludeFilters 的 TypeFilter for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return false; } } // 判斷邏輯 includeFilters 中的 TypeFilter 預設包含的filter有 @components 和 引用他的 @service @controller @Repository for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { // 判斷 @Conditional , @Conditional是Spring4新提供的註解,它的作用是按照一定的條件進行判斷,滿足條件給容器註冊bean。 還有 @ConditionalOnXX 等註解 return isConditionMatch(metadataReader); } } return false; } ``` ```java /** * Determine whether the given bean definition qualifies as candidate. *

The default implementation checks whether the class is not an interface * and not dependent on an enclosing class. *

Can be overridden in subclasses. * @param beanDefinition the bean definition to check * @return whether the bean definition qualifies as a candidate component */ protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { AnnotationMetadata metadata = beanDefinition.getMetadata(); // metadata.isIndependent() 是獨立的 & // metadata.isConcrete() 是否是介面或者是抽象類 或 // 必須是抽象類 和 有@lookup 註解 return (metadata.isIndependent() && (metadata.isConcrete() || (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName())))); } ``` 到這裡就已經講完了Component-scan掃描注入的原始碼,這裡涉及代理和annotation-config沒有做詳細的講解,會在後續的文章中做,碼字不易,轉發請註明出處。 ![1615879776045-153328](https://gitee.com/lantaoGitee/picture-bed/raw/master/uPic/20210316/1615879776045-1533