- 系列文章主旨
- 上篇内容
- 本篇内容
- 核心原理
- 代码示例
- 图解
- 核心处理逻辑
- 进阶源码解析
- spring注册配置类后初始化,调用内置工厂后置处理器
- 源码解析
- 识别并扫描Bean,源码入口
- 初始化配置类解析器ConfigurationClassParser
- 找到容器中的所有配置类
- 图解
- 源码解析
- 实例化配置类解析器ConfigurationClassParser
- 图解
- 源码解析
- 遍历配置类,调用配置类解析器ConfigurationClassParser的解析方法,内部进行扫包处理
- 图解
- 源码解析
- 解析配置类各项注解入口:org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
- 初始化扫描器,ClassPathBeanDefinitionScanner
- 图解
- 源码解析
- 构造方法实例化,ClassPathBeanDefinitionScanner
- 扫描器进行扫描
- 图解
- 源码解析
- 扫描得到的BD集合,注册到spring容器beanDefinitionMap中
- findCandidateComponents()方法,得到扫描结果bd集合
- 解析,scanCandidateComponents()方法
- 总结
- 注脚
上篇内容从如何把自己的Bean注册到Spring容器开始,从该点出发,每篇只关注一个核心流程的原理、源码,再由该篇带出引申出来的其他Spring知识点,继续剖析,最终达到理解Spring核心原理的目的;
本篇内容分析了Spring如何把自己的类注册到容器中,并且引申到spring会实例化自己内部的postProcessor完成扫描bean;
详情:【Spring】Spring原理源码解析(一)-- 从如何把自己的类注册为Bean到容器开始;
核心原理 代码示例从上一篇的spring内部实现的ConfigurationClassPostProcessor,分析spring识别@ComponentScan并完成扫描的原理源码;
文章图片均出自:https://www.processon.com/view/link/62d776fb6376893785aaa0df
指定配置类初始化spring容器,方法内部会把配置类注册到spring容器中
public static void main(String[] args) {
// 该方法会把我们的配置类注册到spirng容器中
// 并执行refresh方法,初始化spring容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
}
配置类MyConfig,指定了扫描包路径
@ComponentScan(basePackages = "com.zsh.demo.spring.learn.register.bean")
public class MyConfig {
}
图解
核心处理逻辑
Spring容器进行初始化时,会默认生成自己的bean工厂后置处理器ConfigurationClassPostProcessor,并注册到spring容器中,然后在执行所有的bean工厂后置处理时,就会执行到ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry()方法;
在执行该方法时,会实例化配置类的解析器ConfigurationClassParser,该解析器会解析我们注册到Spring的配置类,该配置类可以解析配置类的所有注解,得到配置类的注解元数据信息,本篇解析的是@ComponentScan注解的元数据信息;
其内部的还有一个组件解析器componentScanParser,把解析完的@ComponentScan注解的元数据信息交给该解析器,调用其解析方法parse();该组件会实例化扫描器ClassPathBeanDefinitionScanner,这个扫描器会真正的去执行扫描的动作;
进阶源码解析 spring注册配置类后初始化,调用内置工厂后置处理器 源码解析ClassPathBeanDefinitionScanner该组件本身重要的组件为include、exclude拦截器,通过使用ASM技术1得到的类文件数据,会借由拦截器校验是否符合Spring认为的规则,如:@Component注解标识的类,就会把这些类构建为BeanDefinition对象,这样,就得到了所有的扫描bean;
public AnnotationConfigApplicationContext(Class>... componentClasses) {
this();
// 注册配置类
register(componentClasses);
// 初始化
refresh();
}
this()方法会把内部的工厂后置处理器ConfigurationClassPostProcessor,注册到spring容器中,位置是在:org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object),详情可以参考【Spring】Spring原理源码解析(一)-- 从如何把自己的类注册为Bean到容器开始
register()方法把配置类解析为BeanDefinition并注册到spring容器中;
refresh()方法为spring容器初始化方法,跟进去,找到调用工厂后置处理器调用方法,invokeBeanFactoryPostProcessors;
继续跟进,找到调用非api加入的BeanFactoryPostProcessors调用postProcessBeanDefinitionRegistry位置
public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors) { // .. 忽略非相关代码 // 在这里调用 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); // .. 忽略非相关代码 }
这里就是ConfigurationClassPostProcessor实现的postProcessBeanDefinitionRegistry()方法
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 忽略非相关
// 继续跟进
processConfigBeanDefinitions(registry);
}
识别并扫描Bean,源码入口跟进方法之后,这就是我们要分析的扫描Bean的入口
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List 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));
}
}
// 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);
});
// 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();
}
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set candidates = new LinkedHashSet<>(configCandidates);
Set alreadyParsed = new HashSet<>(configCandidates.size());
do {
// 核心入口
parser.parse(candidates);
parser.validate();
// ..忽略非相关
}
while (!candidates.isEmpty());
// ..忽略非相关
}
初始化配置类解析器ConfigurationClassParser 找到容器中的所有配置类 图解下面就是围绕着该方法进行解析;
源码解析
先从spring拿到所有已注册bean的名称,然后进行遍历;
遍历时,会通过bean名称拿到BeanDefinition,然后先看是不是没有处理过,没有的话继续,通过checkConfigurationClassCandidate()方法,知道是不是配置类,是的话,会存入到configCandidates集合里面;
如果集合里面最后没有配置类,就直接返回了,因为连扫包的路径都不知道;
如果有的话,给这些配置类进行一个排序,等待后面有序解析;
// 拿到所有bean的名称 ListconfigCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); // 遍历 for (String beanName : candidateNames) { // 拿到bean定义 BeanDefinition beanDef = registry.getBeanDefinition(beanName); // 这里是看下是不是重复校验了bean,因为有可能名称不一样,但实际上是同一个类 if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } // 这里checkConfigurationClassCandidate()会校验这些bean哪些是配置类 else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // 如果没有找到配置类,则没有需要扫描的包,这里就会直接退出 if (configCandidates.isEmpty()) { return; } // 对要被解析的这些配置Bean,进行一个排序 configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); });
这个方法主要是先通过bd拿到元数据信息
然后先通过最简单:
如果不是代理类,又有@Configuration注解,则是配置类;
或者看isConfigurationCandidate()这个方法的校验是不是配置类;
如果上述两个校验都不符合,则不是配置类;
如果是配置类,则把@Order的值放入到元数据,在元数据打上标识(完全是配置类,可能是配置类,无论哪种都会被解析),后续该bd就不会被重复校验了;
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();
}
// 否则,普通的bean都会走这里
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();
// spirng内置的几个工厂后置处理器,不认为是配置类,直接返回false
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 config = metadata.getAnnotationAttributes(Configuration.class.getName());
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
// 标记为完全是配置类
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
// 重要的是看isConfigurationCandidate()方法,来判断是不是配置类,起码有机会是配置类
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
// 否则不是配置类
else {
return false;
}
// 取排序注解的值,后续进行排序后,配置类解析也会按照该顺序
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
这个校验规则主要是
1、不能是接口
2、被@Component、@ComponentScan@Import、@ImportResource、@Bean任意一个注解标识;
如果不符合校验,则不是配置类后补;
private static final Set实例化配置类解析器ConfigurationClassParser 图解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()); } public static boolean isConfigurationCandidate(AnnotationMetadata metadata) { // 接口不是配置类 if (metadata.isInterface()) { return false; } // 看是否被Component、ComponentScan、Import、ImportResource注解标识,如果是的话,就认为是配置类 for (String indicator : candidateIndicators) { if (metadata.isAnnotated(indicator)) { return true; } } 、 try { // 最后,看有没有@Bean标识,有的话也认为是 return metadata.hasAnnotatedMethods(Bean.class.getName()); } catch (Throwable ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex); } return false; } }
源码解析
为ConfigurationClassParser提供必要组件,如环境、注册器、beanName生成器…
然后实例化
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
// 默认会进入
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
// 这里是扫描包的beanName生成器,如果没有配置的话,使用默认的
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();
}
// 实例化ConfigurationClassParser类
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
这里会把一些重要组件初始化
其中包括componentScanParser ,后续就是它来作为扫描bean的解析
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {
this.metadataReaderFactory = metadataReaderFactory;
this.problemReporter = problemReporter;
this.environment = environment;
this.resourceLoader = resourceLoader;
this.registry = registry;
// 这里会生成component注解扫描解析器,后续使用它作为扫描bean的解析器
this.componentScanParser = new ComponentScanAnnotationParser(
environment, resourceLoader, componentScanBeanNameGenerator, registry);
this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
}
遍历配置类,调用配置类解析器ConfigurationClassParser的解析方法,内部进行扫包处理
图解
源码解析
继续跟进到 org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set
)
// org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions // 继续跟 parser.parse(candidates);
遍历所有配置类,继续解析
public void parse(SetconfigCandidates) { // 遍历配置类集合 for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { // 被@Configuation注解的 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()); } } // ..忽略非相关 } } // ..忽略非相关 }
看下当前配置类需不需要按照条件被跳过;
如果不需要跳过,则看是不是处理过,没有处理过则,遍历解析该配置类及其父类;
最后标识已经处理过该类了;
protected void processConfigurationClass(ConfigurationClass configClass, Predicate解析配置类各项注解入口:org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClassfilter) throws IOException { // 没有被@Conditional注解标识的,不会被跳过 if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } // configurationClasses缓存已经处理过的配置类,所以第一次,这里不会进入 ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { // ..忽略非相关 } // 这里处理了本配置类,会继续拿父类去解析 SourceClass sourceClass = asSourceClass(configClass, filter); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter); } while (sourceClass != null); // 标识配置类已经被处理过 this.configurationClasses.put(configClass, configClass); }
这里会发现会解析配置类的诸如内部类、@Import、@Bean、@ComponentScan等注解,这里的解析等待后续篇章的更新;
本篇着重看解析@ComponentScan的解析;
得到@ComponentScan的信息,得到扫描包路径
如果配置了包路径,而且不会被跳过(一般不会跳过),这里就会调用componentScanParser组件,进行包路径的扫描解析,得到beanDefinition集合然后注册到beanDefinitionMap中;
然后遍历扫描得到的bd集合,如果发现它是配置/候补配置类,则继续调用parse方法,再去解析这个新扫描出来的类
@Nullable protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass, Predicate初始化扫描器,ClassPathBeanDefinitionScanner 图解filter) throws IOException { // 解析Component注解 // ..忽略非相关 // 解析PropertySource 注解 // ..忽略非相关 // 解析ComponentScan 注解 // 本篇核心解析这里 Set componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && // 本身没有被@Conditional注解标识的类,不会被跳过 // 如果被标识了,配置类注册bean阶段,这里不会跳过,因此也会跳进来进行扫描 !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // 这里会调用componentScanParser解析方法,执行扫描,得到bd集合 Set scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // 遍历扫描得到的bd集合,如果发现它是配置/候补配置类,则继续调用parse方法,去解析这个类 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 注解 // ..忽略非相关 // 解析ImportResource 注解 // ..忽略非相关 // 解析Bean 注解 // ..忽略非相关 // 解析接口的默认方法 // ..忽略非相关 // 记录拿父类进行解析,外面就会走继续循环去处理父类 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; }
源码解析 构造方法实例化,ClassPathBeanDefinitionScanner
这里先是调用构造方法实例化
public Setparse(AnnotationAttributes componentScan, final String declaringClass) { // 构造方法,跟进 ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); // ... }
这里会发现,构造方法里面先注册了默认的拦截器,然后加入一些需要的组件,如环境组件、资源读取相关组件…
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment, @Nullable ResourceLoader resourceLoader) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
// 默认为true
if (useDefaultFilters) {
// 这里会注册默认的拦截器,重点关注
registerDefaultFilters();
}
// 放置spring的环境组件,这样就可以拿到配置等信息了
setEnvironment(environment);
// ASM读取文件相关组件
setResourceLoader(resourceLoader);
}
默认include拦截器,加入@Component、@ManagedBean、@Named注解类型的拦截器
因此,这里都可以预估到,spring默认会扫描有这些注解的类,并认为是bean进行管理;
protected void registerDefaultFilters() {
// 加入@Component注解类型拦截器
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
// 加入@ManagedBean注解类型拦截器
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 {
// 加入@Named注解类型拦截器
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.
}
}
解析@Component注解元数据内容,并填充到到扫描器中,如:
扫包路径、beanName生成器(一般采用默认的)、lazyInit、scopedProxy…
// 解析注解元数据,相应设置、处理填充到扫描器中
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);
}
// 解析得到扫包路径
Set 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));
}
excludeFilters增加AbstractTypeHierarchyTraversingFilter exclude过滤器
该过滤器重写了matchClassName()方法,并且入参声明为当前配置类类名
可以预估到,spring扫描的时候,会把当前配置类给过滤掉,因为当前配置类已经在BDMap中了
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
调用扫描器的doScan()方法,完成扫描注册bean到spring的BDMap中
// 完成扫描 return scanner.doScan(StringUtils.toStringArray(basePackages));扫描器进行扫描 图解
源码解析 扫描得到的BD集合,注册到spring容器beanDefinitionMap中
遍历扫描包路径
通过findCandidateComponents()方法得到bd扫描结果,然后遍历扫描结果;
这里先会解析@Scope注解,并记录到BD;
默认构造出来的结果都是AnnotatedBeanDefinition,因此解析Bean上面的其他一些注解,如@Lazy、@DependsOn、@Primary…,填充到BD中,;
然后检查如果该bean不存在在spring中,如果可以注册,则这里会先通过上面的@Scope解析结果,看是否需要修改bd为代理bd(此篇章略过不细看),然后把该bean构造为BeanDefinitionHolder(实际上属性就是beanName和beanDefinition);
最后注册到spring容器中,即填充到beanDefinitionMap中(注册动作,后续篇章再分析);
protected SetdoScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { // 扫描得到结果的核心方法 Set candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { // 没有配置@Scope注解的信息,则scope相关的动作这里都不会进行 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); // 生成默认beanName,首字母小写 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { // 扫描解析出来的bean都是AnnotatedBeanDefinition // 所以会进入这里,解析如@Lazy、@DependsOn、@Primary...注解信息,填充到BD AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } // checkCandidate()检查是不是注册过了 if (checkCandidate(beanName, candidate)) { // 构造为bdHolder对象 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); // scope的东西配置了,则才会在这里把bean修改为代理bean,此处省略 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); // 注册到spring容器中,即填充到bdMap中 registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
解析结果BD上的其他注解元数据,并把解析的值填充到BD中;
这里会解析:@Lazy、@Primary、@DependsOn、@Role、@Description注解;
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
// 解析@Lazy
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"));
}
}
// 解析@Primary
if (metadata.isAnnotated(Primary.class.getName())) {
abd.setPrimary(true);
}
// 解析@DependsOn
AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
if (dependsOn != null) {
abd.setDependsOn(dependsOn.getStringArray("value"));
}
// 解析@Role
AnnotationAttributes role = attributesFor(metadata, Role.class);
if (role != null) {
abd.setRole(role.getNumber("value").intValue());
}
// 解析@Description
AnnotationAttributes description = attributesFor(metadata, Description.class);
if (description != null) {
abd.setDescription(description.getString("value"));
}
}
这里是检查有没有在spring容器已经注册过;
如果不能直接获取出来,则后续可以注册,否则,就对比原有的bd和该bd是否为同一个bd,例如通过:resouce文件是否一致、引用是否一致判断;
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
// 拿不到,则直接返回true,可以进行注册
if (!this.registry.containsBeanDefinition(beanName)) {
return true;
}
BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
if (originatingDef != null) {
existingDef = originatingDef;
}
// 否则通过isCompatible()判断源和现bd是否一致
if (isCompatible(beanDefinition, existingDef)) {
return false;
}
// ..抛出异常
throw new ConflictingBeanDefinitionException("Annotation-specified bean name ...");
}
protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {
return (!(existingDefinition instanceof ScannedGenericBeanDefinition) ||
// resouce资源文件是否一致
(newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource())) ||
// 引用是否一致
newDefinition.equals(existingDefinition));
}
bdHolder,其实最主要的就是beanName、bd、和别名数组
public class BeanDefinitionHolder implements BeanMetadataElement {
private final String beanName;
private final BeanDefinition beanDefinition;
@Nullable
private final String[] aliases;
// ...其他内部方法
findCandidateComponents()方法,得到扫描结果bd集合
跟踪方法
默认不会配index了,所以会走scanCandidateComponents()方法
public Set解析,scanCandidateComponents()方法findCandidateComponents(String basePackage) { if (this.componentsIndex != null && indexSupportsIncludeFilters()) { return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); } else { // 核心入口 return scanCandidateComponents(basePackage); } }
先构建正确的类扫包路径,先构建正确的类扫包路径,最终会构建出来形如:classpath*:/扫包路径***.class private Set
scanCandidateComponents(String basePackage) { String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; // ..方法内部其他内容 } 根据构建好的路径,得到资源文件集合
// 根据构建好的路径,得到资源文件集合 Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);遍历资源文件;
先通过ASM字节码技术1,读取出来类文件的元数据信息private SetscanCandidateComponents(String basePackage) { // ... // 遍历资源文件 for (Resource resource : resources) { // ..日志打印相关,省略 if (resource.isReadable()) { // 通过ASM技术获取文件元数据信息 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); // 后续其他操作.. } } } 校验元数据信息,看是否符合规则,可以交给spring进行管理;
若满足的话,则构建为bd对象,判断该对象不是接口或抽象类,则加入到结果集合中;// 通过拦截器校验类是否满足规则,可以交给spring进行管理 if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setSource(resource); // 判断该对象不是接口或抽象类 if (isCandidateComponent(sbd)) { // ..日志打印,省略 candidates.add(sbd); } // .. }调用拦截器的match方法进行判断,如果exclude包含了,则返回false排除,如果include match通过,则用condition来判断,这里一般不会配置,因此可以认为match(),通过了,就是需要找到的;
下面是放入的拦截器集合:
exclude:AbstractTypeHierarchyTraversingFilter(正在解析的配置类类名)
include:AnnotionTypeFilter (@Component、@ManagedBean、@Named)protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { for (TypeFilter tf : this.excludeFilters) { // 调用exclude拦截器的match方法 if (tf.match(metadataReader, getMetadataReaderFactory())) { return false; } } for (TypeFilter tf : this.includeFilters) { // 调用include拦截器的match方法 if (tf.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } return false; }对于默认加入的exclude,include,分开解析match方法
exclude:
使用AbstractTypeHierarchyTraversingFilter
重写了matchClassName()方法,incident,如果配置类本身的话,返回true,即配置类本身会被忽略,原因是该配置类已经被注册到spring中了,无需重复再注册@Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { if (matchSelf(metadataReader)) { return true; } ClassMetadata metadata = metadataReader.getClassMetadata(); if (matchClassName(metadata.getClassName())) { return true; } if (this.considerInherited) { String superClassName = metadata.getSuperClassName(); if (superClassName != null) { // Optimization to avoid creating ClassReader for super class. Boolean superClassMatch = matchSuperClass(superClassName); if (superClassMatch != null) { if (superClassMatch.booleanValue()) { return true; } } else { // Need to read super class to determine a match... try { if (match(metadata.getSuperClassName(), metadataReaderFactory)) { return true; } } catch (IOException ex) { if (logger.isDebugEnabled()) { logger.debug("Could not read super class [" + metadata.getSuperClassName() + "] of type-filtered class [" + metadata.getClassName() + "]"); } } } } } if (this.considerInterfaces) { for (String ifc : metadata.getInterfaceNames()) { // Optimization to avoid creating ClassReader for super class Boolean interfaceMatch = matchInterface(ifc); if (interfaceMatch != null) { if (interfaceMatch.booleanValue()) { return true; } } else { // Need to read interface to determine a match... try { if (match(ifc, metadataReaderFactory)) { return true; } } catch (IOException ex) { if (logger.isDebugEnabled()) { logger.debug("Could not read interface [" + ifc + "] for type-filtered class [" + metadata.getClassName() + "]"); } } } } } return false; }include:
使用AnnotionTypeFilter ,该类重写了matchSelf()方法,因此如果包含了指定注解,即@Component、@ManagedBean、@Named注解如果有的话,则match()方法直接返回true,外层即会认为这是需要管理的类;public class AnnotationTypeFilter extends AbstractTypeHierarchyTraversingFilter { @Override protectedboolean matchSelf(MetadataReader metadataReader) { AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); // 是不是有指定注解 return metadata.hasAnnotation(this.annotationType.getName()) || (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName())); } }总结本篇主要介绍了,spring如何通过内置的,bean工厂后置处理器,解析配置类,识别配置类注解@ComponentScan,然后去扫描对应的包路径,最后生成bd,注册到spring容器中;
即首先spring容器在实例化的时候,就注册了默认的bean工厂后置处理器(ConfigurationClassPostProcessor)以及自己的配置类,然后在第一次执行postProcessBeanDefinitionRegistry(),方法时,就会调用到该内置类的该方法去解析自己的配置类;
先是判断配置类本身,校验是否为配置类,如果类有以下注解:@Configuration(全配置类),@Component、@ComponentScan、@Bean、@Import、@ImportResource(候补配置/lite配置)则是;
接着,构建parser调用parse()方法,开始解析配置类,并且该方法是支持递归解析该配置类的父类的;
parse内部就会去解析配置类上的各种注解,如:内部类、@Import相关等注解、@Bean等,其中就包括了@ComponentScan注解;
解析@ComponentScan注解时,会先去构造扫描器,会通过@ComponentScan得到的扫描包路径,拿到资源文件然后通过ASM技术1,读取文件元数据,再通过扫描器内部的拦截器,默认@Component、@Named、@ManagedBean都会校验通过,然后构造为beanDefinition对象,并且把这些对象
注脚然后每得到这些新的对象,都会再次看下这些新对象是不是配置类/候补配置类,如果是的话,则又丢给parse方法,去递归解析;
ASM技术简介:区别于类似于Class.forName()通过反射技术得到类信息,通过字节码技术直接解析文件,此时,该类文件不会走jvm的生命周期(如不会执行内部的static方法),使得Spring管理文件更加优秀、并且效率比反射要更高; ↩︎ ↩︎ ↩︎



