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 .componentscanbeannamegenerator, 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://www.cnblogs.com/hermanlife/p/10019473.html