这篇文章主要介绍了spring boot@EnableXXXX注解编程模型,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
正文
spring boot@EnableXXXX注解编程模型讲解
@EnableXXXX编程模型
在spring boot中,@EnableXXX注解的功能通常是开启某一种功能。根据某些外部配置自动装配一些bean,来达到开启某些功能的目的。光说很抽象,要具体分析。
@Enable模型的实现方式基本有3种。一个基本的@Enable注解的模型如下。
|
1
2
3
4
5
6
7
8
9
10
11
|
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@Import(XXXX.class)public @interface EnableDiscoveryClient { /** * If true, the ServiceRegistry will automatically register the local server. */ boolean autoRegister() default true;} |
对应XXXX.class的不同,有3种实现方式。
- 普通配置类,里面包含@Bean方法用于实例化bean
- ImportSelector实现类
- ImportBeanDefinitionRegistrar实现类
上面3种类都属于@Import注解的导入对象,整个外部化配置过程围绕@Import注解进行解析,导入类。
@Import注解处理时机节点(@Confguration注解的类处理)
@Import注解的处理过程大致可以描述为:
- 寻找BeanFactory中所有被@Configuration注解修饰的类,包括被@Configuration派生注解修饰的类。
- 寻找被@Configuration注解修饰的类上的所有注解元信息(这里的搜索不仅是搜索当前注解,还会迭代往修饰注解的注解的注解上层…..一直搜索@Import,直到注解最原始的注解),获取@Import注解的导入类信息,如果没有则不处理。
- 根据导入类的信息,判定为
普通配置类,里面包含@Bean方法用于实例化bean
ImportSelector实现类
ImportBeanDefinitionRegistrar实现类
3种形式进行处理。
从context启动开始跟踪主线处理代码,调用链条如下。
|
1
2
3
4
|
org.springframework.context.support.AbstractApplicationContext#refreshorg.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessorsorg.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistryorg.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions(主线代码,必看) |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { //定义@Conguration注解修饰的类注册信息列表 List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames();//检查当前context中所有的bean注册信息 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); } } //检查class是否是@Conguration注解修饰的类,包括被“继承”@Conguration注解的注解,例如@SpringBootConguration,具体可以跟踪ConfigurationClassUtils.checkConfigurationClassCandidate实现 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 //对配置类排序,顺序由Ordered接口决定 configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); });//......略略略 // 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); //......略略略 } while (!candidates.isEmpty());//......略略略} |
|
1
|
org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set |
ImportSelector
|
1
|
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); |
返回结果是所有需要导入的的类的全限定名。
对于全限定名数组,逐个进行org.springframework.context.annotation.ConfigurationClassParser#processImports,相当于循环调用processImports,把新导入的类也当做@Import导入的类处理,如果新导入的类继续导入新的类,就继续org.springframework.context.annotation.ConfigurationClassParser#processImports。直到新导入的类不是
ImportSelector。
ImportBeanDefinitionRegistrar处理
当@Import的类是不是ImportSelector之后,如果是ImportBeanDefinitionRegistrar,那就做BeanDefinition信息注册到BeanFactory操作,具体实现在org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions实现,在这里的处理过程是。
|
1
2
3
4
5
6
7
8
9
10
11
|
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); //将ImportBeanDefinitionRegistrar放入map缓存起来 configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());} |
|
1
2
3
|
public void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) { this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);} |
先缓存@Import导入的ImportBeanDefinitionRegistrar信息,稍后统一调用ImportBeanDefinitionRegistrar加载注册BeanDefinition信息。
@Configurtion注解的类处理
重复上面的整个流程,处理这个被@Configuration注解标注的类。比较需要注意的是一般@Configuration注解标注的类常用@Bean方式来实例化实例。这里#Bean也会解析出一个BeanMethod信息集合,稍后跟ImportBeanDefinitionRegistrar的缓存信息一样统一调用然后注册BeanDefinition。
|
1
2
3
4
5
6
7
|
// Process individual @Bean methods//对配置类的@Bean方法处理逻辑//获取所有@Bean标注的方法元信息,后续处理Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));} |
统一调用配置类解析出来的信息注册BeanDefinition
在解析完配置类之后,实际还没有进行BeanDefinition的注册,只得到了可以用来注册BeanDefinition的“信息工具”,利用@Bean得到了BeanMethod,@Import(xxxImportBeanDefinitionRegistrar.class)得到了ImportBeanDefinitionRegistrar的实现类。最终要使用这些工具进行BeanDefinition 信息注册。
org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions中,当处理完@Configuration注解的类之后就进行ImportBeanDefinitionRegistrar的BeanDefinition注册加载。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//处理@Configuration,递归寻找@Configuration,以及解析@Configuration里面的@Import、@Bean、@Component、@ImportResource等。parser.parse(candidates);parser.validate();//获取parser中解析得到的所有配置类Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);// Read the model and create bean definitions based on its contentif (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry());}//根据递归找出的配置类和解析配置类得到的信息,加载BeanDefinitionthis.reader.loadBeanDefinitions(configClasses); |
|
1
2
|
org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsorg.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
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()) { //利用@Bean的Method加载BeanDefinition loadBeanDefinitionsForBeanMethod(beanMethod); } loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); //利用缓存的ImportBeanDefinitionRegistrar加载注册beandefintion loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());} |
|
1
2
|
org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars(以ImportBeanDefinitionRegistrar为例跟踪)org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions(注册BeanDefinition信息到BeanFactory) |
以上为个人经验,希望能给大家一个参考,也希望大家多多支持米米素材网。
原文链接:https://blog.csdn.net/qq_20597727/article/details/82713267

发表评论