SpringBean对象扫描过程,如下图。
结合上图的流程和源码,简要分析一下Bean扫描的源码。
// Invoke factory processors registered as beans in the context.
// 调用在上下文中注册为 bean 的工厂处理器
invokeBeanFactoryPostProcessors(beanFactory);
@ComponentScan
,提到spring扫描,离不开这个接口,那么它是如何工作的呢?这里就讲到Spring的refresh()
方法,这个方法就是Spring源码的核心,里面包含了12个方法,今天说的Bean对象扫描入口就在其中的一个方法中。
/**
* Instantiate and invoke all registered BeanFactoryPostProcessor beans,
* respecting explicit order if given.
* Must be called before singleton instantiation.
*/
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
...
}
进入这个方法中,这里是利用beanFactoryPostProcessor(bean工厂的后置处理器)去完成的。再点击invokeBeanFactoryPostProcessors()
方法进入。
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
这里根据获取的bean工厂的后置处理器,找到BeanDefinitionRegistry
类型的处理器,去获取BeanDefinition对象,也就是Bean对象的前身。
bean工厂后置处理器很多,他们帮我们完成很多很多的工作,这里只是其中的一个,然后我们点击postProcessBeanDefinitionRegistry()
方法进入。
/**
* Derive further bean definitions from the configuration classes in the registry.
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
}
我们可以看到这个方法的定义是说,从注册表中的配置类派生更多的 beanDefinition
对象。
系统先根据registry
对象生成一个hashcode值,如果this.registriesPostProcessed
(set集合)中存在,就抛出异常,提示已经调用过了,如果没有,则进入processConfigBeanDefinitions(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<>();
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);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
...
// 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 {
parser.parse(candidates);
...
}
这个方法注释说,构建和认证配置模型基于配置类注册器,
方法中先获取配置类的BeanDefinitionHolder
对象集合,再创建配置类解析器,去解析每一个配置类。我们点击parser.parse()
方法。
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
...
进入parse()方法后,我们看到,for循环遍历配置类的BeanDefinitionHolder
对象,再根据配置类的类型(基于注解的,抽象的,其他的)分别去解析。
我们再点击parse()方法,
点击processConfigurationClass(new ConfigurationClass(metadata, beanName));
方法,找到下面这段代码。
// Recursively process the configuration class and its superclass hierarchy.
// 递归处理配置类及其超类层次结构。
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
接着点击doProcessConfigurationClass()
方法,进入里面。
// 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)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
...
}
}
这个方法就是读取类、方法上的注解,比如@ComponentScan
、@Bean
、@Component
等,将这些构建为配置类,再解析成对应的beanDefinition对象。
因为篇幅有限,这里只粘贴处理@ComponentScan
注解的代码,可以看到如果componentScans集合不为空,则遍历解析那些加了@ComponentScan
注解的类。
我们进入parse()方法中。
准备工作:
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));
...
return scanner.doScan(StringUtils.toStringArray(basePackages));
在上面的源码中,我们可以看到,先获取注解componentScan
的“nameGenerator”属性值,这里如果你没有指定,那就是获取默认的BeanNameGenerator.class
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
点击进入BeanNameGenerator.class中,这是个接口,进入它的注解实现类中,可以看到下面这个方法。
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
if (definition instanceof AnnotatedBeanDefinition) {
String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
if (StringUtils.hasText(beanName)) {
// Explicit bean name found.
return beanName;
}
}
// Fallback: generate a unique default bean name.
return buildDefaultBeanName(definition, registry);
}
这段代码,先查找Bean对象注解上有无beanName,如果有则直接返回,没有则进入buildDefaultBeanName()方法中生成唯一的默认beanName。
点进去,获取beanClassName,再进入到Introspector类中的decapitalize()方法里。
public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
上面decapitalize()方法中,如果开头的第一个、第二个字母都是大写,那么就不做处理,直接返回;如果只是首字母大写,则首字母转为小写,然后返回。
点击scanner.doScan()
,开始进入扫描方法。
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());
...
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;
doScan()方法中,根据basePackages包路径获取BeanDefinition集合,我们点击findCandidateComponents()
方法,进入里面,这里提供两种扫描方式,一种是基于componentsIndex
,这里我们看另外一种默认的方式: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);
}
...
}
return candidates;
}
阅读上面这段源码,先拼接获取包路径;然后利用资源解析器去扫描路径下的所有Resource对象;如果资源对象可读,则利用元数据读取器工厂去获取当前传入资源的元数据读取器。
在获取类的元数据读取器时,用到ASM技术,它是asm包下的一个类属性获取技术,不同于jvm的反射,它不需要占用太多的jvm内存。
获取到元数据读取器后,还要经过两层判断:
isCandidateComponent(metadataReader)
点击进入这个方法,可以看到:
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;
}
这里就是对excludeFilters
、includeFilters
过滤
isConditionMatch()
方法是按指定条件去过滤,同时你也可以自己去实现Condition
接口,重写里面的matches()方法,将你重写的类加到@Conditional
注解中,按自己指定条件匹配对象。
这里就不做演示,感兴趣的可以自己去尝试一下。
isCandidateComponent(sbd)
点击进入下面的方法,这里判断当前扫描的类是否是独立类,即类的获取不需要依赖其他类,比如内部类就不是独立类(静态内部类除外)
判断类既不是接口也不是抽象的,如果是则返回true;如果不是,则判断类是抽象的且其中有方法上有@Lookup
注解的,也返回true。
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return (metadata.isIndependent() && (metadata.isConcrete() ||
(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}
获取到的BeanDefinition集合返回到doScan()
方法。
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
点击进入processCommonDefinitionAnnotations()
方法,
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
if (lazy != null) {
abd.setLazyInit(lazy.getBoolean("value"));
}
else if (abd.getMetadata() != metadata) {
lazy = attributesFor(abd.getMetadata(), Lazy.class);
if (lazy != null) {
abd.setLazyInit(lazy.getBoolean("value"));
}
}
if (metadata.isAnnotated(Primary.class.getName())) {
abd.setPrimary(true);
}
AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
if (dependsOn != null) {
abd.setDependsOn(dependsOn.getStringArray("value"));
}
AnnotationAttributes role = attributesFor(metadata, Role.class);
if (role != null) {
abd.setRole(role.getNumber("value").intValue());
}
AnnotationAttributes description = attributesFor(metadata, Description.class);
if (description != null) {
abd.setDescription(description.getString("value"));
}
}
这个方法就是给当前的BeanDefinition对象设置各种注解属性,如果有的话。
找到checkCandidate(beanName, candidate)
方法,进入这个方法中,找到isCompatible()
方法,这个就是判断生成的bean对象是否重复。
// 确定给定的新 bean 定义是否与给定的现有 bean 定义兼容
if (isCompatible(beanDefinition, existingDef)) {
return false;
}
最后将BeanDefinition对象集合返回,做下一步的处理。