好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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#refresh

org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors

org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

org.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测试数据pare(i1, i2);

  });

//......略略略

  // Parse each @Configuration class

   //处理每一个配置类

  ConfigurationClassParser parser = new ConfigurationClassParser(

        this .metadataReaderFactory, this .problemReporter, this .environment,

        this .resourceLoader, this 测试数据ponentScanBeanNameGenerator, 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 content

if ( this .reader == null ) {

    this .reader = new ConfigurationClassBeanDefinitionReader(

          registry, this .sourceExtractor, this .resourceLoader, this .environment,

          this .importBeanNameGenerator, parser.getImportRegistry());

}

//根据递归找出的配置类和解析配置类得到的信息,加载BeanDefinition

this .reader.loadBeanDefinitions(configClasses);

?

1

2

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions

org.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

查看更多关于spring boot@EnableXXXX注解编程模型讲解的详细内容...

  阅读:15次