主要分析內容
一、@Import、@ImportResource注解使用demo
二、ConfigurationClassPostProcessor加載@Configuration類完整流程圖
三、ConfigurationClassPostProcessor加載@Configuration類源碼分析
- AbstractApplicationContext#refresh
- ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
- ConfigurationClassParser#parse
- ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
四、@EnableXXX設計分析
(源碼基於spring 5.1.3.RELEASE分析)
一、@Import @ImportResource 注解使用demo
簡述:demo例子中配置bean,使用常用注解@PropertySource @ImportResource @ComponentScan @Import 完成bean的加載, 往往在springboot starter組件中最為常見
demo代碼gitee地址點擊這里
CustomImport.java
@Configuration
@PropertySource(value = "classpath:bean.properties")
@ImportResource(value = {"classpath:ioc-importresource.xml"})
@ComponentScan(basePackages = "com.nancy.ioc.BeanFactoryPostProcessor.importtest.componenttoscan")
@Import(value = {MyImportSelector.class, MyImportBeanDefinitionRegistrar.class, MyImportConfiguration.class})
@ImportRegistrarFlag
public class CustomImport {
@Autowired
private Environment environment ;
@Bean("registryBean")
public RegistryBean registryBean(){
String beanFieldName2 = environment.getProperty("bean.field.name2") ;
System.out.println("獲取@PropertySource 注入的properties ==》 bean.field.name2 = [" + beanFieldName2 + "] from environment");
return new RegistryBean("CustomImport內@Bean加載的bean, beanFieldName2=" + beanFieldName2) ;
}
}
MyImportSelector.java
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{MyConfiguration.class.getName()};
}
}
MyImportBeanDefinitionRegistrar.java
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if(importingClassMetadata.hasAnnotation(ImportRegistrarFlag.class.getName())){
System.out.println("存在ImportRegistrarFlag");
BeanDefinitionBuilder builder = rootBeanDefinition(Bean.class);
builder.addConstructorArgValue("ImportBeanDefinitionRegistrar加載bean");
builder.setRole(BeanDefinition.ROLE_APPLICATION);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
registry.registerBeanDefinition("importRegistrarFlagBean", beanDefinition);
}else {
System.out.println("不存在ImportRegistrarFlag");
}
}
}
MyImportConfiguration.java
@Configuration
public class MyImportConfiguration {
@Autowired
private Environment environment ;
@org.springframework.context.annotation.Bean("selectorImportBean")
public Bean bean(){
String beanFieldName2 = environment.getProperty("bean.field.name2") ;
return new Bean(beanFieldName2) ;
}
}
MyConfiguration.java
@Configuration
public class MyConfiguration {
@Autowired
private Environment environment ;
@org.springframework.context.annotation.Bean("myConfigurationBean")
public Bean bean(){
String beanFieldName2 = environment.getProperty("bean.field.name2") ;
return new Bean(beanFieldName2) ;
}
}
ImportRegistrarFlag.java
public @interface ImportRegistrarFlag {
}
Bean.java
public class Bean {
public Bean(){
}
public Bean(String name){
System.out.println("Bean構造函數被調用啦 name =" + name);
this.name = name ;
}
// .......
@Override
public String toString() {
return "Bean{" +
"name='" + name + '\'' +
'}';
}
}
RegistryBean.java
public class RegistryBean {
public RegistryBean(){
}
public RegistryBean(String name){
this.name = name ;
}
// .......
@Override
public String toString() {
return "RegistryBean{" +
"name='" + name + '\'' +
'}';
}
}
ioc-importresource.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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="importResourceBean" class="com.nancy.ioc.Bean">
<property name="name" value="importResourceBean"/>
</bean>
</beans>
ioc-importtest.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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.nancy.ioc.BeanFactoryPostProcessor.importtest.entry">
</context:component-scan>
</beans>
ComponentToScanImport.java
@Component
public class ComponentToScanImport {
public ComponentToScanImport(){
System.out.println("報掃描加載Component組件,對應的bean ==> ComponentToScanImport");
}
@Override
public String toString() {
return super.toString() + " ComponentToScanImport";
}
}
CustomImportTest.java
public class CustomImportTest {
private ApplicationContext applicationContext ;
@Before
public void beforeApplicationContext(){
/**
* ApplicationContext 自動注冊 BeanPostProcessor、InstantiationAwareBeanPostProcessor、BeanFactoryPostProcessor
* 不需要手動注冊
* */
applicationContext = new ClassPathXmlApplicationContext("ioc-importtest.xml") ;
}
@Test
public void test(){
Bean selectorImportBean = applicationContext.getBean("selectorImportBean", Bean.class) ;
System.out.println(selectorImportBean);
Bean importRegistrarFlagBean = applicationContext.getBean("importRegistrarFlagBean", Bean.class) ;
System.out.println(importRegistrarFlagBean);
Bean importResourceBean = applicationContext.getBean("importResourceBean", Bean.class) ;
System.out.println(importResourceBean);
RegistryBean registryBean = applicationContext.getBean("registryBean", RegistryBean.class) ;
System.out.println(registryBean);
ComponentToScanImport componentToScanImport = applicationContext.getBean("componentToScanImport", ComponentToScanImport.class) ;
System.out.println(componentToScanImport);
}
@After
public void after(){
((ClassPathXmlApplicationContext)applicationContext).close();
}
}
運行結果:
存在ImportRegistrarFlag
報掃描加載Component組件,對應的bean ==> ComponentToScanImport
Bean構造函數被調用啦 name =hello world2
Bean構造函數被調用啦 name =hello world2
獲取@PropertySource 注入的properties ==》 bean.field.name2 = [hello world2] from environment
Bean構造函數被調用啦 name =ImportBeanDefinitionRegistrar加載bean
Bean{name='hello world2'}
Bean{name='ImportBeanDefinitionRegistrar加載bean'}
Bean{name='importResourceBean'}
RegistryBean{name='CustomImport內@Bean加載的bean, beanFieldName2=hello world2'}
com.nancy.ioc.BeanFactoryPostProcessor.importtest.componenttoscan.ComponentToScanImport@3b0090a4 ComponentToScanImport
二、ConfigurationClassPostProcessor加載@Configuration類完整流程圖
三、ConfigurationClassPostProcessor加載@Configuration類源碼分析
ConfigurationClassPostProcessor繼承圖如下,原理可參考spring源碼分析系列 (1) spring拓展接口BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// .....
try {
// .....
// 注冊和觸發容器級別接口:BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// .....
}
catch (BeansException ex) {
// .....
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
/**
* Derive further bean definitions from the configuration classes in the registry.
*/
@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);
// 加載所有configuration classes並解析得到對應的bean definitions
processConfigBeanDefinitions(registry);
}
跟進processConfigBeanDefinitions,主要完成步驟:
- 先獲取所有configuration classes自身的bean definitions
- 預處理configuration的bean definitions
- 委托ConfigurationClassParser解析所有configuration class的bean definitions, 所有支持元素:@Bean @Import @ImportResource @ComponentScans @PropertySource @PropertySources等,統一封裝成ConfigurationClass對象
- 將所有ConfigurationClass對象解析為對應的bean definitions
/**
* 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();
// 1、 先獲取所有configuration classes自身的bean definitions
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
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;
}
// 2、 預處理configuration的bean definitions
// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// 3、 委托ConfigurationClassParser解析所有configuration class的bean definitions
// 統一封裝成ConfigurationClass對象,包含所有支持元素:
// @Bean @Import @ImportResource @ComponentScans @PropertySource @PropertySources等
// 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);
// 4、 將第三步所有ConfigurationClass對象解析為對應的bean definitions
// 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();
}
}
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);
}
}
// 延遲處理DeferredImportSelector類
this.deferredImportSelectorHandler.process();
}
實際由ConfigurationClassParser#processConfigurationClass具體解析ConfigurationClass
protected void processConfigurationClass(ConfigurationClass configClass) 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.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// 遞歸解析configuration class以及其父類configuration class
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
跟進doProcessConfigurationClass,主要完成步驟:
- 首先遞歸處理任何成員(嵌套)類,即@Configuration修飾的類中嵌套@Configuration類,eg:springboot開啟aop配置的AopAutoConfiguration
@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration {
}
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
public static class CglibAutoProxyConfiguration {
}
}
- 處理@PropertySource注解
- 處理@ComponentScan包掃描注解
- 處理@Import注解
- 處理@ImportResource注解
- 處理ConfigurationClass中,被@Bean注解修飾發方法,即自定義bean注入
- 處理ConfigurationClass實現的接口中,被@Bean注解修飾發方法,即自定義bean注入
- 處理ConfigurationClass父類
源碼如下:
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// 首先遞歸處理任何成員(嵌套)類
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
}
// 處理@PropertySource注解
// 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");
}
}
// 處理@ComponentScan包掃描注解
// Process any @ComponentScan annotations
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
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());
}
}
}
}
// 處理@Import注解
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
// 處理@ImportResource注解
// Process any @ImportResource annotations
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注入
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 處理ConfigurationClass實現的接口中,被@Bean注解修飾發方法,即自定義bean注入
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// 處理ConfigurationClass父類
// 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;
}
看看對於@Import的解析:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
// 處理導入的ImportSelector、DeferredImportSelector類
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(
configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
// 處理導入的ImportBeanDefinitionRegistrar類
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
// 兩者都不是 即為當做普通configuration class處理
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
DeferredImportSelector繼承自ImportSelector,實現細粒度加載控制:
- 延遲加載:在ImportSelector類加載解析之后
- 同類排序:通過實現org.springframework.core.Ordered 或者 注解@Order實現排序
- 條件加載和過濾規則:實現Group邏輯
public interface DeferredImportSelector extends ImportSelector {
/**
* Return a specific import group or {@code null} if no grouping is required.
* @return the import group class or {@code null}
*/
@Nullable
default Class<? extends Group> getImportGroup() {
return null;
}
/**
* Interface used to group results from different import selectors.
*/
interface Group {
// ...............
}
}
// 根據所有ConfigurationClass加載bean definitions
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
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;
}
// ConfigurationClass將其自身聲明為 bean
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// ConfigurationClass 中對應的@Bean修飾的方法
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 導入xml
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 導入ImportBeanDefinitionRegistrar
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
由此所有bean definitions加載完,由幾個核心工具類配合完成:
- ConfigurationClassPostProcessor(容器級別接口) 加載解析的入口以及完成收尾部分;
- ConfigurationClassParser 負責將Configuration的bean definitions解析為對應的ConfigurationClass;
- ConfigurationClassBeanDefinitionReader 負責將ConfigurationClass集合解析為實際的bean definitions,並注入容器;
四、 @EnableXXX 設計分析
@EnableXXX 類似的注解在springboot中最為常見,starter組件加載往往會設計一個類似的啟動注解。以@SpringBootApplication為例分析:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
/**
* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
* for a type-safe alternative to String-based package names.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
/**
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
其中@SpringBootApplication包含兩個重要注解:
- @SpringBootConfiguration ==> 標識為spring Boot應用,實際就是被標記為spring的@Configuration配置類
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
- @EnableAutoConfiguration ==> spring Boot最核心功能,自動裝配入口
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
看到了熟悉的ImportSelector接口,其實現類AutoConfigurationImportSelector#selectImports源碼如下:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 加載所有spring Boot支持的默認自動裝配配置
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
// 排除程序配置的 不加載的自動化配置
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
spring-boot-autoconfigure的jar中包含了一個文件META-INF/spring-autoconfigure-metadata.properties,里面包含所有默認自動化配置
final class AutoConfigurationMetadataLoader {
// 默認自動裝配配置存放文件
protected static final String PATH = "META-INF/"
+ "spring-autoconfigure-metadata.properties";
private AutoConfigurationMetadataLoader() {
}
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
}
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
try {
Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
: ClassLoader.getSystemResources(path);
Properties properties = new Properties();
while (urls.hasMoreElements()) {
properties.putAll(PropertiesLoaderUtils
.loadProperties(new UrlResource(urls.nextElement())));
}
return loadMetadata(properties);
}
catch (IOException ex) {
throw new IllegalArgumentException(
"Unable to load @ConditionalOnClass location [" + path + "]", ex);
}
}
// ............
}
由此可以借鑒@EnableAutoConfiguration的設計思路,定義一個@EnableXXX注解用於開啟自定義的功能模塊。
除此之外,springboot還有一個工廠類加載機制,SpringFactoriesLoader類默認加載META-INF/spring.factories文件實現自動化裝配,靈活的拓展機制也是springboot大熱的原因。