本文主要介紹Spring的component-scan標籤,瞭解spring是若是實現掃描註解進行bean的註冊,主要實現實在 NamespaceHandler, NamespaceHandlerSupport 和 BeanDefinitionParser 三個接口中,還須要配置spring.handlers文件,在接下里的源碼解析中會詳細解析,在本篇博客中將使用ApplicationConntext做爲起點,直接從差別開始講解,若是想了解ApplicationContext 源碼的全流程請看上篇博客。html
GItHub:github.com/lantaoGitHu…node
這裏解析解釋一下他們之間的關係:git
NamespaceHandlerSupport 是 Abstract 修飾的抽象類 並 實現 NamespaceHandler 接口,繼而實現了 NamespaceHandler接口的parser和docreate方法,自定的NamespaceHandler須要繼承NamespaceHandlerSupport類並須要實現NamespaceHandler接口的init方法,init方法須要作解析類的註冊操做,代碼以下:github
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()); } }複製代碼
![]()
/** * 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方法中年進行;spring
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。express
<?xml version="1.0" encoding="UTF-8" ?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- use-default-filters 屬性的默認值爲 true,即便用默認的 Filter 進行包掃描,而默認的 Filter 對標有 @Service,@Controller和@Repository 的註解的類進行掃描 -->
<context:component-scan base-package="lantao.scan" use-default-filters="false">
<!-- 只掃描 base-package 的 controller 註解 還有對應的 exclude-filter 標籤 排除 ; use-default-filters="false" 和 include-filter 一塊兒使用 和 exclude-filter一塊兒回拋異常-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>複製代碼
由於這裏使用ApplicationContext,ApplicationContext在上篇文章已經進行了源碼解讀,接下來咱們直接看差別點。緩存
/**
* 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 {
//對自定義標籤處理 會解析 <context:component-scan base-package="lantao.scan"/> 或者自定義 dubbo
delegate.parseCustomElement(ele);
}
}
}
}
else {
//對自定義標籤處理
delegate.parseCustomElement(root);
}
}複製代碼
主要差別就在 parseDefaultElement(ele, delegate) 和 delegate.parseCustomElement(ele) 方法上,parseDefaultElement方法僅僅會處理node的namespace是:www.springframework.org/schema/bean… 的標籤,其餘標籤和 自定義標籤所有都是經過這個方法來解析的;bash
@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));
}複製代碼
這裏代碼很簡單,只作了一下三件事情:app
1:獲取Element的NamespaceUri;ide
2:經過命名空間處理解析器(NamespaceHandlerResolver)的resolver方法進行NameSpaceHandler的處理;
3:經過NameSpaceHandler的Parse方法進行標籤解析;
/**
* 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<String, Object> 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;
/**
* Load the specified NamespaceHandler mappings lazily.
*/
private Map<String, Object> getHandlerMappings() {
Map<String, Object> 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方法;
/**
* 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解析類,源碼以下:
/**
* 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:開始解析;
@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<BeanDefinitionHolder> 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(這個後續會詳細講解,這裏就不贅述了)
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上代碼有對應的註釋;
/**
* Perform a scan within the specified base packages,
* returning the registered bean definitions.
* <p>This method does <i>not</i> 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<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
// 循環掃描
for (String basePackage : basePackages) {
// 獲取指定包下全部 BeanDefinition
Set<BeanDefinition> 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" 表示該對象不參與自動注入,借鑑:blog.csdn.net/shangboerds…
5:處理定義在目標類上的註解,包括@Lazy, @Primary, @DependsOn, @Role, @Description,這裏會檢查和設置 AnnotatedBeanDefinition 的 @Lazy(懶加載) @Primary(主要,www.cnblogs.com/liaojie970/…) @DependsOn(須要依賴但不須要持有) 註解;
6:檢查beanName是否已經存在 beanDefinitionMap 中存在;
7:若是設置了scopedProxyMode,則須要建立代理類和註冊代理類;
8:調用registerBeanDefinition註冊bean,就是put到beanDefinitionMap中;
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> 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必須是一個實現類,不能夠是接口等;
/**
* 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;
}複製代碼
/**
* Determine whether the given bean definition qualifies as candidate.
* <p>The default implementation checks whether the class is not an interface
* and not dependent on an enclosing class.
* <p>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沒有作詳細的講解,會在後續的文章中作,碼字不易,轉發請註明出處:blog.csdn.net/qq_30257149…