内容简介:本文主要介绍Spring的component-scan标签,了解spring是如果实现扫描注解进行bean的注册,主要实现实在 NamespaceHandler, NamespaceHandlerSupport 和 BeanDefinitionParser 三个接口中,还需要配置spring.handlers文件,在接下里的源码解析中会详细解析,在本篇博客中将使用ApplicationConntext作为起点,直接从差异开始讲解,如果想了解ApplicationContext 源码的全流程请看上篇博客。GI
本文主要介绍Spring的component-scan标签,了解spring是如果实现扫描注解进行bean的注册,主要实现实在 NamespaceHandler, NamespaceHandlerSupport 和 BeanDefinitionParser 三个接口中,还需要配置spring.handlers文件,在接下里的源码解析中会详细解析,在本篇博客中将使用ApplicationConntext作为起点,直接从差异开始讲解,如果想了解ApplicationContext 源码的全流程请看上篇博客。
GItHub: github.com/lantaoGitHu…
这里解析解释一下他们之间的关系:
NamespaceHandlerSupport 是 Abstract 修饰的抽象类 并 实现 NamespaceHandler 接口,继而实现了 NamespaceHandler接口的parser和docreate方法,自定的NamespaceHandler需要继承NamespaceHandlerSupport类并需要实现NamespaceHandler接口的init方法,init方法需要做解析类的注册操作,代码如下:
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方法中年进行;
- 还是先看一下测试类:
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。
<?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 在上篇文章已经进行了源码解读,接下来我们直接看 差异点。
- 差异代码在DefaultBeanDefinitionDocumentReader类中的parseBeanDefinitions方法中:
/**
* 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… 的标签, 其他标签 和 自定义标签 全部都是通过这个方法来解析的;
- delegate.parseCustomElement源码:
@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方法:
/**
* 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;
- getHandlerMappings方法源码:
/**
* 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方法;
- NameSpaceHandlerSupport的parse方法源码:
/**
* 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:开始解析;
- parse方法源码:
@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(这个后续会详细讲解,这里就不赘述了)
- 先看一下configureScanner方法源码:
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方法:
/**
* 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中;
- 这里只说核心的scanCandidateComponents方法,其他的方法都很简单,读者自行通过debug来做就可以了:
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必须是一个实现类,不可以是接口等;
- 我们看一下核心判断方法isCandidateComponent:
-
/** * 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…
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Spring Boot源码分析 - Configuration注解
- Spring源码分析:@Autowired注解原理分析
- Spring AOP 源码解析:注解式切面增强机制
- Spring源码解读(3)AOP-切面类的注解处理
- SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三)
- Spring 注解编程之模式注解
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
ANSI Common Lisp
Paul Graham / Prentice Hall / 1995-11-12 / USD 116.40
For use as a core text supplement in any course covering common LISP such as Artificial Intelligence or Concepts of Programming Languages. Teaching students new and more powerful ways of thinking abo......一起来看看 《ANSI Common Lisp》 这本书的介绍吧!