好得很程序员自学网

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

Springboot @Import 详解

springboot 的 @import 用于将指定的类实例注入之spring ioc container中。 

今天抽空在仔细看了下springboot 关于 @import 的处理过程, 记下来以后看。

1. @import

先看spring对它的注释 (文档贴过来的), 总结下来作用就是和xml配置的 <import />标签作用一样,允许通过它引入 @configuration 注解的类 (java config), 引入importselector接口(这个比较重要, 因为要通过它去判定要引入哪些@configuration) 和 importbeandefinitionregistrar 接口的实现, 也包括 @component注解的普通类。

但是如果要引入另一个xml 文件形式配置的 bean, 则需要通过 @importresource 注解。

?

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

/**

  * indicates one or more {@link configuration @configuration} classes to import.

  *

  * <p>provides functionality equivalent to the {@code <import/>} element in spring xml.

  * allows for importing {@code @configuration} classes, {@link importselector} and

  * {@link importbeandefinitionregistrar} implementations, as well as regular component

  * classes (as of 4.2; analogous to {@link annotationconfigapplicationcontext#register}).

  *

  * <p>{@code @bean} definitions declared in imported {@code @configuration} classes should be

  * accessed by using {@link org.springframework.beans.factory.annotation.autowired @autowired}

  * injection. either the bean itself can be autowired, or the configuration class instance

  * declaring the bean can be autowired. the latter approach allows for explicit, ide-friendly

  * navigation between {@code @configuration} class methods.

  *

  * <p>may be declared at the class level or as a meta-annotation.

  *

  * <p>if xml or other non-{@code @configuration} bean definition resources need to be

  * imported, use the {@link importresource @importresource} annotation instead.

  *

  * @author chris beams

  * @author juergen hoeller

  * @since 3.0

  * @see configuration

  * @see importselector

  * @see importresource

  */

@target (elementtype.type)

@retention (retentionpolicy.runtime)

@documented

public @interface import {

 

   /**

    * {@link configuration}, {@link importselector}, {@link importbeandefinitionregistrar}

    * or regular component classes to import.

    */

   class <?>[] value();

 

}

2. importselector

因为 @import 的实现有很多时候需要借助 importselector 接口, 所以我们再看下这个接口的描述, 总结下来就是需要通过这个接口的实现去决定要引入哪些 @configuration。 它如果实现了以下四个aware 接口, 那么spring保证会在调用它之前先调用aware接口的方法。

至于为什么要保证调用aware, 我个人觉得应该是你可以通过这些aware去感知系统里边所有的环境变量, 从而决定你具体的选择逻辑。

?

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

/**

  * interface to be implemented by types that determine which @{@link configuration}

  * class(es) should be imported based on a given selection criteria, usually one or more

  * annotation attributes.

  *

  * <p>an {@link importselector} may implement any of the following

  * {@link org.springframework.beans.factory.aware aware} interfaces, and their respective

  * methods will be called prior to {@link #selectimports}:

  * <ul>

  * <li>{@link org.springframework.context.environmentaware environmentaware}</li>

  * <li>{@link org.springframework.beans.factory.beanfactoryaware beanfactoryaware}</li>

  * <li>{@link org.springframework.beans.factory.beanclassloaderaware beanclassloaderaware}</li>

  * <li>{@link org.springframework.context.resourceloaderaware resourceloaderaware}</li>

  * </ul>

  *

  * <p>importselectors are usually processed in the same way as regular {@code @import}

  * annotations, however, it is also possible to defer selection of imports until all

  * {@code @configuration} classes have been processed (see {@link deferredimportselector}

  * for details).

  *

  * @author chris beams

  * @since 3.1

  * @see deferredimportselector

  * @see import

  * @see importbeandefinitionregistrar

  * @see configuration

  */

public interface importselector {

 

   /**

    * select and return the names of which class(es) should be imported based on

    * the {@link annotationmetadata} of the importing @{@link configuration} class.

    */

   string[] selectimports(annotationmetadata importingclassmetadata);

 

}

3. springboot 对@import注解的处理过程

springboot对注解的处理都发生在abstractapplicationcontext -> refresh() -> invokebeanfactorypostprocessors(beanfactory) -> configurationclasspostprocessor -> postprocessbeandefinitionregistry()方法中。

(稍微说下也免得我自己忘了, springboot初始化的普通context(非web) 是annotationconfigapplicationcontext, 在初始化的时候会初始化两个工具类, annotatedbeandefinitionreader 和 classpathbeandefinitionscanner 分别用来从 annotation driven 的配置和xml的配置中读取beandefinition并向context注册, 那么在初始化 annotatedbeandefinitionreader 的时候, 会向beanfactory注册一个configurationclasspostprocessor 用来处理所有的基于annotation的bean, 这个configurationclasspostprocessor 是 beanfactorypostprocessor 的一个实现,springboot会保证在  invokebeanfactorypostprocessors(beanfactory) 方法中调用注册到它上边的所有的beanfactorypostprocessor)

如下代码显示是通过 configurationclassparser 类来转换的

?

1

2

3

4

// parse each @configuration class

     configurationclassparser parser = new configurationclassparser(

         this .metadatareaderfactory, this .problemreporter, this .environment,

         this .resourceloader, this 测试数据ponentscanbeannamegenerator, registry);

那么在 configurationclassparser -> processconfigurationclass() -> doprocessconfigurationclass() 方法中我们找到了 (这里边的流程还是很清楚的, 分别按次序处理了@propertysource, @componentscan, @import, @importresource, 在处理这些注解的时候是通过递归处理来保证所有的都被处理了)

?

1

2

// process any @import annotations

     processimports(configclass, sourceclass, getimports(sourceclass), true );

那接下来就看它到底是怎么做的 . 流程依然清晰 :

  首先, 判断如果被import的是 importselector.class 接口的实现, 那么初始化这个被import的类, 然后调用它的selectimports方法去获得所需要的引入的configuration, 然后递归处理

  其次, 判断如果被import的是 importbeandefinitionregistrar 接口的实现, 那么初始化后将对当前对象的处理委托给这个importbeandefinitionregistrar (不是特别明白, 只是我的猜测)

  最后, 将import引入的类作为一个正常的类来处理 ( 调用最外层的doprocessconfigurationclass())

所以, 从这里我们知道, 如果你引入的是一个正常的component, 那么会作为@compoent或者@configuration来处理, 这样在beanfactory里边可以通过getbean拿到, 但如果你是 importselector 或者 importbeandefinitionregistrar 接口的实现, 那么spring并不会将他们注册到beanfactory中,而只是调用他们的方法。

?

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

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

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) {

           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 ( this .deferredimportselectors != null && selector instanceof deferredimportselector) {

               this .deferredimportselectors.add(

                   new deferredimportselectorholder(configclass, (deferredimportselector) selector));

             }

             else {

               string[] importclassnames = selector.selectimports(currentsourceclass.getmetadata());

               collection<sourceclass> importsourceclasses = assourceclasses(importclassnames);

               processimports(configclass, currentsourceclass, importsourceclasses, false );

             }

           }

           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());

           }

           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();

       }

     }

   }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

原文链接:http://HdhCmsTestcnblogs测试数据/hermanlife/p/10019473.html

查看更多关于Springboot @Import 详解的详细内容...

  阅读:9次