postProcessBeanDefinitionRegistry
这个章节我们讲的是关于springFramework中最重要也是最最最复杂的方法postProcessBeanDefinitionRegistry方法。该方法来自于spring中最重要的类:ConfigurationClassPostProcessor。
@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);
}
先稍微讲一下该类可以干啥:
- 完成扫描。
- 对配置类进行定义,并且完成对配置类的标志。
- 对@import注解的处理:
- @ImportSelector(SpringBoot) 接口的处理。
- @ImportBeanDefinitionRegistrar(MyBatis) 接口的处理
- 一个普通的类:
-
- 没有任何的特殊注解
- 就只加了Import注解
- @ImportResource
- @Bean注解的处理
- 接口中的@Bean的处理(嵌套)
- @PropertySource注解的处理(XML)
- 处理内部类:该类的内部类中有@Bean注解也是可以解析到的
- 父类处理:该类的父类中有@Bean注解也是可以解析到的
ok上述对于该方法的作用大致讲完了,下面开始进行一一的证明这些功能postProcessBeanDefinitionRegistry方法到底有没有实现,是怎么实现的。
/**
* Prepare the Configuration classes for servicing bean requests at runtime
* by replacing them with CGLIB-enhanced subclasses.
* 准备配置类,以便在运行时为 Bean 请求提供服务,方法是将它们替换为 CGLIB 增强的子类。
*/
@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);
}
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
/**
* Build and validate a configuration model based on the registry of 基于 注册表构建和验证配置模型
* {@link Configuration} classes.
*/
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// 1.存储配置类的集合
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 2.获取所有的BD的Name:正常情况如果你不提供自定义的BD就五个spring内置的
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
/**
* 在这里你获取到BD之后是可以执行你自己想要执行的自定义操作的,如何实现,每个BD都实现了一个接口AttributeAccessor,这个接口有一个方法getAttribute,这个方法的在一个叫做AttributeAccessorSupport类中的实现:
* public Object getAttribute(String name) {
* Assert.notNull(name, "Name must not be null");
* return this.attributes.get(name);
* }
* 其中的this.attributes是一个Map,所以spring是允许你自己在针对于spring给你定义的BD不满足的时候,还可以向这个Map中丢你想要定义的一些规则(其实spring中对于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);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
// 如果未找到@Configuration类,请立即返回
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
// 按先前确定的@Order值排序(如果适用)
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
// 检测通过封闭应用程序上下文提供的任何自定义 Bean 名称生成策略
// 看一下当前的spring环境中有没有生成默认的BeanNameGenerator
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
// localBeanNameGeneratorSet默认为false,那么表示在spring第一次实例化的时候,为true
// spring会从单例池中获取到对应的beanNameGenerator
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
// 并且如果beanNameGenerator不为null的话就会将它赋值给componentScanBeanNameGenerator和importBeanNameGenerator
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// Parse each @Configuration class
// 分析每个@Configuration类
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();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
// 读取模型并根据其内容创建 Bean 定义
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
// 将 ImportRegistry 注册为 Bean 以支持 ImportAware @Configuration类
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();
}
}
我们先来聊5~28行高亮的地方:
- 首先spring先定义了一个配置类的集合
- 然后获取BDName的数组
- 将这个BDName的数组进行遍历
- 从beanDefinitionMap中拿到当前遍历的BD然后下面有一个if块:
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
- 看起来它好像在判断啥东西?
- 是这样的spring提供了一个类较为BeanDefinition,这个咱都知道,但是可以看一下这个类的具体长啥样子:
- 它实现了一个叫做:AttributeAccessor的接口:
- 在看这个刚刚的if块中的getAttribute方法就是来自这个接口的,这个AttributeAccessor有多个实现类,看AttributeAccessorSupport抽象类的实现即可:
@Override
@Nullable
public Object getAttribute(String name) {
Assert.notNull(name, "Name must not be null");
return this.attributes.get(name);
}
- 我们在看这个this.attributes是个啥?
/** Map with String keys and Object values. */
private final Map<String, Object> attributes = new LinkedHashMap<>();
- 很明显它就是一个Map,然后它的注释说,它是一个以String类型的key,Object类型的value的map,那么这个Map的具体是干啥用呢?
- 其实这个map的作用就是在上面第一句说的作用,spring允许你对我定义的BeanDefinition做些额外的定义,然后你将你想要的定义以K-V的形式放入到这个Map中。
- 然后spring在processConfigBeanDefinitions方法中在if中进行判断,你是不是实现了ConfigurationClassPostProcessor.class
public static final String CONFIGURATION_CLASS_ATTRIBUTE =
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
- 然后去实现你自定义的BD定义,比如在spring中对于bean执行的顺序就是这么玩的,我是不是可以在我定义的BD中放入一个属性Order为键,它的排序值为值,在解析BD实例化为Bean的时候,对这些BD做排序。
下面我们继续讲这个方法的前面几段代码:
- 首先我们要看在循环中的第一个if块中判断的是当前BD的attributesMap中是否有ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE?
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
// log:Bean 定义已作为配置类进行处理
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
public static final String CONFIGURATION_CLASS_ATTRIBUTE =
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
- 如果不是的话那么就到下面的else if块中:
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
- 我们进入到if块中的判断ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)方法中:
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;
}
}
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);
}
else if (config != null || isConfigurationCandidate(metadata)) {
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;
}
- 其他的代码我们不看,就先看36~42行的代码,这段代码啥意思?
- 先获取到当前类的MetaDataReader,判断该类有没有实现实现注解@Configuration。
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
- 这个判读是啥意思呢?
- 就是说当前的类是有实现@Configuration注解的,那么我就给当前的BD的就向当前类中的BD中的attributes集合中设置一个键值对:key:CONFIGURATION_CLASS_ATTRIBUTE value:CONFIGURATION_CLASS_FULL
- CONFIGURATION_CLASS_ATTRIBUTE:configurationClass
- CONFIGURATION_CLASS_FULL:full
- 重点是表示当前类是一个全注解类
- ok,说到这里还记得上面说的每个BD都有有一个存放,如果你对spring定义的BD或者说是建模的BD不满意的时候,满足不了你想要的BD的样子,你是不是可以通过一个Map来存放一个键值对来满足你想要的BD的定制化的操作,在这里其实就是向当前的BD添加了spring对于BD建模不满足的地方。
- 下面的也是这样子的,但是细节也是有些区别:
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
- 别看就这么点代码,其实里面的内容还是很多的,首先我们和上面的那个if的判断还是一模一样的,还是判断当前的BD有没有实现@Configuration注解,如果它不为空的化,在判断一个方法isConfigurationCandidate(metadata),但是这个if的中间是一个 || 代表的是一个或的含义,简单来讲啥意思:就是说你第一个条件:如果当前的BD不满足实现了@Configuration注解的条件,那么你是不是满足isConfigurationCandidate(metadata)方法的条件,两者其中之一要是满足的话,你都可以走到这else if块中,然后把当前的BD的attributes的集合中put一个键值对:
- key:CONFIGURATION_CLASS_ATTRIBUTE()
public static final String CONFIGURATION_CLASS_ATTRIBUTE =
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
- value:CONFIGURATION_CLASS_LITE
public static final String CONFIGURATION_CLASS_LITE = "lite";
- 然后我们走进isConfigurationCandidate(metadata)方法里面去:
/**
* Check the given metadata for a configuration class candidate
* (or nested component class declared within a configuration/component class).
* @param metadata the metadata of the annotated class
* @return {@code true} if the given class is to be registered for
* configuration class processing; {@code false} otherwise
检查给定的元数据以查找配置类候选(或在配置/组件类中声明的嵌套组件类)。
@param metadata注释类的元数据@return
{@code true}如果给定的类要注册配置类处理;
{@code false}否则
*/
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
if (metadata.isInterface()) {
return false;
}
// 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);
}
- 我们发现这个方法其实很简单,但是它的作用可没这么简单哈。
- 首先第一步该方法先做过滤,判断当前BD如果是:不要考虑接口或注释...。这个条件的话就直接跳过。
- 然后找到任何典型的注释吗?,判断当前的BD中是否有"典型的注释",奇怪这个典型的注解到底是个啥注解???
- 来我们点进这个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());
}
- 它向candidateIndicators集合中实例化了四个注解,分别是:
- Component
- ComponentScan
- Import
- ImportResource
- 同时在它的最后一行代码中的hasBeanMethods(metadata);方法其实在判断当前的BD有没有加@Bean注解,所以总结来说还有一个@Bean
- 让我们回到上面的代码中,解释这段长篇大论到底讲了个啥子东东??
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);
}
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
- 我们可以看到在两个这小段的代码中,想要进入到第一个if块中的条件是当前的@Configuration注解不为空,并且满足后面的条件,但是后面的条件咱们先不看,总之如果当前的BD实现了@Configuration注解就会进入到第一个if块中,并且将当前的BD的attributes集合中设置一个键值对CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL,那么这个BD我们定义它为一个全注解的类。
- 如果它进入到了下面的这个if块中就说明它是一个半注解的BD,如何判断就是通过isConfigurationCandidate(matedata)方法来进行判断,判断的条件就是上面说的实现了那几个注解。然后也给当前的BD的attraibutes集合设置一个键值对CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE。
- ok,现在有一个问题要进行讨论,我问你对于spring来说到底什么类或者说什么BD算是一个配置类???
- 这个问题很重要,首先说结论昂:所有不管是什么BD只要加了@Configuration(当然这是废话),@Component,@ComponentScan,@Import,@ImportResource这几个注解的就是配置类!
- 来下面我们来证明这个说法是不是对的。
我们先准备几个类:
@Component
public class A {
public A(){
System.out.println("A init...");
}
}
@ComponentScan("com.lukp.postProcessorBeanDefinitionRead")
public class Config {
}
@Component("com.lukp.postProcessorBeanDefinitionRead")
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Config.class);
context.refresh();
}
}
当我们运行启动类的时候就会发现在控制台出现了 A init...
我们再来证明的清楚一点为什么说符合上面的条件的BD就是一个配置类。
我们改造一下当前的代码:
public class C{
public C(){
System.out.println("c init ...");
}
}
@Component
public class A {
public A(){
System.out.println("A init...");
}
// 在这里我们通过了@Bean注解来把C类注入到了springContext中
@Bean
public C c(){
return new C();
}
}
启动类不变。
来看一下结果:
看见没C类是不是被实例化了,被注入到了springContext中。
ok,我们再换种思路来说这个事情:
public class A {
public A(){
System.out.println("A init...");
}
@Bean
public C c(){
return new C();
}
}
说明当前情况下,spring是不会将这个A类扫描出来并且注入到SpringContext中的。
但是我再配置类Config中添加一个注解@Import注解将A类注入进来,阁下又当如何应对?
ok我们先来看看在A类上去掉了@Compenont注解后是怎么样的:
ok,啥也没输出,说明A类在去掉了@Compenont注解后没有被springContext扫描进去。
然后我在Config这个类上加上@Import(A.class)注解:
@ComponentScan("com.lukp.postProcessorBeanDefinitionRead")
@Import(A.class)
public class Config {
}
再运行来看结果:
是不是被扫描进来了!
但是我这里想说的不是这些注解的熟练使用(笑死了),我想说的是,我们在上面讲一个类只要实现了@Component、@ComponentScan、@Import、@ImportResource、@Bean这五个注解,该类,或者说该BD就是一个配置类,因为它代替了其他的类做了想要对SpringContext想要做的一些事情,上述的一个案例不是说了,我的C是完全没有实现spring提供的注解和接口来注入到springContext中,但是却因为在A类中通过@Bean注解被注入进来了!就是这个原因。
然后我们来稍稍的证明一下如果当前的类不实现我们上面说的那些注解它就不是配置类。
/**
* Build and validate a configuration model based on the registry of 基于 注册表构建和验证配置模型
* {@link Configuration} classes.
*/
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// 1.存储配置类的集合
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 2.获取所有的BD的Name:正常情况如果你不提供自定义的BD就五个spring内置的
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
/**
* 在这里你获取到BD之后是可以执行你自己想要执行的自定义操作的,如何实现,每个BD都实现了一个接口AttributeAccessor,这个接口有一个方法getAttribute,这个方法的在一个叫做AttributeAccessorSupport类中的实现:
* public Object getAttribute(String name) {
* Assert.notNull(name, "Name must not be null");
* return this.attributes.get(name);
* }
* 其中的this.attributes是一个Map,所以spring是允许你自己在针对于spring给你定义的BD不满足的时候,还可以向这个Map中丢你想要定义的一些规则(其实spring中对于Bean的执行顺序就是这么实现的)
*/
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
// log:Bean 定义已作为配置类进行处理
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
// 如果未找到@Configuration类,请立即返回
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
// 按先前确定的@Order值排序(如果适用)
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
// 检测通过封闭应用程序上下文提供的任何自定义 Bean 名称生成策略
// 看一下当前的spring环境中有没有生成默认的BeanNameGenerator
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
// localBeanNameGeneratorSet默认为false,那么表示在spring第一次实例化的时候,为true
// spring会从单例池中获取到对应的beanNameGenerator
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
// 并且如果beanNameGenerator不为null的话就会将它赋值给componentScanBeanNameGenerator和importBeanNameGenerator
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// Parse each @Configuration class
// 分析每个@Configuration类
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();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
// 读取模型并根据其内容创建 Bean 定义
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
// 将 ImportRegistry 注册为 Bean 以支持 ImportAware @Configuration类
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();
}
}
就是在processConfigBeanDefinitions方法的第33~35行,看没看到如果未找到@Configuration类,请立即返回,也就是说,这个方法下面的逻辑都是在对配置类进行解析,如果当前的BD不是配置类就注解返回了,说明只是一个普通的Bean。
你看这里开始解析每一个@Configuration注解的类。
下面我们说说,在spring什么样的配置类被定义为全配置类,什么样的配置类被定义为半配置类。
首先我们看这样子的案例:
public class E {
public E(){
System.out.println("E init...");
}
}
public class C{
public C(){
System.out.println("C init...");
}
}
定义上面的两个类,这两个类只有全参构造器,其他啥也没有。
然后我定义一个A类,通过@Bean的方式将该上面的两个类都进行实例化,这个时候肯定会在控制台输出两句话,也就是在他们的全参构造器中的话,为什么很简单默认的Bean是不是都是单例的。
但是我要是这样子去做的话:
@ComponentScan("com.lukp.postProcessorBeanDefinitionRead")
public class Config {
@Bean
public E e() {
return new E();
}
@Bean
public C c() {
//我在这里是不是调用了一个e()方法实例化了一个E对象出来
e();
return new C();
}
}
最后输出的结果想到不用想,肯定是多e的初始化进行了两次:
但是我这个时候就要突出,为什么说,加了@Configuration的类是全配置类:
只有一次!为什么呢?
这个@Configuration注解有何神奇之处?或者说针对于这样子重复加载的场景spring是如何保证当前只有对应的Bean对象它只会被加载一次是一个单例Bean呢?
其实是使用了CGLib来对当前的对象进行了代理工作,保证在同一次的加载中,只会被加载一次!
证明:
public class Sub extends Config{
BeanFactory beanFactory;
@Override
public E e() {
E bean = beanFactory.getBean(E.class);
if (bean == null) {
super.e();
}
return bean;
}
@Override
public C c() {
C bean = beanFactory.getBean(C.class);
if (bean == null) {
super.c();
}
return bean;
}
}
- 我们来说一说上面的逻辑。
- 首先第一步我们注入了一个beanFactory,然后当我们调用e()或者c()方法的时候就会先去beanFactory中找。
- 判断是不是为null
- 如果为null,就会调用父类的方法来实例化
- 比如说现在是e()方法被调用了,它会去调用了父类中的c()方法:
- ok,这个时候在spring的beanFactory中有了e这个BeanName的Bean。
- 然后我们在调用,sub中的c()方法,同样的道理也会先进行判断,在beanFactory中有还是没有,这个时候是不是没有,它会调用父类的方法创建C类。
- 但是我们会发现,还是那个问题,它会在调用c()方法的时候调用一下e()方法,会导致重复加载的问题,但是!
- 现在不同了,你仔细想想看,因为我们是从子类sub调用c()方法,然后再父类Config中调用的e()方法。
- 但是由于是子类调用父类的方法,方法中还有方法,但是这个方法在子类的本身实现了,它会调那个类的方法?
- 毋庸置疑,子类的,因为这一串调用是从子类开始的。
- 这个时候会到sub中的c()方法来创建。
- 但是它会判断在beanFactory中有还是没有,很显然刚刚已经创建过了,所以就直接返回bean了,也就是直接返回c这个bean了。
下面来从现象来证明我说的对不对:
我们把代码恢复到之前的样子:
可以看的很清楚,当前的类是一个代理类,并且再最后一个小红框中有一个beanFactory。
再来证明一下,我们把@Configuration注解去掉,那么当前类就不是CGLib代理出来的子类了:
它的类型就是一个Config。
所以现在什么叫做全配置类,什么叫做半配置类?
全配置类可以保证bean的单例性!但是半配置类可能会打破bean的单例性!
然后我们还可以证明,如果一个类的上面添加@Configuration注解之后,它会被标记为一个proxy,进行CGLib代理。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
/**
* Explicitly specify the name of the Spring bean definition associated with the
* {@code @Configuration} class. If left unspecified (the common case), a bean
* name will be automatically generated.
* <p>The custom name applies only if the {@code @Configuration} class is picked
* up via component scanning or supplied directly to an
* {@link AnnotationConfigApplicationContext}. If the {@code @Configuration} class
* is registered as a traditional XML bean definition, the name/id of the bean
* element will take precedence.
* @return the explicit component name, if any (or empty String otherwise)
* @see AnnotationBeanNameGenerator
*/
@AliasFor(annotation = Component.class)
String value() default "";
/**
* Specify whether {@code @Bean} methods should get proxied in order to enforce
* bean lifecycle behavior, e.g. to return shared singleton bean instances even
* in case of direct {@code @Bean} method calls in user code. This feature
* requires method interception, implemented through a runtime-generated CGLIB
* subclass which comes with limitations such as the configuration class and
* its methods not being allowed to declare {@code final}.
* <p>The default is {@code true}, allowing for 'inter-bean references' via direct
* method calls within the configuration class as well as for external calls to
* this configuration's {@code @Bean} methods, e.g. from another configuration class.
* If this is not needed since each of this particular configuration's {@code @Bean}
* methods is self-contained and designed as a plain factory method for container use,
* switch this flag to {@code false} in order to avoid CGLIB subclass processing.
* <p>Turning off bean method interception effectively processes {@code @Bean}
* methods individually like when declared on non-{@code @Configuration} classes,
* a.k.a. "@Bean Lite Mode" (see {@link Bean @Bean's javadoc}). It is therefore
* behaviorally equivalent to removing the {@code @Configuration} stereotype.
* @since 5.2
*/
boolean proxyBeanMethods() default true;
}
你看@Configuration注解中有一个参数是proxyBeanMethods参数,并且默认值为truel;
然后咱们再看在Bean被标记为一个全配置类或者是半配置类的时候,它是怎么判断的:
这段代码是不是很熟悉呀,它就是spring在标记当前的bena到底为全配置类或者是半配置类的逻辑,你看,它是不是在全配置类的逻辑前判断当前的config中的proxyBeanMethods字段是不是为true?下面的逻辑就没有判断!因为所有的Bean都会先经过第一个if嘛。
ok,由于我们知道ConfigurationClassPostProcessor类是实现了BeanDefinitionRegistryPostProcessor接口,那么就会默认有两个方法:
- postProcessBeanDefinitionRegistry(子类)
- postProcessBeanFactory(父类)
上面我们聊的其实都是postProcessBeanDefinitionRegistry
/**
* Derive further bean definitions from the configuration classes in the registry.
* 从注册表中的配置类派生更多 Bean 定义。
*/
@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);
}
但是它总有一天会执行下面的postProcessBeanFactory方法
/**
* Prepare the Configuration classes for servicing bean requests at runtime
* by replacing them with CGLIB-enhanced subclasses.
* 准备配置类,以便在运行时为 Bean 请求提供服务,方法是将它们替换为 CGLIB 增强的子类。
*/
@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...
// BeanDefinitionRegistryPostProcessor钩子显然不受支持...只需在这一点上懒惰地调用 processConfigurationClasses。
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
在postProcessBeanDefinitionRegistry中完成扫描并且完成对应配置类信息的加载和定义,然后在postProcessBeanFactory中的enhanceConfigurationClasses(beanFactory);方法中对全配置类进行CGLibg代理。
我们接下来讲的详细点:
- 首先还是从postProcessorBeanFactory方法调过来,并且执行enhanceConfigurationClasses(beanFactory);方法将当前的beanFactory放进去。
- 然后到enhanceConfigurationClasses方法内部:
/**
* Post-processes a BeanFactory in search of Configuration class BeanDefinitions;
* any candidates are then enhanced by a {@link ConfigurationClassEnhancer}.
* Candidate status is determined by BeanDefinition attribute metadata.
* @see ConfigurationClassEnhancer
* 对 BeanFactory 进行后处理以查找配置类的BeanDefinitions;
* 然后,任何候选者都会通过 {@link ConfigurationClassEnhancer} 进行增强。
* 候选状态由 BeanDefinition 属性元数据确定。
* @see 配置类增强器
*/
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
// 完成扫描之后的所有BeanDefinition
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
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);
}
}
}
}
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;
}
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()));
}
beanDef.setBeanClass(enhancedClass);
}
}
}
- 重点我们先看1~10行spring原生对于这行的解释:对 BeanFactory 进行后处理以查找配置类的BeanDefinitions,然后,任何候选者都会通过 {@link ConfigurationClassEnhancer} 进行增强。
- 相信作者已经表达的很清楚了吧,这个方法就是来判断到底那些是全配置类,然后对全配置做CGLib代理增强的,并且也指明了代理动作在ConfigurationClassEnhancer类中。
下面是代码解析,注释我都写在上面了,我就解释了:
/**
* Post-processes a BeanFactory in search of Configuration class BeanDefinitions;
* any candidates are then enhanced by a {@link ConfigurationClassEnhancer}.
* Candidate status is determined by BeanDefinition attribute metadata.
* @see ConfigurationClassEnhancer
* 对 BeanFactory 进行后处理以查找配置类的BeanDefinitions;
* 然后,任何候选者都会通过 {@link ConfigurationClassEnhancer} 进行增强。
* 候选状态由 BeanDefinition 属性元数据确定。
* @see ConfigurationClassEnhancer
*/
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
// 完成扫描之后的所有BeanDefinition
for (String beanName : beanFactory.getBeanDefinitionNames()) { //开始依次遍历beanFactory中的BeanName
// 通过BeanName来获取到BeanDefinition
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
/**
* 这里就是重点了!它在这里获取当前的BeanDefinition中的自定义操作的集合的ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE字段对应的值:
* public static final String CONFIGURATION_CLASS_ATTRIBUTE =
* Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
* 这段代码熟悉不,就是我们在postProcessorBeanDefinitionRegistry方法中对配置类进行判断,到底是全配置类,还是半配置类,
* 两种类型的key都是CONFIGURATION_CLASS_ATTRIBUTE
*/
/*=======================================================这段代码先不看===============================================================*/
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);
}
}
}
}
/*=======================================================这段代码先不看===============================================================*/
/**
* 然后这里就是判断当前的配置类的attributes集合中的CONFIGURATION_CLASS_ATTRIBUTE字段对应的值是不是CONFIGURATION_CLASS_FULL也就是full,也就是全配置类!
*/
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'.");
}
// 上面的代码的逻辑是防止,类实例创建BD的时候,由于创建实例的时候相互依赖,但是被依赖方还没有创建完成,可以会导致程序报错
// ok,下面就是将当前的BeanName和BeanDefinition放入到configBeanDefs集合中
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
if (configBeanDefs.isEmpty()) {
// nothing to enhance -> return immediately
return;
}
/**
* 然后到这里获取到上面的configBeanDefs集合
* 1.先创建一个ConfigurationClassEnhancer对象到时候来创建代理对象(子类)完成代理
* 2.最后完成全配置类的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()));
}
beanDef.setBeanClass(enhancedClass);
}
}
}
下面我来讲讲spring对于配置类的解析过程吧,上面说的都是spring对于配置类的标记。
/**
* 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));
}
}
// 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) {
// localBeanNameGeneratorSet默认为false,那么表示在spring第一次实例化的时候,为true
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();
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();
}
}
上述代码是方法processConfigBeanDefinitions(BeanDefinitionRegistry registry)方法,我们先说该方法的主要作用是对配置类的标记(存入到一个Set集合中)和对配置类的解析通过parse()方法。
下面我们来一点点的说processConfigBeanDefinitions方法是如何操作的:
- 首先可以看到在上述的代码的第9~19行代码在循环遍历candidateNames集合,其实从上述的代码中我们可以读懂candidateNames是当前spring容器中的所有的BeanDefinition,然后通过遍历的方式将candidateNames集合中的元素进行一一取出,并且将当前的beanName对应的BeanDefinition从BeanFactory中取出来。
- 取出来当前的BeanName对应的BeaDefinition之后,在11~18行开始判断当前的beanDefinition是否是配置类。
- 首先第一个if:如果当前的BeanDefinition已经被标识了,spring不对其进行任何的操作,输出一段log"Bean定义已经被处理为配置类:beanDefinition",说当前的BeanDefiniton已经被标记为配置类了。
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中:判断当前的beanDefinitionspring需不需要关心它,判断它是不是一个配置类?
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
-
- 怎么判断其实是在当前的if代码中的checkConfigurationClassCandidate方法中对配置类有定义信息:
- 该方法的具体解析咱们就不看了因为在上面的实话其实已经解析过了,就是该方法偏下面的那些,那我们现在在说的是该方法上面的部分,也就是将一个BeanDefinition标记为一个全配置类还是半配置做的一个前置过滤逻辑。
- 稍微说一下我刚刚说的标记一个BeanDefinition为全/半配置类的逻辑在44~56行。
- 那么我们当前讨论的前置过滤条件在25~29行。
- 上面的东西我们先不看,先看我说的25~29行的代码逻辑,就是在说,如果你当前传递进来的BeanDefinition为:
-
-
- BeanFactoryPostProcessor
- BeanPostProcessor
- AopInfrastructureBean
- EventListenerFactory
-
-
- 那么我就不玩了直接退出,至于为什么退出,下面会说的。
/**
* Check whether the given bean definition is a candidate for a configuration class
* (or a nested component class declared within a configuration/component class,
* to be auto-registered as well), and mark it accordingly.
* 检查给定的 Bean 定义是否是配置类(或在配置组件类中声明的嵌套组件类,也要自动注册)的候选者,并相应地对其进行标记。
* @param beanDef the bean definition to check
* @param metadataReaderFactory the current factory in use by the caller
* @return whether the candidate qualifies as (any kind of) configuration class
*/
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();
// 判断如果是下面判断的这些Bean就直接return
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);
}
else if (config != null || isConfigurationCandidate(metadata)) {
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;
}
- 现在我们回过头来讲,这段else if
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
// 加入到配置类集合中
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
- 如果你满足了,当前我else if中checkConfigurationClassCandidate()方法的条件的话就会被add到configCandidates,也就是加入到配置类的集合中。
- 然后再进行排序、通过parse()方法进行解析等等...
- 下面的方法咱们先不看,在这里要讨论的是,上述:
-
- BeanFactoryPostProcessor
- BeanPostProcessor
- AopInfrastructureBean
- EventListenerFactory
- 这些类为什么会被过滤掉呢?
- 这些类看起来很眼熟,是的没错,这些类都是spring的内置bean,也就是说spring启动的时候最先会加载的一批Bean。那么问题来为什么这些类不会被当成配置类呢?
- 两个原因:
-
- 一:就是这些spring内置的配置类,是会在BeanDefinitionMap中,但是他们可能不会满足spring对于一个配置类的要求,我们还记得吗?不管你是全配置类还半配置类,你都得要实现以下的注解才算一个spring的配置类:
-
-
- @Component
- @ComponentScan
- @Import
- @ImportResource
- @Bean
- 还有一个就是全配置类的注解@Configuration
- 当然这里并不是这个原因昂!注意下。
-
-
- 二:spring自己把这些spring内置的bean给过滤了!嗯?啥?spring自己把这些spring自己内置的bean给过滤了!啥情况,笑死了。ok下面开始解释spring为什么要这么做
-
-
- 其实对于spring自己内置bean的过滤还是在那个checkConfigurationClassCandidate方法中做的。
- 还记得我上面对于这个方法的整体分析吗,我说这个方法的前部分是在做过滤,下面才是真正的对一个BeanDefintion做一个配置类的标记的过程。
- 我们来看代码:
-
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();
// 判断如果是下面判断的这些Bean就直接return
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;
}
}
-
-
- 其他的代码我们都不看,就看16~20行的代码,看到没,checkConfigurationClassCandidate方法的进入到真正给beanDefintion标记为配置类之前的时候,直接在这段高亮的代码中把spring内置的这些Bean给排除出去了。
- 分别是这些是这些大怨种:
-
-
-
-
- BeanFactoryPostProcessor
- BeanPostProcessor
- AopInfrastructureBean
- EventListenerFactory
-
-
-
-
- 注意昂,这里的过滤是通过类型过滤,不过这话好像是个废话,不过呢,这里spring把它过滤出去了,不代表spring以后也不要这些内置的bean了!
- 下面我来通过现象来证明我说的对不对:
- 首先有一个类W,该类实现了BeanFactoryPostProcessor
-
public class W implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("w handler same ....");
}
@Bean
public Y y() {
return new Y();
}
}
-
-
- 还有一个Y类,它啥注解也没加
-
public class Y {
public Y() {
System.out.println("y inti ...");
}
}
-
-
- 还有一个Config类,作为一个指定扫描范围的配置类
-
@ComponentScan("com.lukp.postProcessBeanDefinitionRegistry")
public class Config {
}
-
-
- 最后是一个启动类,指定取扫描的是那个配置类
-
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(com.lukp.postProcessBeanDefinitionRegistry.Config.class);
}
}
-
-
- 按道理来说,此时控制台是啥也不会输出,首先W类上就啥也加,类似于@Component注解。
-
-
-
- 你看啥也没输出
- 但是我在W类上加一个@Component注解之后,那W就是一个配置类,并且在W中的Y类也会被扫描并且实例化,咱瞧瞧:
-
@Component
public class W implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("w handler same ....");
}
@Bean
public Y y() {
return new Y();
}
}
-
-
- 看结果:
-
-
-
- 好的结果就是W的beanFactoryPostPorcessor接口方法postProcessBeanFactory方法中的内容被输出了,并且Y构造器中的内容也被输出了。
- 咱们回到原来的话题,也就是为什么spring要在第一次标记配置类的时候,要先把spring内置的这些配置类过滤掉呢?
- 因为spring提供的内置的配置类,根本就不是配置类,所以第一次要过滤掉,但是为什么后面又要将这些spring内置的配置类?把他们扫描到配置类的集合中呢?
- 原因是spring的作者想到,为什么有些剑走偏锋的小可爱,不按照套路来,在这些spring提供的内置bean实例化完成后,做一些别的动作,那spring又要保证自己在初始化spring容器的同时,又要保证,后续如果有人要做骚操作的话,我也可以将这些动作包含到我的spring中。
- 我们回忆回忆在原先spring容器初始化的时候是注入了那些内置的bean:
- 我们点开AnnotationConfigApplicationContext这个类,点到这个类的构造器:
-
/**
* Create a new AnnotationConfigApplicationContext, deriving bean definitions
* from the given component classes and automatically refreshing the context.
* 创建一个新的 AnnotationConfigApplicationContext,从给定的组件类派生 Bean 定义并自动刷新上下文。
* @param componentClasses one or more component classes — for example,
* {@link Configuration @Configuration} classes
*/
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
/**
* Create a new AnnotationConfigApplicationContext that needs to be populated
* 创建一个需要填充的新 AnnotationConfigApplicationContext
* through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
* 通过 {@link注册} 调用,然后手动 {@linkplain刷新刷新}。
*/
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
-
-
- 我们可以看到在该构造器中有两个被实例化的对象,这次我们聊的是上面那个AnnotatedBeanDefinitionReader(),咱们点进这个类中。
-
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}
-
-
- 再点击this
-
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
//注册注解配置的处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
-
-
- 点进registerAnnotationConfigProcessors(this.registry)中
-
/**
* Register all relevant annotation post processors in the given registry.
* 在给定注册表中注册所有相关的注释后处理器。
* @param registry the registry to operate on
*/
public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
registerAnnotationConfigProcessors(registry, null);
}
-
-
- 点进registerAnnotationConfigProcessors(registry, null);方法中
-
/**
* Register all relevant annotation post processors in the given registry.
* 在给定注册表中注册所有相关的注释后处理器。
* @param registry the registry to operate on
* @param source the configuration source element (already extracted)
* that this registration was triggered from. May be {@code null}.
* @return a Set of BeanDefinitionHolders, containing all bean definitions
* that have actually been registered by this call
*/
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
BeanDefinitionHolder beanDefinitionHolder = registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME);
beanDefs.add(beanDefinitionHolder);
}
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition();
try {
def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader()));
} catch (ClassNotFoundException ex) {
throw new IllegalStateException("Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
}
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}
return beanDefs;
}
-
-
- 可以看到高亮的代码都是spring内置的bean,在spring容器一开始创建的时候就会被创建添加到BeanDefinitionMap中,有以下几个:
-
-
-
-
- ConfigurationClassPostProcessor
- AutowiredAnnotationBeanPostProcessor
- CommonAnnotationBeanPostProcessor
- EventListenerMethodProcessor
- DefaultEventListenerFactory
-
-
-
-
- 我们挑第一个ConfigurationClassPostProcessor来看,进到该类的内部
-
public class ConfigurationClassPostProcessor implements
BeanDefinitionRegistryPostProcessor,
PriorityOrdered,
ResourceLoaderAware,
BeanClassLoaderAware,
EnvironmentAware {
-
-
- 咱们就光光看ConfigurationClassPostProcessor类的实现,发现它实现了咱们的老朋友BeanDefinitionRegistryPostProcessor,它是不是BeanFactoty接口的子类。
- 还记得嘛在之前咱们讲invokeBeanFactoryPostProcessors()方法的时候,将的它在处理 BeanFactoryPostProcessor接口 和 BeanDefinitionRegistryPostProcessor接口的区别,对于BeanFactoryPostProcessor接口的来说,它在进入到循环的时候是不是不会执行该接口下的回调方法,包括你自己实现该接口并且编写一定的代码逻辑,它是会把实现BeanFactoryPostProcessor接口的那些类放到一个集合中,并且在下面的进行统一处理,而且会在执行会当前扫描到的那些实现了BeanFactoryPostProcessor接口 和 BeanDefinitionRegistryPostProcessor接口之后,spring还考虑到了,在第一次刷新BeanFactory之后又有新的后置回调处理进行来,保证这些回调不会丢掉,所以在还在else if当中又执行了一遍。
- 好的回忆就先到这里,咱们接着说,spring对于自身内置的bean在第一次启动BeanFactory的时候不做处理,第一点,首先spring的这些内置bean不是配置类,并且考虑到有可能,外部人员会拿这些内置的bean去做一些回调扩展动作,它要保证不能丢掉,所以在第一次初始化BeanFactory的时候,要默认去除掉这5个内置bean。
- 下面我们来按照之前的逻辑理一遍,spring是在什么时候执行BeanFactoryPostProcessr的:
-
-
-
-
- 首先回到梦开始的地方:PostProcessorRegistrationDelegate类中的invokeBeanFactoryPostProcessors()方法中:
-
-
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
/*判断如果是BeanDefinitionRegistryPostProcessor*/
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
/*就直接强转为BeanDefinitionRegistryPostProcessor*/
BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
/*执行当前的PostProcessor*/
registryProcessor.postProcessBeanDefinitionRegistry(registry);
/**
* 执行完成之后将当前的registryProcessor放入到registryProcessors集合中
* 这里执行完成后为什么还要放到registryProcessors集合中呢?
* 1. 主要是父类的也就是BeanFactoryPostProcessor的回调方法还没有被执行呢!
* 2. 后续执行到了父类的回调方法,然后依次取出来执行父类中的回调方法。
*/
registryProcessors.add(registryProcessor);
}
else {
/**
* 否则不是BeanDefinitionRegistryPostProcessor接口类型那么就将他放入到regularPostProcessors集合中
* 1. 主要目的是为了实现执行顺序,
* 2. 虽然他们两个都是为了Bean的回调扩展服务的,但毕竟一个是父类一个是子类,
* 3. 假如有一天我想要先执行子类中的方法回调方法咋办?换而言之我又想要先执行父类中的方法咋办呢?
* 4. 总结一句话:为什么这么做其实是为了做到可以控制PostProcessor的执行顺序(执行时机)
*/
regularPostProcessors.add(postProcessor);
}
}
-
-
-
- 上面截取的代码是spring开始处理执行BeanFacotry和BeanDefintionResgitry方法,如果是子类的执行调用postProcessBeanDefinitionRegistry方法来执行回调动作,并且将当前执行的registryProcessor添加到registryProcessors集合中方便spring在下面对BeanFactory接口进行回调处理的时候进行统一回调处理。
- ok,其他的不多说我们直接进入到子类接口执行回调的方法中(postProcessBeanDefinitionRegistry)
-
-
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
//判断当前beanDef是否是配置类
if (logger.isDebugEnabled()) {
// log:Bean 定义已作为配置类进行处理
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));
}
}
-
-
-
- 当前就是第一次在对配置类进行标记,排除spring内置的那5个bean,标记完成后加入到配置类集合中,方便后面进行统一的回调处理。
- 具体checkConfigurationClassCandidate()方法咱就不细说了,上面已经说过好多次了。
- 好的,下面我们进入到解析配置类的流程中,看代码:
-
-
// Parse each @Configuration class
// 分析每个@Configuration类
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();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
// 读取模型并根据其内容创建 Bean 定义
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());
-
-
-
- 看到上述代码中高光的代码创建了一个ConfigurationClassParser,配置类解析器。
- 将上述的configCandidates(配置类存放集合)copy到candidates集合中。
- 在第15行调用ConfigurationClassParser配置类解析器的parse()方法,来解析当前所有的配置类。
- 这里我们进入到parse()方法中:
-
-
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
// 这个if是来判断当前的BD是否为AnnotatedBeanDefinition,
// 是这样的由于我们spring默认的BD的类型是AbstractBeanDefinition,
// 但是这个默认的BD和我们平时通过注解来注入的BD是不一样的,至于为什么spring要提供这样的机制,
// 其实是为了防止有些程序员想要对spring进行扩展,没有地方扩展并且有五个spring内置的配置类,外部人员你是不能破环它的,
// 所以spring在这里提供了AnnotatedBeanDefinition来做为承载媒介
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());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
this.deferredImportSelectorHandler.process();
}
-
-
-
- 可以看到上述代码高光的地方,由于我们当前的BeanDefinition为AnnotatedBeanDefinition类型,就会进入到第一个parse方法中。
-
-
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
/**
* ConfigurationClass:它是作为来抽象一个配置类
* 1、站在spring的角度,当前在做的事情就是来解析一个配置类
* 2、开始解析--在解析的过程有个问题,站在spring的角度,对于一个配置类我如何来进行描述??
* 3、
*/
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
-
-
-
- 在该方法中spring创建了一个configurationClass类来对当前的配置类进行抽象,进行spring定义上的描述。
- 好的我接下来往下走,点进processConfigurationClass方法中:
-
-
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.
// 找到显式 Bean 定义,可能替换了导入。让我们删除旧的并使用新的。
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 {
//扫描当前类上是否带有@Component、@PropertySources、@ComponentScans注解
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
-
-
-
- 当前其他的代码逻辑都先不看,我们先看高光的代码,也就是doProcessConfigurationClass(configClass, sourceClass, filter)方法。
-
-
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass,
SourceClass sourceClass,
Predicate<String> filter) throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first 首先递归处理任何成员(嵌套)类
processMemberClasses(configClass, sourceClass, filter);
}
// Process any @PropertySource annotations 处理任何@PropertySource注释
Set<AnnotationAttributes> annotationAttributes = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(),
PropertySources.class,
org.springframework.context.annotation.PropertySource.class
);
for (AnnotationAttributes propertySource : annotationAttributes) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info(
"Ignoring @PropertySource annotation on ["
+
sourceClass.getMetadata().getClassName()
+
"]. Reason: Environment must implement ConfigurableEnvironment"
);
}
}
// Process any @ComponentScan annotations 处理任何@ComponentScan注释
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 配置类用 @ComponentScan -> 立即执行扫描进行注释
// 在这里就是将前面实例化好的beanNameGenerator获取到
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());
}
}
}
}
// Process any @Import annotations 处理任何@Import注释
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// Process any @ImportResource annotations 处理任何@ImportResource注释
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);
}
}
// Process individual @Bean methods 处理单个@Bean方法
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;
}
-
-
-
- 我们来看高光的地方,其实就是对当前的循环的componentScan进行处理,通过调用this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
-
-
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String declaringClass) {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(
this.registry,
componentScan.getBoolean("useDefaultFilters"),
this.environment,
this.resourceLoader
);
//如果不在@ComponentScan注解的nameGenerator上做自定义配置的话,则generatorClass为默认配置BeanNameGenerator
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
//那么默认情况下就为true
boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
/*
* 1.给这个scanner(扫描器)添加一个生成名字的策略
* 2.如果上面的useInheritedGenerator为true就代表用户没有指定BeanName的生成策略
* 3.如果为false的话则使用用户提供的BeanName生成策略
*/
scanner.setBeanNameGenerator(
useInheritedGenerator
?
this.beanNameGenerator//如果没有你自己的定制的BeanName的生成策略则默认使用BeanNameGenerator来生成BeanName
:
BeanUtils.instantiateClass(generatorClass)//如果有你自己配置的BeanName的生成策略那么就实例化
);
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));
}
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
// 调用doScan方法来进行扫描
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
-
-
-
- 该parse()方法注册一个ClassPathBeanDefinitionScanner,扫描器,看到没,下面就是对这个扫描器的一系列配置,全部都是些set()方法。
- 最后set完了就调用doScan方法来对扫描出来(第47~54行)的basePackages,进入到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) {
//扫描所有的BD(class文件-->>BeanDefinition)
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations(
(AnnotatedBeanDefinition) candidate
);
}
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;
}
-
-
-
- 其实这个方我们说过蛮多次了,就不多说,最后返回一个扫描之后的BeanDefinitions。
- 说了这么多,回到最开始的地方来看processConfigBeanDefinitions方法中第一次调用parse()方法,然后再进入到第一个parse()方法中,然后再进入到processConfigurationClass方法中,然后再进入到doProcessConfigurationClass方法中,然后再进入到parse方法中,然后再进入到doScan方法中,完成扫描,并且返回到doProcessConfigurationClass方法中继续下面的逻辑,来看图:
-
-
-
-
-
- 上面的流程图只是说了一个大概,但是也较为明确的讲出了我想要说的内容。
- 不过这里还有一点想要进行补充的是,在parse方法中不是有三个parse方法嘛:
-
-
-
-
-
- spring为何在这里要设置三个?一个是 AnntotaionBeanDefinition 一个是 AbstractBeanDefinition 还有匹配不上上面的两个类型的那些BD
- 我们来看看通过扫描进来的BD和spring内置在BeanFactory启动的时候内置的五个Bean的类型有什么区别
- 我们先来看内置的Bean,这五兄弟都不是直接new出来的,而是new出来之后还包了一层 RootBeanDefinition
-
-
-
-
-
- 这里其他几个就不看了
- 重要的RootBeanDefinition,你点进去之后就会发现它的无参构造器和有参构造器都会先执行一个方法:super(),干啥就是先去执行父类的方法,并且你看看RootBeanDefinition的父类是谁!AbstractBeanDefinition
-
-
-
-
-
- ok!到这里咱们再来回头看看上述讲的parser方法,联系起来!
-
-
public void parse(Set<BeanDefinitionHolder> configCandidates) {
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());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
this.deferredImportSelectorHandler.process();
}
-
-
-
- 所以当spring容器启动进入到processConfigBeanDefinitions方法中的时候,第一个进行检查checkConfigurationClassCandidate方法,当前类是否是配置类的时候,就会判断到当前的BD的类型为AbstractBeanDefinition。因为这会儿还没有processConfigBeanDefinitions方法第一次执行parser方法的时候呢!所以这里的BD类型只会是AbstractBeanDefinition,那么checkConfigurationClassCandidate自然就是会返回fasle就不会将当前的beanDef加入到configCandidates(配置类集合)
- 中间的排序、generatorBeanName咱就不看了,直接到parser.parse(candidates);方法,那么按照现在的思路当前被执行的在spring容器创建的时候就被注入到容器中那五兄弟,比如AutowiredAnnotationBeanPostProcessor,在被包装为RootBeanDefinition之后就被加入到了当前的beanDefs中。
-
-
-
-
-
- 这里稍微来说一下为什么,这五兄弟的注册时机是整个spring容器最早的。下面的流程图:
-
-
-
-
-
- 上面说了这五兄弟的执行时机,下面我们再回头看之前再processConfigBeanDefinitions()方法的逻辑,在第一次checkConfigurationClassCandidate的时候当前的springContext中只有这五兄弟
- 然后到下面去执行parser方法。
- 并且执行的是第二个if中的parser方法:
-
-
-
-
-
- 完成了spring内置的五个bean的初始化之后,再完成后续用户实现的BeanFactory的后置回调。
- 然后我们再注意一下,三个parser方法中其实都是重写parser方法罢了,但是,每个parser方法中都会processConfigurationClass(new ConfigurationClass(....))
-
-
-
-
-
- 看到我对每一个的Configurtation类都花了红框,来聊一下该类。
- 其实Configuration其实对于配置类的一个抽象,也可以说对一个配置类的处理,这个在上面也说过了。
- 对于一个配置类来说,我们描述一个情况,比如说我一个Config类,然后在这个Config类上我加一个了@Import注解,我在这个注解上加了一个B类,但是在这个B类中我注入了其他的一些Bean,比如C、D都可以,那么当spring扫描到Config类的时候,spring会将@Import中的类的下的所有的类都注入进来。
- 那么对于一个配置类的描述咱们就可以抽象成为一个配置类的描述:
-
-
-
-
-
- 站在代码的角度上来说,我们上面所说的parser,包括到该方法内部一直调用到扫描解析指定的包basePages的那步,所有的一切都是spring使用parser方法对spring中的所有要被注入的bean进行扫描。
- 下面的this.reader.loadBeanDefinitions(configClasses);方法才是真正开始对扫描出来被描述为Configuration类进行加载。
-
-
-
-
-
- 是不是在循环的处理在上面的parser方法解析出来的Configuration。
- 然后有个问题为什么mybatis在spring加载的初期就会和spring内置的那几个Bean开始一起加载?
-
-