文章目錄
-
- 一,ConfigurationClassPostProcessor簡介
- 二,ConfigurationClassPostProcessor注入時機
-
- 1,注解方式的注入
- 2,XML方式的注入
- 三,postProcessBeanDefinitionRegistry()方法
-
- 1,第一步,篩選出被@Configuration注解标注的BeanDefinition
- 2,第二步,解析被@Configuration注解标注的BeanDefinition
- 3,第三步,将掃描到的所有beanDefinition注冊到容器的BeanDefinitionMap中
- 4,第四步,判斷第三步中注入到BeanDefinitionMap中BeanDefinition是否已經被解析過,如果沒有被解析過,那麼需要繼續解析
- 四,postProcessBeanFactory()方法
- 五,總結
一,ConfigurationClassPostProcessor簡介
ConfigurationClassPostProcessor
是一個後置處理器的類,主要功能是參與BeanFactory的建造,主要功能如下:
- 解析加了@Configuration的配置類
- 解析@ComponentScan掃描的包
- 解析@ComponentScans掃描的包
- 解析@Import注解
ConfigurationClassPostProcessor
類圖:
ConfigurationClassPostProcessor
實作了
BeanDefinitionRegistryPostProcessor
接口,而
BeanDefinitionRegistryPostProcessor
接口繼承了
BeanFactoryPostProcessor
接口,是以
ConfigurationClassPostProcessor
中需要重寫
postProcessBeanDefinitionRegistry()
方法和
postProcessBeanFactory()
方法。
-
方法:定位、加載、解析、注冊相關注解。postProcessBeanDefinitionRegistry()
-
方法:添加CGLIB增強處理及postProcessBeanFactory()
後置處理類。ImportAwareBeanPostProcessor
二,ConfigurationClassPostProcessor注入時機
1,注解方式的注入
配置類
SpringConfiguration.java
@Configuration
@ComponentScan(basePackages="com.bobo.aop.annotation")
@EnableAspectJAutoProxy
public class SpringConfiguration { }
啟動類:
public class MyTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
}
}
Spring源碼會在建立
AnnotationConfigApplicationContext
容器的時候,會去建立
AnnotatedBeanDefinitionReader
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
在建立
AnnotatedBeanDefinitionReader
的過程中會去注入注解相關的後置處理器:
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}
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);
}
public abstract class AnnotationConfigUtils {
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
// 省略部分代碼....
// 建立BeanDefinitionHolder集合
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
// 注冊内部管理的用于處理@configuration注解的後置處理器的bean
// ConfigurationClassPostProcessor
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// 注冊内部管理的用于處理@Autowired,@Value,@Inject以及@Lookup注解的後置處理器bean
// AutowiredAnnotationBeanPostProcessor
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));
}
// 注冊内部管理的用于處理JSR-250注解,例如@Resource,@PostConstruct,@PreDestroy的後置處理器bean
// 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));
}
// 注冊内部管理的用于處理JPA注解的後置處理器bean
// 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));
}
// 注冊内部管理的用于處理@EventListener注解的後置處理器的bean
// EventListenerMethodProcessor
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));
}
// 注冊内部管理用于生産ApplicationListener對象的EventListenerFactory對象
// DefaultEventListenerFactory
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;
}
}
此處導入了五個後置處理器:
-
:beanName為ConfigurationClassPostProcessor
用于處理@configuration注解的後置處理器的beaninternalConfigurationAnnotationProcessor
-
:beanName為AutowiredAnnotationBeanPostProcessor
用于處理@Autowired,@Value,@Inject以及@Lookup注解的後置處理器beaninternalAutowiredAnnotationProcessor
-
:beanName為CommonAnnotationBeanPostProcessor
用于處理JSR-250注解,例如@Resource,@PostConstruct,@PreDestroy的後置處理器beaninternalCommonAnnotationProcessor
-
:beanName為EventListenerMethodProcessor
用于處理@EventListener注解的後置處理器的beaninternalEventListenerProcessor
-
:beanName為DefaultEventListenerFactory
管理用于生産ApplicationListener對象的EventListenerFactory對象internalEventListenerFactory
2,XML方式的注入
boboTest.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.bobo.aop.annotation"/>
</beans>
測試類:
public class Test {
public static void main(String[] args) {
MyClassPathXmlApplicationContext ac = new MyClassPathXmlApplicationContext("boboTest.xml");
}
}
源碼中document樹在解析xml檔案的時候,解析到context标簽屬于自定義标簽,是以會走自定義标簽的解析
public class BeanDefinitionParserDelegate {
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 擷取對應的命名空間
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 根據命名空間找到對應的NamespaceHandlerspring
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// 調用自定義的NamespaceHandler進行解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
}
這裡會擷取到
ComponentScanBeanDefinitionParser
解析器,然後會走到regsiterComponents()方法裡面:
public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
protected void registerComponents(
XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {
// 省略部分代碼....
// Register annotation config processors, if necessary.
boolean annotationConfig = true;
if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
// 擷取component-scan标簽的annotation-config屬性值,預設為true
annotationConfig = Boolean.parseBoolean(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
}
if (annotationConfig) {
// 如果annotation-config屬性值為true,在給定的系統資料庫中注冊所有用于注解的bean後置處理器
Set<BeanDefinitionHolder> processorDefinitions =
AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
}
// 省略部分代碼...
}
}
可以看到擷取到
component-scan
标簽,預設會調用
AnnotationConfigUtils.registerAnnotationConfigProcessors()
方法,進行對注解相關的後置處理器的注冊,此方法上面已經分析過了!
三,postProcessBeanDefinitionRegistry()方法
很顯然
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()
方法的執行是在
invokeBeanFactoryPostProcessors()
方法中執行的,如果不了解
invokeBeanFactoryPostProcessors()
方法的執行流程的話,請移步去看上一篇文章:吃透Spring源碼(十五):invokeBeanFactoryPostProcessors 執行流程
此方法主要完成對
@Configuration
注解标注的
BeanDefinition
解析,同時解析出 @ComponentScan 和 @ComponentScans 掃描出的Bean,也會解析出加了 @Bean 注解的方法所注冊的Bean,以及通過 @Import 注解注冊的Bean和 @ImportResource 注解導入的配置檔案中配置的Bean。
下面我們直接到
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()
方法中:
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
/**
* 定位、加載、解析、注冊相關注解
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 根據對應的registry對象生成hashcode值,此對象隻會操作一次,如果之前處理過則抛出異常
int registryId = System.identityHashCode(registry);
// 将馬上要進行處理的registry對象的id值放到已經處理的集合對象中
this.registriesPostProcessed.add(registryId);
// 處理配置類的bean定義資訊
processConfigBeanDefinitions(registry);
}
}
處理配置類的bean定義資訊方法:
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
/**
* 建構和驗證一個類是否被@Configuration修飾,并做相關的解析工作
* 如果你對此方法了解清楚了,那麼springboot的自動裝配原理就清楚了
*/
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// 建立存放BeanDefinitionHolder的對象集合
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 目前registry就是DefaultListableBeanFactory,擷取所有已經注冊的BeanDefinition的beanName
String[] candidateNames = registry.getBeanDefinitionNames();
//----------------第一步-----------------
// 周遊所有要處理的beanDefinition的名稱,篩選對應的被注解修飾的beanDefinition
for (String beanName : candidateNames) {
// 擷取指定名稱的BeanDefinition對象
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 判斷目前BeanDefinition是否是一個配置類,并為BeanDefinition設定屬性為lite或者full,此處設定屬性值是為了後續進行調用
// 如果Configuration配置proxyBeanMethods代理為true則為full
// 如果加了@Bean、@Component、@ComponentScan、@Import、@ImportResource注解,則設定為lite
// 如果配置類上被@Order注解标注,則設定BeanDefinition的order屬性值
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
// 添加到對應的集合對象中
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
//----------------第二步-----------------
// 存放相關的BeanDefinitionHolder對象
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
// 存放掃描包下的所有bean
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
// 解析帶有@Controller、@Import、@ImportResource、@ComponentScan、@ComponentScans、@Bean的BeanDefinition
parser.parse(candidates);
// 将解析完的Configuration配置類進行校驗,1、配置類不能是final,2、@Bean修飾的方法必須可以重寫以支援CGLIB
parser.validate();
// 擷取所有的bean,包括掃描的bean對象,@Import導入的bean對象
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
// 清除掉已經解析處理過的配置類
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
// 判斷讀取器是否為空,如果為空的話,就建立完全填充好的ConfigurationClass執行個體的讀取器
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
//----------------第三步-----------------
// 核心方法,将完全填充好的ConfigurationClass執行個體轉化為BeanDefinition注冊入IOC容器
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());
}
}
1,第一步,篩選出被@Configuration注解标注的BeanDefinition
周遊容器中的
BeanDefinitionNames
集合,找出來被
@Configuration
标注的
BeanDefinition
并加入到代處理集合
configCandidates
中。
主要由
ConfigurationClassUtils.checkConfigurationClassCandidate()
方法來完成:
abstract class ConfigurationClassUtils {
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
// 擷取bean定義資訊中的class類名
String className = beanDef.getBeanClassName();
AnnotationMetadata metadata;
// 通過注解注入的db都是AnnotatedGenericBeanDefinition,實作了AnnotatedBeanDefinition
// spring内部的bd是RootBeanDefinition,實作了AbstractBeanDefinition
// 此處主要用于判斷是否歸屬于AnnotatedBeanDefinition
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
// Can reuse the pre-parsed metadata from the given BeanDefinition...
// 從目前bean的定義資訊中擷取中繼資料資訊
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
// 判斷是否是spring中預設的BeanDefinition
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
// 如果class執行個體是下面四種類或接口的子類、父接口等任何一種情況,直接傳回
if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
BeanPostProcessor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
EventListenerFactory.class.isAssignableFrom(beanClass)) {
return false;
}
// 為給定類建立新的AnnotationMetadata執行個體
metadata = AnnotationMetadata.introspect(beanClass);
}
// 如果上述兩種情況都不符合
else {
try {
// 擷取className的MetadataReader執行個體
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
// 讀取底層類的完整注釋中繼資料,包括帶注解方法的中繼資料
metadata = metadataReader.getAnnotationMetadata();
}
}
// 擷取bean定義的中繼資料被@Configuration注解标注的屬性字典值
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
// 如果bean被@Configuration注解标注,且屬性proxyBeanMethods為false(使用代理模式),則将bean定義記為full
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
// 如果bean被@configuration注解标注,且被注解@Component,@ComponentScan、@Import、@ImportResource或者@Bean标記的方法,則将bean定義标記為lite
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
// bean定義是一個标記為full或lite的候選項,如果設定order則設定order屬性值
Integer order = getOrder(metadata);
// 如果值不為空的話,那麼直接設定值到具體的beanDefinition
if (order != null) {
// 設定bean定義的order值
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
}
以上主要完成:
判斷目前BeanDefinition是否被@Configuration注解标注,如果是,并為BeanDefinition設定屬性為lite或者full,此處設定屬性值是為了後續進行調用。如果不是被@Configuration注解标注,直接傳回false。
如果被@Configuration注解标注:
- 配置proxyBeanMethods代理為true則為full
- 如果又被标注了@Bean、@Component、@ComponentScan、@Import、@ImportResource注解,則設定為lite
- 如果配置類上被@Order注解标注,則設定BeanDefinition的order屬性值
- 傳回true,則該beanDefinition會被加入到configCandidates集合中。
執行完第一步,我們的例子中隻有SpringConfiguration是被@Configuration注解标注的,是以符合。
2,第二步,解析被@Configuration注解标注的BeanDefinition
解析被@Configuration注解标注的BeanDefinition且帶有@Controller、@Import、@ImportResource、@ComponentScan、@ComponentScans、@Bean的BeanDefinition,擷取所有的bean,包括掃描的bean對象,@Import導入的bean對象等放入configClasses集合裡面。
主要是由
ConfigurationClassParser#paser()
方法來完成的
class ConfigurationClassParser {
public void parse(Set<BeanDefinitionHolder> configCandidates) {
// 循環周遊configCandidates
for (BeanDefinitionHolder holder : configCandidates) {
// 擷取BeanDefinition
BeanDefinition bd = holder.getBeanDefinition();
// 根據BeanDefinition類型的不同,調用parse不同的重載方法,實際上最終都是調用processConfigurationClass()方法
try {
// 注解類型
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
// 有class對象的
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
}
}
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
// 判斷是否跳過解析,基于@Conditional标簽判斷該對象是否要跳過
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// SourceClass的意義:簡單的包裝類,目的是為了以統一的方式去處理帶有注解的類,不管這些類是如何加載的
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
// 解析各種注解
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
// 将解析的配置類存儲起來,這樣回到parse方法時,能取到值
this.configurationClasses.put(configClass, configClass);
}
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// @Configuration繼承了@Component
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// 遞歸處理内部類,因為内部類也是一個配置類,配置類上有@configuration注解,該注解繼承@Component,if判斷為true,調用processMemberClasses方法,遞歸解析配置類中的内部類
processMemberClasses(configClass, sourceClass, filter);
}
// 如果配置類上加了@PropertySource注解,那麼就解析加載properties檔案,并将屬性添加到spring上下文中
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
}
// 處理@ComponentScan或者@ComponentScans注解,并将掃描包下的所有bean轉換成填充後的ConfigurationClass
// 此處就是将自定義的bean加載到IOC容器,因為掃描到的類可能也添加了@ComponentScan和@ComponentScans注解,是以需要進行遞歸解析
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和@ComponentScans配置的掃描的包所包含的類
// 比如 basePackages = com.bobo, 那麼在這一步會掃描出這個包及子包下的class,然後将其解析成BeanDefinition
// (BeanDefinition可以了解為等價于BeanDefinitionHolder)
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 通過上一步掃描包com.bobo,有可能掃描出來的bean中可能也添加了ComponentScan或者ComponentScans注解.
//是以這裡需要循環周遊一次,進行遞歸(parse),繼續解析,直到解析出的類上沒有ComponentScan和ComponentScans
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
// 判斷是否是一個配置類,并設定full或lite屬性
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
// 通過遞歸方法進行解析
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// 處理@Import注解
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// 處理@ImportResource注解,導入spring的配置檔案
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);
}
}
// 處理加了@Bean注解的方法,将@Bean方法轉化為BeanMethod對象,儲存再集合中
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 處理接口的預設方法實作,從jdk8開始,接口中的方法可以有自己的預設實作,是以如果這個接口的方法加了@Bean注解,也需要被解析
processInterfaces(configClass, sourceClass);
// 解析父類,如果被解析的配置類繼承了某個類,那麼配置類的父類也會被進行解析
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();
}
}
return null;
}
}
3,第三步,将掃描到的所有beanDefinition注冊到容器的BeanDefinitionMap中
将第二步中掃描到的所有bean(在
configClasses
中存放着),注入到容器的
BeanDefinitionMap
中,完成導入bean的注入工作。
class ConfigurationClassBeanDefinitionReader {
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
// 循環調用loadBeanDefinitionsForConfigurationClass方法
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
/**
* 讀取一個單獨的ConfigurationClass類,注冊bean本身(@Import引入的普通類)或者@Configuration配置類的Bean方法
*/
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
// 如果一個bean是通過@Import(ImportSelector)的方式添加到容器中的,那麼此時configClass.isImported()傳回的是true
// 而且configClass的importedBy屬性裡面存儲的是ConfigurationClass就是将bean導入的類
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 判斷目前的bean中是否含有@Bean注解的方法,如果有,需要把這些方法産生的bean放入到BeanDefinitionMap當中
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 将@ImportResource引入的資源注入IOC容器
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 如果bean上存在@Import注解,且import的是一個實作了ImportBeanDefinitionRegistrar接口,
//則執行ImportBeanDefinitionRegistrar的registerBeanDefinitions()方法
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
}
最終會注冊到
BeanDefinitionMap
中。
4,第四步,判斷第三步中注入到BeanDefinitionMap中BeanDefinition是否已經被解析過,如果沒有被解析過,那麼需要繼續解析
// 這裡判斷registry.getBeanDefinitionCount() > candidateNames.length的目的是為了知道reader.loadBeanDefinitions(configClasses)這一步有沒有向BeanDefinitionMap中添加新的BeanDefinition
// 如果有,registry.getBeanDefinitionCount()就會大于candidateNames.length
// 這樣就需要再次周遊新加入的BeanDefinition,并判斷這些bean是否已經被解析過了,如果未解析,需要重新進行解析
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;
}
這裡判斷registry.getBeanDefinitionCount() > candidateNames.length的目的是為了知道reader.loadBeanDefinitions(configClasses)這一步有沒有向BeanDefinitionMap中添加新的BeanDefinition,如果有,registry.getBeanDefinitionCount()就會大于candidateNames.length,這樣就需要再次周遊新加入的BeanDefinition,并判斷這些bean是否已經被解析過了,如果未解析,需要重新進行解析。
四,postProcessBeanFactory()方法
當然
ConfigurationClassPostProcessor#postProcessBeanFactory()
方法的執行也是在
invokeBeanFactoryPostProcessors()
方法中執行的,如果不了解
invokeBeanFactoryPostProcessors()
方法的執行流程的話,請移步去看上一篇文章:吃透Spring源碼(十五):invokeBeanFactoryPostProcessors 執行流程
此方法主要是對被
@Configuration
注解标注的類添加CGLIB增強處理以及添加
ImportAwareBeanPostProcessor
後置處理。
下面我們直接到
ConfigurationClassPostProcessor#postProcessBeanFactory()
方法中:
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
// 如果還沒執行解析工作,則先去執行processConfigBeanDefinitions進行解析
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
// 對目前容器中被@Configuration注解标注的BeanDefinition進行CGLIb增強
enhanceConfigurationClasses(beanFactory);
// 添加ImportAwareBeanPostProcessor後置處理器
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
為什麼要對被
@Configuration
注解标注的類添加CGLIB增強?
解釋這個問題我們先來看一段代碼:
@Configuration
public class SpringConfiguration {
@Bean
public A a() {
return new A();
}
@Bean
public B b() {
a();
return new B();
}
}
配置類中有兩個方法,a()方法傳回對象A,b()方法調用一下a()方法,在傳回對象B,并且都被@Bean注解修飾。
SpringConfiguration配置類,在被Spring容器去建立B對象的時候,此時A對象已經建立,那麼調用a()方法會再去建立A對象,是以無法保證Bean對象的單例性。
凡是加了@Configuration注解修飾的類都會被spring代理,目的是為了解決@Bean單例問題
五,總結
-
類是用來對@Configuration注解标注的BeanDefinition來處理的。ConfigurationClassPostProcessor
-
方法是解析@Configuration注解标注的BeanDefinition,同時解析出 @ComponentScan 和 @ComponentScans 掃描出的Bean,也會解析出加了 @Bean 注解的方法所注冊的Bean,以及通過 @Import 注解注冊的Bean和 @ImportResource 注解導入的配置檔案中配置的BeanpostProcessBeanDefinitionRegistry()
-
方法,對加了@Configuration注解修飾的類都會被spring代理,目的是為了解決@Bean單例問題。postProcessBeanFactory()