流程图:
首先,Spring会去我们指定包路径下扫描出符合条件的类,而且,这里比较关键的是,Spring默认会在组件ClassPathBeanDefinitionScanner中,为注解@Component注册相应的注解过滤器,这样方便Spring筛选出那些标注了注解@Component的类,而且,我们在源码中也看到了Spring确实也是这样来过滤筛选的。
另外,我们发现添加了注解@Component的类最后也会封装为BeanDefinition,只不过BeanDefinition是注解类型的ScannedGenericBeanDefinition,ScannedGenericBeanDefinition不但继承了GenericBeanDefinition,同时也实现了接口AnnotatedBeanDefinition。
所以,我们看到Spring不仅为BeanDefinition设置了一些默认的属性信息,而且,Spring还进一步的解析类中的其他注解的信息,并且,将所有的注解信息都统一封装到了BeanDefinition中,然后将BeanDefinition注册到Spring容器里,核心思想和我们之前解析xml文件是类似的。
最后,我们又看了几个和注解@Component类似的注解,如注解@Controller、@Service和@Repository,最后发现它们底层也是通过注解@Component来实现功能的,本质和注解@Component是类似的,标注了这些注解的类同样也是会被Spring扫描到的。
* @see AnnotationConfigApplicationContext
* @see ConfigurationClassPostProcessor
* @see org.springframework.core.env.Environment
* @see org.springframework.test.context.ContextConfiguration
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
ConfigurationClassPostProcessor是接口BeanFactoryPostProcessor的实现类,也就是说ConfigurationClassPostProcessor本质上来说就是一个工厂级别的后处理器BeanFactoryPostProcessor。
实现了接口BeanDefinitionRegistryPostProcessor,Spring容器在初始化的refresh方法中,会先执行接口BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry方法,最后统一执行接口BeanFactoryPostProcessor中的postProcessBeanFactory方法。
因为ConfigurationClassPostProcessor还实现了接口PriorityOrdered和Ordered,所以在具体执行时,还会根据优先级进行排序。
Spring容器初始化,首先会执行ConfigurationClassPostProcessor中,接口BeanDefinitionRegistryPostProcessor中的方法postProcessBeanDefinitionRegistry,然后再执行BeanFactoryPostProcessor中的方法postProcessBeanFactory。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
//方法最开始先通过System的方法identityHashCode,获取容器registry对应的hash code,接下来再判断一下集合registriesPostProcessed以及集合factoriesPostProcessed中是否存在该hash cod
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);
}
方法processConfigBeanDefinitions中的逻辑还比较关键,添加了注解@Configuration的类的解析
首先通过方法getBeanDefinitionNames,从Spring容器registry中,获取已经注册到Spring容器的BeanDefinition的名称,然后在for循环中遍历这些BeanDefinition。
可以看到,在for循环的第一个if分支中,如果发现BeanDefinition中不存在属性“org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass”的值时,这就意味着注解配置类的BeanDefinition并没有被处理过。
第一次执行该方法时,默认是没有这个属性值的,所以接下来就会到else if分支中,通过ConfigurationClassUtils的方法checkConfigurationClassCandidate,看下当前的BeanDefinition是否满足候选条件,也就是判断是否属于添加了注解@Component的注解配置类。
/**
* Build and validate a configuration model based on the registry of
* {@link Configuration} classes.
*/
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//获取spring已经注册,符合条件的BeanDefinitionHolder
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
//获取已经注册到Spring容器的BeanDefinition的名称,
String[] candidateNames = registry.getBeanDefinitionNames();
//然后在for循环中遍历这些BeanDefinition。
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);
}
}
//看下当前的BeanDefinition是否满足候选条件,也就是判断是否属于添加了注解@Component的注解配置类。
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
如果BeanDefinition中的类名称都为null,或者当前BeanDefinition中配置了工厂方法。
方法checkConfigurationClassCandidate就是通过是否存在@Configuration注解,且该注解中的属性proxyBeanMethods的值是否为默认的true,以此来判断当前的BeanDefinition是符合条件的注解配置类。
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
//从注解元数据中,尝试获取注解@Configuration的信息,并将信息封装在config中。
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
//如果config不为空,且注解@Configuration中的属性proxyBeanMethods值为true,此时第一个if分支条件成立,此时在BeanDefinition中将属性“org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass”的值设置为CONFIGURATION_CLASS_FULL,也就是“full”
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;
}
//通过getOrder方法,获取注解元数据中注解@Order的属性值,并设置到BeanDefinition中。
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
继续processCOnfigBeanDefinitions
接下来集合configCandidates通过sort方法,对集合内的元素进行一个排序,排序的规则也比较简单,就是通过解析到的注解@Order中的属性值来排序,值越小优先级越大。
然后判断registry是否是SingletonBeanRegistry的实例,默认registry是SingletonBeanRegistry的实例,接下来会初始化一个组件BeanNameGenerator,主要负责生成bean的名称,这里默认获取到的generator为null。
// 集合configCandidates通过sort方法,对集合内的元素进行一个排序,排序的规则也比较简单,就是通过解析到的注解@Order中的属性值来排序,值越小优先级越大。
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// 判断registry是否是SingletonBeanRegistry的实例,默认registry是SingletonBeanRegistry的实例,接下来会初始化一个组件BeanNameGenerator,主要负责生成bean的名称,这里默认获取到的generator为null。
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
初始化了一个组件ConfigurationClassParser,根据名称我们可以知道,它应该就是解析注解配置类的一个解析器
// 初始化了一个组件ConfigurationClassParser,、解析注解配置类的一个解析器
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();
方法parser中的逻辑还是挺清晰的,可以看到在if分支中,分别对注解类型的AnnotatedBeanDefinition、普通类型的AbstractBeanDefinition都有相应的解析逻辑。
public void parse(Set<BeanDefinitionHolder> configCandidates) {
//方法parser中的逻辑还是挺清晰的,可以看到在if分支中,分别对注解类型的AnnotatedBeanDefinition、普通类型的AbstractBeanDefinition都有相应的解析逻辑。
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());
}
}
}
this.deferredImportSelectorHandler.process();
}
//在方法parse中,首先将注解元数据metadata以及bean的名称,都封装到了组件ConfigurationClass中,我们继续跟进看下:
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
、
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
执行方法doProcessConfigurationClass,对注解配置类信息configClass进行深度的解析,然后将解析完毕的configClass缓存到configurationClasses中。
可以看到,方法doProcessConfigurationClass中的逻辑,主要就是对注解配置类中的各种其他注解进行解析,毕竟添加了注解@Configuration的类当中,可能还会添加其他的注解。
在方法doProcessConfigurationClass中会解析注解@PropertySource、@ComponentScans、@ImportResource等,。
但是,我们可以发现最终会将解析到的注解信息,都设置到configClass中了,而且,我们在上个方法也看到了,解析得到的configClass最终会被添加到configurationClasses中。
@Nullable
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
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// No superclass -> processing is complete
return null;
}
接下来会获取解析器parser中configurationClasses中的元素,并放到集合configClasses中。
同时还初始化了一个组件ConfigurationClassBeanDefinitionReader来解析它们,可以看到,接下来调用的是reader中的方法loadBeanDefinitions。
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);
在方法loadBeanDefinitionsForConfigurationClass中,果然开始处理注解配置类中的每个方法,我们再到方法loadBeanDefinitionsForBeanMethod中看下:
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
//比那里注解配置类的每个方法
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
开始解析方法上的注解@Bean,然后为方法创建一个ConfigurationClassBeanDefinition类型的BeanDefinition。
接下来就是为BeanDefinition填充各种各样的属性信息,这些属性大部分我们应该都很熟悉了,最后通过registry将BeanDefinition注入到Spring容器中。
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
String methodName = metadata.getMethodName();
//获取方法上的Bean
AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
Assert.state(bean != null, "No @Bean annotation attributes");
//创建ConfigurationClassBeanDefinition
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata, beanName);
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}
接下来在if分支的逻辑中,如果发现当前Spring容器中注册的BeanDefinition数量,大于candidateNames中的数量,candidateNames其实就是当前方法刚执行时,Spring容器中注册BeanDefinition的数量。
现在,我们解析了注解配置类之后,又为每个添加了注解@Bean的方法封装了BeanDefinition,此时Spring容器中的BeanDefinition数量,肯定是大于之前容器中BeanDefinition的数量啊,所以,接下来我们看下if分支中的逻辑
可以看到,接下来会将已经解析完毕的configClasses,添加到集合alreadyParsedClasses中,然后在for循环中遍历集合newCandidateNames,也就是当前Spring容器中已经注册的那些BeanDefinition的名称。
目的也是很简单,就是想要看下注解@Bean对应的这些方法解析到的BeanDefinition,是否也存在添加了注解@Configuration的注解配置类,如果存在的话,也是需要添加到集合candidates中,准备开始下一轮while循环的解析的,保证不放过任何一个符合候选条件的注解配置类。
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;
}