12.postProcessBeanDefinitionRegistry源码(上)
所以分为2部分去讲。
第一部分主要是讲遍历候选的BeanDefinition,然后根据BeanDefinition的属性configClass判断是否是配置类,如果是配置类加入候选配置类集合,第二部分对候选配置类集合进行解析。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 一个工厂的后置处理器只会执行一次
// 具体逻辑是根据对象的hashCode判断是否被调用过
int registryId = System.identityHashCode(registry);
//如果被调用过 那么
if (this.registriesPostProcessed.contains(registryId)) {
throw new Exception("postProcessBeanDefinitionRegistry already called");
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new Exception("postProcessBeanDefinitionRegistry already called");
}
this.registriesPostProcessed.add(registryId);
//重点代码跟进去!!!
//重点代码跟进去!!
//重点代码跟进去!
processConfigBeanDefinitions(registry);
}
ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry(registry)
的核心逻辑在processConfigBeanDefinition()方法中。
processConfigBeanDefinitions源码
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
/**
* registry就是DefaultListableBeanFactory
* DefaultListableBeanFactory是bean工厂
* 步骤1.获取所有已经注册的beanName放入候选数组candidateNames
*/
String[] candidateNames = registry.getBeanDefinitionNames();
/**
* 步骤2.遍历candidateNames对应的BeanDefinition,判断BeanDefinition是否是配置类
* 如果是配置类将BeanDefinition加入configCandidates
* 比如下面配置类,找到配置类后spring就知道从哪里开始扫描啦
* @ComponentScan("com")
* public class Config {
* }
*/
for (String beanName : candidateNames) {
// 根据beanName获得BeanDefinition
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
/*
BeanDefinition中有这么一个变量attributes,类型为LinkedHashMap
Map<String, Object> attributes = new LinkedHashMap<>();
用来存储属性值,如果该attributes中存在configurationClass这个key
且对应的值是full或者lite,意味着已经处理过了,直接跳过。
后面处理BeanDefinition时会给bd设置一个属性
key为configurationClass,value为full或者lite
*/
//如果是 FULL 或者 LITE 那么跳过
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
//啥也不做 说明被处理过了
}
/*
checkConfigurationClassCandidate方法会判断是否是配置类
并为BeanDefinition设置属性configurationClass为lite或者full或者false
如果加了@Configuration,那么对应的BeanDefinition为full。
如果加了@Component,@ComponentScan,@Import,@ImportResource这些注解
或者是带有@Bean方法的类,则为lite。
如果不是lite和full 返回的是false
lite和full均表示这个BeanDefinition对应的类是一个配置类
为BeanDefinition设置lite和full属性值是为了后面在使用
具体可看代码 1.checkConfigurationClassCandidate
*/
else if(ConfigurationClassUtils.checkConfigurationClassCandidate
(beanDef, this.metadataReaderFactory)){
//添加到候选配置集合中,后面统一处理
//BeanDefinitionHolder对BeanDefinition和名字做了简单封装
configCandidates.add
(new BeanDefinitionHolder(beanDef, beanName));
}
}
//configCandidates放的是候选配置类
//即带有如下注解的类
//@Configuration @Component @ComponentScan @Import @ImportResource
//或者是带有@Bean方法的类
//如果没有候选配置类,就直接返回,啥也不执行
if (configCandidates.isEmpty()) {
return;
}
//步骤3
//将配置类进行排序
//我们可以提供多个配置类,然后在配置类上添加@Order注解决定配置类的先后调用顺序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
/**
* 判断是否有自定义的beanName生成器,没有话就用默认的BeanNameGenerator
* 因为后面会扫描出所有加入到spring容器中calss类
* 然后把这些class解析成BeanDefinition类
* 此时需要利用BeanNameGenerator为这些BeanDefinition生成beanName
* 默认的BeanNameGenerator是类名首字母小写
*/
SingletonBeanRegistry sbr = null;
// DefaultListableBeanFactory是SingletonBeanRegistry类型
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
//localBeanNameGeneratorSet = false
if (!this.localBeanNameGeneratorSet) {
//步骤4
//获取internalConfigurationBeanNameGenerator
//返回的是null
BeanNameGenerator generator = (BeanNameGenerator)
sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
/**
* 实例化ConfigurationClassParser是为了解析各个配置类即带@Configuration注解的类
* 初始化ConfigurationClassParser的一些属性
*/
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory,
this.problemReporter, this.environment,
this.resourceLoader,
this.componentScanBeanNameGenerator,
registry);
/**
* 实例化两个set
* candidates用于保存候选配置类
* alreadyParsed用于保存已经解析过的配置类
*/
Set<BeanDefinitionHolder> candidates =
new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed =
new HashSet<>(configCandidates.size());
//注意 这个 doWhile是在for循环里
do {
// 开始扫描/注册包下的类
// 在此处会解析配置类上的注解
// 这一步只会将加了@Configuration注解以及@ComponentScan注解扫描的类加入到
// BeanDefinitionMap中
// 通过其他注解(例如@Import、@Bean)的方式,在parse()方法并不会将其解析为 // BeanDefinition放入到BeanDefinitionMap中
// 真正放入到map中是在下面的this.reader.loadBeanDefinitions()方法中实现的
parser.parse(candidates);
parser.validate();
//获取本次扫描到的所有configurationClasses
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());
}
// 在这里统一处理,没有注册的进行注册
// 将上一步parser解析出的ConfigurationClass类加载成BeanDefinition
// 实际上经过上一步的parse()后,解析出来的bean已经放入BeanDefinitionMap中
// 但是由于这些bean可能会引入新的bean
// 例如实现了ImportBeanDefinitionRegistrar或者ImportSelector接口的bean
// 或者bean中存在被@Bean注解的方法
// 因此需要执行一次loadBeanDefinition()去
// 处理ImportBeanDefinitionRegistrar或者@Import或者@从ImportedResource
// 或者@Bean修饰的方法 来加载bd
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
// 这里判断registry.getBeanDefinitionCount() > candidateNames.length
// 目的是为了知道reader.loadBeanDefinitions(configClasses)
// 这一步有没有向BeanDefinitionMap中添加新的BeanDefinition
// 实际上就是看配置类(例如AppConfig类会向BeanDefinitionMap中添加bean)
// 如果有,registry.getBeanDefinitionCount()就会大candidateNames.length
// 这样就需要再次遍历新加入的BeanDefinition,并判断这些bean是否已经被解析过了
// 如果未解析,需要重新进行解析
// 这里的AppConfig类向容器中添加的bean
// 实际上在parser.parse()这一步已经全部被解析了
// 所以为什么还需要做这个判断,目前没看懂,似乎没有任何意义。
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());
}
// 如果有未解析的类,则将其添加到candidates中
// 这样candidates不为空,就会进入到下一次的while的循环中
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());
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton
(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
((CachingMetadataReaderFactory)
this.metadataReaderFactory).clearCache();
}
}
步骤总结
步骤1:遍历所有的BeanDefinitionName
方法伊始,我们拿到容器中所有的BeanDefinition的名字,包括系统内置的5个后置处理器及我们提供的Config配置类。
紧接着,循环遍历这些BeanDefinition,过滤掉spring内置的后置处理器,留下我们提供的配置类,解析配置类,完成扫描和注册,这个配置类只能解析使用一次。
那么spring如何过滤出我们提供的配置类以及如何保证只解析使用一次呢?
保证使用一次很简单,只要第一次解析使用完成后添加一个标志即可。在BeanDefinition维护一个变量, Map<String, Object> attributes = new LinkedHashMap<>();用来存储属性值。
如果该attributes中存在configurationClass这个键,且对应的值是full或者lite意味着已经处理过了,直接跳过。
下面的源码就是判断是否有该键值的:
private static final String CONFIGURATION_CLASS_FULL = "full";
private static final String CONFIGURATION_CLASS_LITE = "lite";
public static boolean isFullConfigurationClass(BeanDefinition beanDef) {
return CONFIGURATION_CLASS_FULL
.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
}
public static boolean isLiteConfigurationClass(BeanDefinition beanDef) {
return CONFIGURATION_CLASS_LITE
.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
}
//如果是@Configuration 就是full
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
return metadata.isAnnotated(Configuration.class.getName());
}
//如果是下面这些注解修饰的类或者是带有@Bean的方法类 就是Lite
/*
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
*/
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
if (metadata.isInterface()) {
return false;
}
//下面这些注解修饰的类
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
//带有@Bean的方法类
return metadata.hasAnnotatedMethods(Bean.class.getName());
}
最关键的是如何筛选出我们的配置类?答案就是根据类型。
文章开头,我们通过代码往spring中添加我们的配置类context.register(Config.class);,我们跟进这行代码看spring是如何把Config转成BeanDefinition的,一直跟到doRegisterBean方法:
//beanClass就是Config.class
AnnotatedGenericBeanDefinition abd =
new AnnotatedGenericBeanDefinition(beanClass);
//... 中间省略...
BeanDefinitionHolder definitionHolder =
new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils
.applyScopedProxyMode
(scopeMetadata, definitionHolder, this.registry);
BeanDefinitionReaderUtils
.registerBeanDefinition(definitionHolder, this.registry);
由此可见自定义的Config被转成了AnnotatedGenericBeanDefinition类型,还记得之前讲过的BeanDefinition继承图吗,笔者不厌其烦的再次祭出BeanDefinition的继承图:
AnnotatedGenericBeanDefinition在最底层右面第二个的位置。上文中的“注册时机”我们能看到,spring内置的后置处理器都是RootBeanDefinition类型的。
由此可见AnnotatedGenericBeanDefinition和RootBeanDefinition没有半毛钱关系,我们自然通过instanceof类型匹配关键字过滤出我们的配置类了!分析过滤的源码:
else if (ConfigurationClassUtils
.checkConfigurationClassCandidate
(beanDef, this.metadataReaderFactory)) {
configCandidates
.add(new BeanDefinitionHolder(beanDef, beanName));
}
<!---->
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())) {
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}else if (beanDef instanceof AbstractBeanDefinition &&
((AbstractBeanDefinition) beanDef).hasBeanClass()) {
Class<?> beanClass =
((AbstractBeanDefinition) beanDef).getBeanClass();
metadata = new StandardAnnotationMetadata(beanClass, true);
}else {
try {
MetadataReader metadataReader =
metadataReaderFactory.getMetadataReader(className);
metadata = metadataReader.getAnnotationMetadata();
}
catch (IOException ex) {
return false;
}
}
//判断是否存在@Configuration注解
//设置为FULL
if (isFullConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
//判断是否存在@Bean修饰的方法的类或者被以下注解修饰的类
//设置为LITE
/*
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
*/
else if (isLiteConfigurationCandidate(metadata)) {
beanDef.setAttribute
(CONFIGURATION_CLASS_ATTRIBUTE,CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
//解析order的value值作为属性设置进去
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
步骤2:如果符合以下条件,那么加入集合
@Configuration = full
@Component = lite
@Service = lite
@Component
public @interface Service {}
@Controller = lite
@Component
public @interface Controller {}
@Repository = lite
public @interface Repository {}
@ComponentScan = lite
@ComponentScans = lite
@ComponentScans({@ComponentScan(""),@ComponentScan("")})
@Import = lite
@ImportResource = lite
带@Bean注解的方法 = lite
跟进checkConfigurationClassCandidate这行代码,这行代码的意思是找到我们的配置类并获取到他的注解信息。
如果注解包含了@Configuration,则设置属性键值对configurationClass=full,会加入候选集合。
否则判断注解是否包含@Component、@ComponentScan、@Import、@ImportResource、@Bean注解的方法。如果是则设置键值对configurationClass=lite,会加入候选集合。
如果都不是则返回false,说明Config不是配置类,不会加入候选集合中。
看源码:
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
//获取class名字,这里是com.config.Config
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
AnnotationMetadata metadata;
//判断BeanDefinition是否是AnnotatedBeanDefinition类型,
//AnnotatedGenericBeanDefinition实现了AnnotatedBeanDefinition
//我们的配置类Config对应的BeanDefinition确实就是AnnotatedBeanDefinition类型
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition)
beanDef).getMetadata().getClassName())) {
//获取配置类的注解信息
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
//如果BeanDefinition是AbstractBeanDefinition并且包装了业务类,
//RootBeanDefinition继承了如果BeanDefinition是AbstractBeanDefinition
//spring内置的后置处理器确实是AbstractBeanDefinition类型的
else if (beanDef instanceof AbstractBeanDefinition &&
((AbstractBeanDefinition) beanDef).hasBeanClass()) {
//beanClass = ConfigurationClassPostProcesso
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
//拿到注解信息
metadata = new StandardAnnotationMetadata(beanClass, true);
}else {
try {
//既不是AnnotatedBeanDefinition类型也不是AbstractBeanDefinition类型
//或者是AbstractBeanDefinition类型但没有包装业务类
MetadataReader metadataReader =
metadataReaderFactory.getMetadataReader(className);
metadata = metadataReader.getAnnotationMetadata();
}
catch (IOException ex) {
return false;
}
}
//判断注解中是否包含了@Configuration注解
if (isFullConfigurationCandidate(metadata)) {
//设置属性,相当于beanDef.setAttribute("configurationClass","full")
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE,
CONFIGURATION_CLASS_FULL);
}
//判断注解中是否包含了@Component、ComponentScan、Import、ImportResource其中之一
//或者是否有@Bean注解的方法。
else if (isLiteConfigurationCandidate(metadata)) {
//设置属性,相当于beanDef.setAttribute("configurationClass","lite")
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE,
CONFIGURATION_CLASS_LITE);
}else {
//实际上,如果你打断点仔细运行,你会发现,spring内置后置处理器都没有上面那些注解
return false;
}
//如果有@Order注解,拿到注解的value值,并将值设置到属性中,后面执行会根据这个属性值的大小进行先后调用
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
由此可知,所谓的配置类就是要么含有@Configuration注解,要么含有@Component、@ComponentScan、@Import、@ImportResource其中之一,要么类中含有@Bean注解的方法。
注意其中 @Component包括他的子注解@Service、@Controller、@Repository。spring后续根据这些注解完成扫描和注册。 实际上上面的代码运行结束后,只有我们的配置类Config对应的BeanDefinition完成了属性值的设置,且返回true,最后添加到集合configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));。spring内置的后置处理器都没有上面所说的注解所以返回false,自然不会添加到集合中。源码中我也解释清楚了,希望读者仔细阅读思考,有疑问打在留言区一起探讨,我会一一回复。
//如果扫描完毕没有配置类就直接返回
if (configCandidates.isEmpty()) {
return;
}
如果,我们没有提供配置类,直接返回,后续也不会扫描和注册。也就是我们自定义的bean不会被引入。
运行到这里的时候我们注册了自定义配置类对应的BeanDefinition, 并且BeanDefinition对应的属性configurationClass是full。
步骤3:对配置类进行排序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
上面checkConfigurationClassCandidate代码中的最后处理了@Order注解,将他的值放到了属性中,在这里,如果你有多个配置类,进行排序,后面会按照顺序进行处理。打个比方:
@ComponentScan("com")
@Order(1)
public class Config {
}
@Service
@Order(2)
public class Config2 {
}
public class SpringTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Config.class);
context.register(Config2.class);
context.refresh();
}
}
步骤4:获取BeanName生成器
SingletonBeanRegistry sbr = null;
/**
* 由于当前传入的是DefaultListableBeanFactory是SingletonBeanRegistry的子类
*/
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
/**
* 判断是否有自定义的beanName生成器
*/
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator)
sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
// 获取spring默认的beanName生成器,这里为空
if (generator != null) {
/**
* componentScanBeanNameGenerator与importBeanNameGenerator定义时 * 就赋值了new AnnotationBeanNameGenerator()
* 如果spring有默认的beanName生成器,则重新赋值
*/
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
说实话,笔者不是很关心这几行源码,大概意思就是如果你提供了自定义的名字生成器那就用你的,否则spring用自己默认的名字生成器,我知道是Class类名首字母小写。读者可研究下名字生成器类AnnotationBeanNameGenerator,很简单。
它实现了BeanNameGenerator接口,这个接口只有一个String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);方法,你可以实现这个接口生成自定义名字生成器。打个比方:
/自定义名字生成器
public class MyNameGenerator implements BeanNameGenerator {
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
return definition.getBeanClassName()+"源码之路";
}
}
public class SpringTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Config.class);
//创建一个名字生成器类
MyNameGenerator myNameGenerator = new MyNameGenerator();
//通过上面的源码可知,必须以单例的方式注册的bean工厂中
context.getBeanFactory()
.registerSingleton
(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
myNameGenerator);
context.refresh();
System.out.println();
}
}
在读此篇博客之前,如果你要生成一个自定义名字生成器你怎么做?查百度对吧,百度没有呢?spring源码重要吗?不重要,工作中会用spring就行,我关注业务就行,谁会关注源码?谁会关注?你的技术领导会,你们的CTO会,因为他是建筑师,而你是建筑工人。我没有吹嘘我自己,我只是强调spring源码对你的整个职业生涯起着至关重要的作用。
继续往下看:
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
标准资源环境,以后专门写一篇博客讲解环境抽象。
上面我们拿到了配置类集合configCandidates,下一步就是解析处理这些配置类。鉴于解析篇幅太长所以放在下一篇文章。
下一篇文章可以带着问题去读。
1.@Configuration作用?
2.如何解析@Configuration的类?
3.解析@ComponentScan时机?
4.解析@Import注解时机,如何解析的?
5.解析@Bean注解的时机?
转载自:https://juejin.cn/post/7236758506597417017