配置以及代码如下
配置
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.test.controller">
</context:component-scan>
代码
package com.test.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
}
当我们在配置文件中配置了<context:component-scan>,那么spring将自动帮我们扫描指定包下的类是否标注了@Component,@Repository,@Service,@Controller这几个注解,如果标注了那么spring将自动帮我们实例化。那么spring是如何做到的呢?下面来看源码分析。
源码分析
(1)前面讲过[<context:annotation-config/>自动扫描标签详解],当解析自定义的标签时,就由BeanDefinitionParser接口的子类来完成的。而NamespaceHandler <context:component-scan>的同样是ContextNamespaceHandler 类
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());
}
}
最终由ComponentScanBeanDefinitionParser来解析<context:component-scan>标签。
(2)看下ComponentScanBeanDefinitionParse的解析方法parse()
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
//获取要扫描的包名,也就是base-package元素对应的值,我这里配置的是com.test.controller
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
// Actually scan for bean definitions and register them.
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
//这里进行扫描
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
//注册其他的BeanPostProcessor,这个放在最后讲
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
从上面的代码可以看出,先是获取到<context:component-scan>标签中配置的base-package元素,通过这个元素的值知道要扫描那些类,然后执行扫描,获取标注了@Component,@Repository,@Service,@Controller这几个注解的类,并注册到beanDefinitionMap中,在容器触发getBean()方法,将对这些类进行实例化,并保存在spring的注册表(当然这是在Singleton情况下)
(3)下面来看具体的扫描过程
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
进入doScan方法中
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
for (String basePackage : basePackages) {
//这里是重点,这里将对指定的包下的类进行扫描,拿到候选的扫描类
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) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//这里注册到beanDefinitionMap中
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
重点是继承自父类ClassPathScanningCandidateComponentProvider 的findCandidateComponents方法,这里将进行扫描
来看下这个findCandidateComponents方法
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + "/" + this.resourcePattern;
Resource[] resources = this.resourcePatternResolver.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 = this.metadataReaderFactory.getMetadataReader(resource);
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);
}
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;
}
未待完续。。。
[朝阳区尼克杨]
转载请注明原创出处,谢谢啦!