spring的扫描一般可以通过两种方式:
测试类:
@Component
public class Scan_A {
@PostConstruct
public void init(){
System.out.println("-----------Scan_A");
}
}
1)、@ComponentSscan注解
public class ComponentScanTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ContextConfig.class);
}
}
配置类:(可配@Compnent,也可不配,都会生效)
@ComponentScan("com.spring.demo.scan")
public class ContextConfig {
}
启动此时控制台:
----------Scan_A
则表示扫描到了。
2)、使用api调用扫描
public class ApplicationContextTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.spring.demo.scan");
context.refresh();
}
}
此时运行,照样能打印----------Scan_A
两种方法实例化了各自实例化了自己的扫描器,但最终都是调用doscan方法完成解析的。
(关于两种方式的区别,后面说明,不过我们最常用的应该就是第一种方式)
==========================我们先以第一种情况作为分析========================
上一篇文章中,invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors,registry)代码行中(情况2),ConfigurationClassPostProcessor执行了其子接口实现类。我们说到这个实现类完成对注解类的扫描。我们就以此为入口。
调用链:ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry(子接口实现方法)
--------》processConfigBeanDefinitions(registry)
/**
* ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口的实现类
* 功能:完成对注解的扫描解析,并放入BeanDefinitionMap(此时未进行实例化为bean,应为采用的是ASM字节码技术,所以不会在这里实例化)
* registry------容器对象
* */
/**
* Build and validate a configuration model based on the registry of
* {@link Configuration} classes.
*/
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
//获取内置的+我们提供的配置类的BeanDefinition名称
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
//判断是否已经被解析了
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
//标记配置类是全配置类(用了@Configuration)或者半配置类(加了CompnentScan、Component、Import、ImportResource任一个注解)
//很多情况下@Component注解和@Configuration注解在配置功能方面几乎没有什么区别;如果不存在这个@Bean方法的嵌套调用,你完全可以用@Component去替代@Configuration
//是则加入集合(我们这里一般会有6个配置类,5个内置的+1个我们提供的(当然我们可以提供多个,不过一般是一个)
// 此时只有我们提供配置类有用到@CompnentScan,所以这里只会加入一个
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
//排序,由于一般情况下是一个,所以没什么影响
// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
//获取Bean名称的生成策略
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
//环境变量
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
//实例化一个配置类的解析器
//此时构造方法传入一个componentScanBeanNameGenerator----类型AnnotationBeanNameGenerator(即一个Bean的名称生成策略)
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
//去重
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
//开始循环解析配置类
do {
//调用parse方法进行解析
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
===============
这里做个小插曲讲讲什么是全配置类和半配置类:
配置类会在ConfifigurationClassPostProcessor#postProcessBeanDefifinitionRegistry 的方法里面被解析
主要看改配置类上面是否加了@Configuration注解如果加了则会标记这个类为 full—— 全配置 类;如果没加@Configuration 但是加了其他注解则标识为 lite—— 半配置类;
static {
candidateIndicators . add ( Component . class . getName ());
candidateIndicators . add ( ComponentScan . class . getName ());
candidateIndicators . add ( Import . class . getName ());
candidateIndicators . add ( ImportResource . class . getName ());
}
加了四个其中任意一个都会作为半配置类。
(注意:只有直接被直接穿进去的配置类才不用做@Compnent、或者@Configuration的注入bean操作,spring会将其实例化为bean,而通过配置类的@CompnentScan扫描出来的必须要加上@Compnent、@Configuration才能注入到spring中)
所以很多情况下 @Component 注解和 @Confifiguration 注解在配置功能方面几乎没有什么区别;如果不
存在这个 @Bean 方法的嵌套调用,你完全可以用 @Component 去替代 @Confifiguration。
不止我们传进去的配置类会被标记为全、半配置类,我们扫描出来的类也会被标记。
那么spring是如何处理的?全配置类和半配置类有什么区别?
1、spring如何处理全配置类?
//判断配置类是全配置类(用了@Configuration)或者半配置类(加了CompnentScan、Component、Import、ImportResource任一个注解)
//很多情况下@Component注解和@Configuration注解在配置功能方面几乎没有什么区别;如果不存在这个@Bean方法的嵌套调用,你完全可以用@Component去替代@Configuration
//是则加入集合(我们这里一般会有6个配置类,5个内置的+1个我们提供的(当然我们可以提供多个,不过一般是一个)
// 此时只有我们提供配置类有用到@CompnentScan,所以这里只会加入一个
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
我们进去ConfigurationClassUtils.checkConfigurationClassCandidate方法:
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
AnnotationMetadata metadata;
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
// Can reuse the pre-parsed metadata from the given BeanDefinition...
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
// Check already loaded Class if present...
// since we possibly can't even load the class file for this Class.
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
BeanPostProcessor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
EventListenerFactory.class.isAssignableFrom(beanClass)) {
return false;
}
metadata = AnnotationMetadata.introspect(beanClass);
}
else {
try {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
metadata = metadataReader.getAnnotationMetadata();
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not find class file for introspecting configuration annotations: " +
className, ex);
}
return false;
}
}
//判断是否加了Configuration注解
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
//加了Configuration注解,并获取注解的属性proxyBeanMethods(默认值为true)
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
//标记为CONFIGURATION_CLASS_ATTRIBUTE属性为full,即该类被标记了为全配置类
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
//加了Configuration注解 || 是否加了Component、ComponentScan、Import、ImportResource
//注意这里是||符号
else if (config != null || isConfigurationCandidate(metadata)) {
//标记为lite,版配置类
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
// It's a full or lite configuration candidate... Let's determine the order value, if any.
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
此时我们进入isConfigurationCandidate方法看看怎么判断的:
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
if (metadata.isInterface()) {
return false;
}
//可以查看candidateIndicators的值被初始化了
//static {
// candidateIndicators.add(Component.class.getName());
// candidateIndicators.add(ComponentScan.class.getName());
// candidateIndicators.add(Import.class.getName());
// candidateIndicators.add(ImportResource.class.getName());
// }
// Any of the typical annotations found?
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
// Finally, let's look for @Bean methods...
return hasBeanMethods(metadata);
}
点击这个candidateIndicators发现做了初始化:
private static final Set<String> candidateIndicators = new HashSet<>(8);
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
至此spring对配置类的标记完成。
2、扫描出来的类也会被标识吗?
在执行ConfigurationClassPostProcessor#processConfigBeanDefinitions时,解析其调用解析方法
parser.parse(candidates);解析@CompnentScan注解时就对扫描出来的类进行了标记:
进入parse()--->processConfigurationClass()--->doProcessConfigurationClass()
标记方法与上面对配置类的标记方法一样的。
所以说不管是配置类还是扫描出来的类,都会被标记为Full还是lite。
下面我们看下个问题
3、spring如何处理全、半配置类:
上面的方法只是对配置类进行标记为full还是lite(ConfifigurationClassPostProcessor执行postProcessBeanDefifinitionRegistry会进行标记),但没有对其进行处理,而对全/半配置类的处理(ConfifigurationClassPostProcessor的父接口方法postProcessBeanFactory时进行处理)入口:
注意:到执行ConfifigurationClassPostProcessor的父接口方法时,扫描出来的类也会被加到BeanDefinitionMap中了。
我们继续点进去,找到这个方法:ConfifigurationClassPostProcessor#postProcessBeanFactory
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
//增强、代理全配置类(Full),生成全配置类的代理类
//不对半配置类(lite)做代理
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
进入代理方法:
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
//执行到这里时,扫描出来的类也被加到BeanDefinitionMap了
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
//获取BeanDefinition中全、半配置的标记属性---CONFIGURATION_CLASS_ATTRIBUTE
// (之前在ConfifigurationClassPostProcessor执行postProcessBeanDefifinitionRegistry会进行标记是否全full、半lite配置)
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
AnnotationMetadata annotationMetadata = null;
MethodMetadata methodMetadata = null;
//判断是否加了注解的
if (beanDef instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDef;
annotationMetadata = annotatedBeanDefinition.getMetadata();
methodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata();
}
if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
// Configuration class (full or lite) or a configuration-derived @Bean method
// -> eagerly resolve bean class at this point, unless it's a 'lite' configuration
// or component class without @Bean methods.
AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
if (!abd.hasBeanClass()) {
boolean liteConfigurationCandidateWithoutBeanMethods =
(ConfigurationClassUtils.CONFIGURATION_CLASS_LITE.equals(configClassAttr) &&
annotationMetadata != null && !ConfigurationClassUtils.hasBeanMethods(annotationMetadata));
if (!liteConfigurationCandidateWithoutBeanMethods) {
try {
abd.resolveBeanClass(this.beanClassLoader);
}
catch (Throwable ex) {
throw new IllegalStateException(
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
}
//如果被标记了为Full(全配置类),先put到configBeanDefs集合中
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
if (!(beanDef instanceof AbstractBeanDefinition)) {
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
}
else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
logger.info("Cannot enhance @Configuration bean definition '" + beanName +
"' since its singleton instance has been created too early. The typical cause " +
"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
"return type: Consider declaring such methods as 'static'.");
}
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
if (configBeanDefs.isEmpty()) {
// nothing to enhance -> return immediately
return;
}
//统一对全配置类的集合configBeanDefs进行处理(使用cglib生成代理类)
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// Set enhanced subclass of the user-specified bean class
//生成一个代理类
Class<?> configClass = beanDef.getBeanClass();
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
}
//设置BeanDefinition的BeanClass为代理类
beanDef.setBeanClass(enhancedClass);
}
}
}
至此我们可以知道spring对全配置类做了动态代理,BeanDefinition.setBeanClass(代理类),所以我们注入拿到的对象就是一个代理对象。而对半配置类,spring并没有做任何处理。所以我们注入拿到的是原对象。这也就是常见面试题@Configuration和@Compnent的区别。
可是我们现在又有一个问题,
4、为什么要做代理?
先给结论:加了@Configuration注解是为了保证注入的bean的作用域的正确性:
我们来看两个例子
Scan_D类:
public class Scan_D {
public Scan_D() {
System.out.println("create Scan_D");
}
}
Scan_E类:
public class Scan_E {
public Scan_E() {
System.out.println("create Scan_E");
}
}
配置类:先使用半配置类的方式:
@ComponentScan(value = "com.spring.demo.scan")
public class FullOrLiteConfig {
@Bean
public Scan_D scan_d(){
return new Scan_D();
}
@Bean
public Scan_E scan_e(){
scan_d();
return new Scan_E();
}
}
启动类:
public class ComponentScanTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(FullOrLiteConfig.class);
}
}
此时控制台打印:
create Scan_D
create Scan_D
create Scan_E
可以看到,我们本来默认注入到spring的bean是单例的,只需要实例化一次,而这里Scan_D被实例化了两次,显然这不符合我们对bean作用域的理解。那么我们现在修改配置类为Full方式的:
@ComponentScan(value = "com.spring.demo.scan")
@Configuration
public class FullOrLiteConfig {
@Bean
public Scan_D scan_d(){
return new Scan_D();
}
@Bean
public Scan_E scan_e(){
scan_d();
return new Scan_E();
}
}
再次运行:
create Scan_D
create Scan_E
发现Scan_D只被实例化了一次,所以说全配置类的方式保证了注入的bean的作用域的正确性。
那么spring怎么去实现的?
可以看看我的下一篇文章:
(我们就不在这篇文章里面写了)
==============
继续我们的源码分析,进入parser.parse(candidates)---》parse()-----》processConfigurationClass(ConfigurationClass configClass)
此时可以看到processConfigurationClass方法传进了一个ConfigurationClass类型,该类型就是用来抽象化配置类、半配置类的一些配置信息等,例如:
final class ConfigurationClass {
private final AnnotationMetadata metadata;
private final Resource resource;
@Nullable
private String beanName;
//该配置类是被谁导入进来的
private final Set<ConfigurationClass> importedBy = new LinkedHashSet<>(1);
//当前配置类中的所有@Bean方法
private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();
//xml相关的
private final Map<String, Class<? extends BeanDefinitionReader>> importedResources =
new LinkedHashMap<>();
//当前配置类上所有被@Import进来的实现了ImportBeanDefinitionRegistrar的对象
private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
new LinkedHashMap<>();
}
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
//查看缓存是否存在(每解析完一个配置类会放进缓存)
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
//描述配置类的基本信息
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
//解析配置类
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
看到上面的ConfigurationClass来描述配置类的配置信息,而SourceClass用来描述配置类的基本信息。此时asSourceClass方法会将配置信息一起放到SourceClass中。
进入doProcessConfigurationClass()
@Nullable
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
//解析配置类是否加了@Compnent注解
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
//如果加了,则处理内部类
processMemberClasses(configClass, sourceClass, filter);
}
//处理配置类@propertySource注解(读取文件)
// Process any @PropertySource annotations
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注解(@ComponentScans即可扫描多个包)
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
//配置类存在@ComponentScan或@ComponentScans注解
for (AnnotationAttributes componentScan : componentScans) {
//解析这个@ComponentScan注解,完成对@Compnent注解的扫描,并返回Set<BeanDefinitionHolder>
// The config class is annotated with @ComponentScan -> perform the scan immediately
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)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
//解析@Import
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
//解析@ImportResource
// Process any @ImportResource annotations
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
//解析@Bean
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
注意:这里涉及对于配置类上面加了多个注解例如@Import、@ImportResource、@Bean、@PropertySource、@Compnent的处理逻辑,由于该篇不解析这部分内容,所以专门有一篇写如何解析这些注解---------》文章如下:
spring-----如何处理配置类上的注解_先熬半个月的博客-CSDN博客
进入this.componentScanParser.parse(componentScan,sourceClass.getMetadata().getClassName())
开始解析@CompnentScan注解:
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String declaringClass) {
//实例化一个扫描器
//registry----容器对象
//useDefaultFilters----是否使用默认的过滤器
//存在两种过滤器:icludeFilter引入过滤器、excludeFilter排除过滤器
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
//看看@CompnentScan注解中的nameGenerator属性(bean名称的生成策略,可以自己配置)是否有配置,有则获取
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
//设置bean名字的生成策略
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
BeanUtils.instantiateClass(generatorClass));
//下面的都是对注解的属性的解析(scopedProxy、resourcePattern、includeFilters等等)
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"));
//加入Include过滤器
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);
}
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);
}
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
//加入Exclude过滤器
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
//
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
进入scanner.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) {
//使用ASM字节码技术读取对应的目录,解析符合的类(加了@Compnent注解)并返回BeanDefinition集合
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
//根据bean名字生成策略生成bean名称
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
//查看是否已经存在BeanDefinition,不存在则进行注册
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;
}
我们可以继续往findCandidateComponents方法看看,大概是怎么扫描的,再次进入
scanCandidateComponents方法。
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
//防止重复
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
//获取包全路径
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//读取目录转为Resource[]对象
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
//遍历获取到单个Resource对象(即单个文件)
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
try {
//ASM技术读取文件内容转为MetadataReader对象
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
//判断该对象是否被@CompnentScan的excludeFilters排除被排除则false,
// 被includeFilters或者存在@Compnent则实例化成ScannedGenericBeanDefinition对象放进集合
//注意:这里判断是否有@Compnent是因为在实例化扫描器ClassPathBeanDefinitionScanner的时候会执行registerDefaultFilters()方法
// 在该方法中默认就注入了Component到includeFilter中(this.includeFilters.add(new AnnotationTypeFilter(Component.class));)
// 所以如果有@Compnent注解则在判断是否在includeFilter是返回true。
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
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 (FileNotFoundException ex) {
if (traceEnabled) {
logger.trace("Ignored non-readable " + resource + ": " + ex.getMessage());
}
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
然后进入isCandidateComponent方法看看怎么判断的:
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
可以看到只获取includeFilters中存在的,我们知道我们自己在配置类的@CompnentScan注解中手动加入了一个Scan_C类到includeFilters,所以Scan_C是可以扫到的,那么加了@Compnent注解的Scan_A又是怎么被扫描的?
其实在实例化一个扫描器时,就会默认加入Compnent.Class到IncludeFilters中,我们可以看看代码:进入扫描器类ClassPathBeanDefinitionScanner
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment, @Nullable ResourceLoader resourceLoader) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
//加入默认的类到includeFilters
if (useDefaultFilters) {
registerDefaultFilters();
}
setEnvironment(environment);
setResourceLoader(resourceLoader);
}
进去registerDefaultFilters方法看看加了哪些类:
protected void registerDefaultFilters() {
//加入Compnent类
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.
}
}
至此则解答了如何扫描出有@Compnent注解的类的疑问。
总体下来扫描的大概流程:
spring内置的ConfigurationClassPostProcessor执行子接口实现方法(postProcessBeanDefinitionRegistry)
-----》通过容器registry获取spring内置类+传入的配置类
-----》依次判断是否为合格的配置类
-----》如是则获取配置类的部分属性,实例化一个扫描器(扫描器实例化的时候会将Compnent类加入到includeFilters中,为了后判断哪些类用了@Compnent注解)
-----》解析配置类是否加了@Compnent、@Improt、@CompnentScan等等注解,有则进行解析
-----》解析@CompnentScan注解,解析注解中的各种属性(includeFilters、excludeFilters、beanNameGenerator等)
----》扫描包路径,使用ASM技术读取文件内容为MetadataReader对象,并判断是否能对应includeFilters中的类(并排除excludeFilters的类)
----》有则转化为ScannedGenericBeanDefifinition对象,并判断ScannedGenericBeanDefifinition是否接口是否抽象、是否加了LockUp注解等等
-----》然后加入Set<BeanDefinition>并返回,最后对其进行注册(put到BeanDefinitionMap,但还不会实例化为bean)。
=============================第二种情况做分析==============================
第二种情况非常简单,而且和第一种都是扫描器调用了doscan方法进行扫描解析的。
AnnotationConfigApplicationContext会实例化一个扫描器,然后该扫描器直接调用doScan(basePackages)方法。
=====================第一种情况下的excludeFilter、includeFilter===================
我们先看看怎么使用这两个@CompnentScan注解的属性。
新增两个测试类,一个加@Compnent,一个不加。
@Component
public class Scan_B {
@PostConstruct
public void init(){
System.out.println("-----------Scan_B");
}
}
public class Scan_C {
@PostConstruct
public void init(){
System.out.println("-----------Scan_C");
}
}
修改下配置类:
/**
* 排除Scan_B,加入Scan_C
* */
@ComponentScan(value = "com.spring.demo.scan",
excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = Scan_B.class)},
includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = Scan_C.class)})
public class ContextConfig {
}
此时启动ComponentScanTest,查看控制台打印:
由此我们可以知道includeFilters 属性的作用是增加型过滤器,而excludeFilters是排除型过滤器。
》》》》》》》》》》》》》》》》》》》问题解答
1、为什么spring扫描要使用ASM获取字节码技术?
ASM是读取字节码文件,并不会提前加载类或执行类的方法,这样不会对类的生命周期等流程产生影响。且效率较高(比反射等方式效率高)
我们以第一种情况为例,看看完成扫描的时候Scan_A有没有被实例化(如被实例化会执行init方法)前一篇文章我们可以知道情况2执行invokeBeanDefinitionRegistryPostProcessors后会完成扫描,此时我们在这行打上断点。
此时发现Scan_A还没被扫描到,控制台也没打印任何东西,让后我们执行这行代码后:
此时Scan_A被扫描到了,但ini方法并没有被实例化,所以spring采用ASM扫描并不会对对象实例化产生影响。(如果此时使用反射等方式,此时就会对对象进行实例化,从而打乱了原先对象实例化的顺序(提前进行实例化了))
2、两个扫描器的区别?
两种方式的扫描器类型都是ClassPathBeanDefinitionScanner,且都调用了doscan方法进行扫描解析。不同的是@CompnentScan注解提供了更多属性的配置,例如nameGenerator、includeFilters等。
3、@Inherited注解的作用
配置是否能被子类继承。