文章目录
系列文章主旨
从如何把自己的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的配置类,该jie类可以解析配置类的所有注解,得到配置类的注解元数据信息,本篇解析的是@ComponentScan注解的元数据信息;
其内部的还有一个组件解析器componentScanParser,把解析完的@ComponentScan注解的元数据信息交给该解析器,调用其解析方法parse();该组件会实例化扫描器ClassPathBeanDefinitionScanner,这个扫描器会真正的去执行扫描的动作;
ClassPathBeanDefinitionScanner该组件本身重要的组件为include、exclude拦截器,通过使用ASM技术1得到的类文件数据,会借由拦截器校验是否符合Spring认为的规则,如:@Component注解标识的类,就会把这些类构建为BeanDefinition对象,这样,就得到了所有的扫描bean;
进阶源码解析
spring注册配置类后初始化,调用内置工厂后置处理器
源码解析
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, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// .. 忽略非相关代码
// 在这里调用
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
// .. 忽略非相关代码
}
这里就是ConfigurationClassPostProcessor实现的postProcessBeanDefinitionRegistry()方法
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 忽略非相关
// 继续跟进
processConfigBeanDefinitions(registry);
}
跟进方法之后,这就是我们要分析的扫描Bean的入口
识别并扫描Bean,源码入口
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));
}
}
// 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<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
// 核心入口
parser.parse(candidates);
parser.validate();
// ..忽略非相关
}
while (!candidates.isEmpty());
// ..忽略非相关
}
下面就是围绕着该方法进行解析;
初始化配置类解析器ConfigurationClassParser
找到容器中的所有配置类
图解
源码解析
先从spring拿到所有已注册bean的名称,然后进行遍历;
遍历时,会通过bean名称拿到BeanDefinition,然后先看是不是没有处理过,没有的话继续,通过checkConfigurationClassCandidate()方法,知道是不是配置类,是的话,会存入到configCandidates集合里面;
如果集合里面最后没有配置类,就直接返回了,因为连扫包的路径都不知道;
如果有的话,给这些配置类进行一个排序,等待后面有序解析;
// 拿到所有bean的名称
List<BeanDefinitionHolder> configCandidates = 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<String, Object> 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<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());
}
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
图解
源码解析
为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.beans.factory.config.BeanDefinitionHolder>)
// org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
// 继续跟
parser.parse(candidates);
遍历所有配置类,继续解析
public void parse(Set<BeanDefinitionHolder> configCandidates) {
// 遍历配置类集合
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<String> filter) 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);
}
解析配置类各项注解入口:org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
这里会发现会解析配置类的诸如内部类、@Import、@Bean、@ComponentScan等注解,这里的解析等待后续篇章的更新;
本篇着重看解析@ComponentScan的解析;
得到@ComponentScan的信息,得到扫描包路径
如果配置了包路径,而且不会被跳过(一般不会跳过),这里就会调用componentScanParser组件,进行包路径的扫描解析,得到beanDefinition集合然后注册到beanDefinitionMap中;
然后遍历扫描得到的bd集合,如果发现它是配置/候补配置类,则继续调用parse方法,再去解析这个新扫描出来的类
@Nullable
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// 解析Component注解
// ..忽略非相关
// 解析PropertySource 注解
// ..忽略非相关
// 解析ComponentScan 注解
// 本篇核心解析这里
Set<AnnotationAttributes> 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<BeanDefinitionHolder> 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
图解
源码解析
构造方法实例化,ClassPathBeanDefinitionScanner
这里先是调用构造方法实例化
public Set<BeanDefinitionHolder> parse(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<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));
}
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 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) {
// 没有配置@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注解;
/***
* org.springframework.context.annotation.AnnotationConfigUtils#processCommonDefinitionAnnotations(org.springframework.beans.factory.annotation.AnnotatedBeanDefinition, org.springframework.core.type.AnnotatedTypeMetadata)
*/
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<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
// 核心入口
return scanCandidateComponents(basePackage);
}
}
解析,scanCandidateComponents()方法
先构建正确的类扫包路径,先构建正确的类扫包路径,最终会构建出来形如:classpath*:/扫包路径**/*.class路径;
// 最终会构建为: classpath*:/扫包路径**/*.class
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
// ..方法内部其他内容
}
根据构建好的路径,得到资源文件集合
// 根据构建好的路径,得到资源文件集合
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
遍历资源文件;
先通过ASM字节码技术1,读取出来类文件的元数据信息
private Set<BeanDefinition> scanCandidateComponents(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方法,去递归解析;
本文深入探讨Spring框架中@ComponentScan注解的工作原理和源码解析。从Spring初始化开始,分析ConfigurationClassPostProcessor如何处理配置类,进而触发扫描过程。通过ConfigurationClassParser解析配置类,使用ClassPathBeanDefinitionScanner进行包扫描,识别@Component等注解的类并将其注册为Bean。文章详细讲解了每个关键步骤的源码和逻辑,帮助读者理解Spring的核心机制。






548

被折叠的 条评论
为什么被折叠?



