共计 20085 个字符,预计需要花费 51 分钟才能阅读完成。
本文主要介绍 Spring 的 component-scan 标签,了解 spring 是如何实现扫描注解进行 bean 的注册,主要实现实在 NamespaceHandler, NamespaceHandlerSupport 和 BeanDefinitionParser 三个接口中,还需要配置 spring.handlers 文件,在接下里的源码解析中会详细解析,在本篇博客中将使用 ApplicationConntext 作为起点,直接从差异开始讲解,如果想了解 ApplicationContext 源码的全流程请看上篇博客。
GItHub:https://github.com/lantaoGitH…
这里解析解释一下他们之间的关系:
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 是:http://www.springframework.org/schema/beans 的标签, 其他标签 和 自定义标签 全部都是通过这个方法来解析的;
- 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” 表示该对象不参与自动注入,借鉴:https://blog.csdn.net/shangbo…
5:处理定义在目标类上的注解,包括 @Lazy, @Primary, @DependsOn, @Role, @Description,这里会检查和设置 AnnotatedBeanDefinition 的 @Lazy(懒加载) @Primary(主要,https://www.cnblogs.com/liaoj… @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 没有做详细的讲解,会在后续的文章中做,码字不易,转发请注明出处。
博客地址:http://lantaoblog.site