作用
@ComponentScan用于扫描指定包下的类,
将标注有@Controller、@Service、@Repository、@Component4个注解其中一个的类扫描到Spring容器,
作为SpringBean
结构
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}
流转图说明
分析
refresh
org.springframework.context.support.AbstractApplicationContext#refresh
直接定位到此处堆栈
doProcessConfigurationClass
org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
**问题1:**为什么启动JavabaseApplication也包含 @Component 注解呢?
答:经过分析直接定位到此方法在此处进行断点,这里会通过投递队列的形式递归解析每一个类注解上的注解
条件断点:annotationType.getName().equals(“org.springframework.boot.autoconfigure.SpringBootApplication”)
底层堆栈:org.springframework.core.annotation.AnnotationTypeMappings#addAllMappings
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// 处理配置类的嵌套内部类
// 为什么启动类JavabaseApplication 也包含 @Component 注解呢
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// 首先递归处理任何成员(嵌套)类
// 注册恰好是配置类本身的成员(嵌套)类。
processMemberClasses(configClass, sourceClass);
}
// 处理 @PropertySource 注解
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// 处理@ComponentScan注解,把ComponentScan或@ComponentScans者注解解析为AnnotationAttributes
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// ****核心代码**** 配置类用@ComponentScan注释->立即执行扫描 *******
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
// 这里会递归执行,递归的作用是为了检测,本质也不是递归,源码注释说是递归
// 每个bean上的是否有 @PropertySource 和 @ComponentScan 注解
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// No superclass -> processing is complete
return null;
}
处理MemberClasses
org.springframework.context.annotation.ConfigurationClassParser#processMemberClasses
处理成员变量带注解的情况如下:
@Import({ServiceB.class,ImportBeanDefinitionRegistrarTest.class}) //导入
public class ConfigurationTest {
@Configuration(proxyBeanMethods = false)
@Conditional(ConfigurationTest.EmbeddedDatabaseCondition.class)
protected static class EmbeddedDatabaseConfiguration {
}
static class EmbeddedDatabaseCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
return new ConditionOutcome(true, (String) null);
}
}
@Configuration(proxyBeanMethods = false)
protected static class A {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(DataSource.class)
protected static class B {
}
}
处理@PropertySource
处理@ComponentScans
parse
这里的解析和执行引出俩个思考问题
问题1:为什么我们配置了 @Controller、@Service、@Repository、@Component 注解就能被spring扫描到呢?
答:ClassPathBeanDefinitionScanner 构造方法默认添加了 ,this.includeFilters.add(new AnnotationTypeFilter(Component.class));
问题2:为什么springBoot启动类要在所有的bean目录之上,才能扫描到bean?
**答:**答案就在下面代码中,因为默认的@ComponentScan注解 basePackages 是空的,所以会执行
if (basePackages.isEmpty()) {
// declaringClass = com.aaa.javabase.JavabaseApplication
// ClassUtils.getPackageName(declaringClass) = com.aaa.javabase
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
org.springframework.context.annotation.ComponentScanAnnotationParser#parse
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
// 这里默认的构造方法执行了:this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
BeanUtils.instantiateClass(generatorClass));
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
scanner.setScopedProxyMode(scopedProxyMode);
}
else {
Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
}
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addIncludeFilter(typeFilter);
}
}
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addExcludeFilter(typeFilter);
}
}
boolean lazyInit = componentScan.getBoolean("lazyInit");
if (lazyInit) {
scanner.getBeanDefinitionDefaults().setLazyInit(true);
}
// 解析 @ComponentScan basePackages 参数
Set<String> basePackages = new LinkedHashSet<>();
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Collections.addAll(basePackages, tokenized);
}
// 解析 @ComponentScan basePackageClasses 参数
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
// 解析 @ComponentScan basePackages 参数
// 魔幻的地方出现了,
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#registerDefaultFilters
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
doScan
org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
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) {
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);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
return scanCandidateComponents(basePackage);
}
}
扫描匹配指定候选组件
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
// 扫描指定路径下面的静态文件
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = getResourcePatternResolver().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 = getMetadataReaderFactory().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;
}
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent
对应前面的 this.includeFilters.add(new AnnotationTypeFilter(Component.class));
这里会匹配@Component注解匹配上才返回候选组件
处理@Import
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
// 1、@Bean注解-扫描 ImportSelector接口的地方
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
// 这里需要注意的是 DeferredImportSelector 的直接实现类,不再递归
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
// 这里又是一个递归,把ImportSelector的直接实现类的 selectImports返回的数组,注册bean
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
// 2、@Bean注解-扫描 ImportBeanDefinitionRegistrar 接口的地方
// 用途:@MapperScan注解就是基于此条件执行
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
// 这里放入list,后面会在Parse之后,在这里面调用解析ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
// 3、@Bean注解-直接设置class文件的地方
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 同样也是递归再次解析,因为一个类可能包含别的注解
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
处理@ImportResource
处理@Bean
处理 Interfaces @Bean
org.springframework.context.annotation.ConfigurationClassParser#processInterfaces
经过debug发现spring内部bean没有用到processInterfaces,所以这块不重要。
处理接口的默认方法实现,从 JDK8 开始,接口中的方法可以有自己的默认实现,如果这个接口的方法加了 @Bean 注解,也需要被解析;然后检索配置类中加了 @Bean 注解的方法,将 @Bean 方法转化为 BeanMethod 对象,保存再集合中。
public interface UserManager {
@Bean
default Object testBeanTlz() {
return new HashSet();
}
}
@Component
public class UserManagerImpl implements UserManager {
}
private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
// 找到配置类的所有接口,遍历接口
for (SourceClass ifc : sourceClass.getInterfaces()) {
// 找到含有 @Bean 注解的默认方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
for (MethodMetadata methodMetadata : beanMethods) {
if (!methodMetadata.isAbstract()) {
// A default method or other concrete method on a Java 8+ interface...
// 添加到集合中
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
}
//递归处理,因为接口可能存在父接口
processInterfaces(configClass, ifc);
}
}