您好,登錄后才能下訂單哦!
今天小編給大家分享一下Spring注解@Import怎么使用的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來(lái)了解一下吧。
在項(xiàng)目開(kāi)發(fā)的過(guò)程中,我們會(huì)遇到很多名字為 @Enablexxx 的注解,比如@EnableApolloConfig
、 @EnableFeignClients
、 @EnableAsync
等。他們的功能都是通過(guò)這樣的注解實(shí)現(xiàn)一個(gè)開(kāi)關(guān),決定了是否開(kāi)啟某個(gè)功能模塊的所有組件的自動(dòng)化配置,這極大的降低了我們的使用成本。
按照默認(rèn)的習(xí)慣,我們會(huì)把某個(gè)功能模塊的開(kāi)啟注解定義為 @Enablexxx,功能的實(shí)現(xiàn)和名字格式其實(shí)無(wú)關(guān),而是其內(nèi)部實(shí)現(xiàn),這里用 @EnableAsync 來(lái)舉例子。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AsyncConfigurationSelector.class) public @interface EnableAsync { …… }
可以看到除了3個(gè)通用注解,還有一個(gè)@Import(AsyncConfigurationSelector.class)
注解,顯然它真正在這里發(fā)揮了關(guān)鍵作用,它可以往容器中注入一個(gè)配置類。
在 Spring 容器啟動(dòng)的過(guò)程中,執(zhí)行到調(diào)用invokeBeanFactoryPostProcessors(beanFactory)
方法的時(shí)候,會(huì)調(diào)用所有已經(jīng)注冊(cè)的 BeanFactoryPostProcessor,然后會(huì)調(diào)用實(shí)現(xiàn) BeanDefinitionRegistryPostProcessor
接口的后置處理器 ConfigurationClassPostProcessor
,調(diào)用其 postProcessBeanDefinitionRegistry()
方法, 在這里會(huì)解析通過(guò)注解配置的類,然后調(diào)用 ConfigurationClassParser#doProcessConfigurationClass()
方法,最終會(huì)走到processImports()
方法,對(duì) @Import 注解進(jìn)行處理,具體流程如下。
如果這部分流程不是很理解,推薦詳細(xì)閱讀一下 Spring 生命周期相關(guān)的代碼,不過(guò)不重要,不影響理解后面的內(nèi)容。
@Import 注解的功能是在ConfigurationClassParser
類的 processImports()
方法中實(shí)現(xiàn)的,對(duì)于這個(gè)方法我已經(jīng)做了詳細(xì)的注釋,請(qǐng)查看。
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) { // 如果使用@Import注解修飾的類集合為空,直接返回 if (importCandidates.isEmpty()) { return; } // 通過(guò)一個(gè)棧結(jié)構(gòu)解決循環(huán)引入 if (checkForCircularImports && isChainedImportOnStack(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { // 添加到棧中,用于處理循環(huán)import的問(wèn)題 this.importStack.push(configClass); try { // 遍歷每一個(gè)@Import注解的類 for (SourceClass candidate : importCandidates) { // 1. // 檢驗(yàn)配置類Import引入的類是否是ImportSelector子類 if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports // 候選類是一個(gè)導(dǎo)入選擇器->委托來(lái)確定是否進(jìn)行導(dǎo)入 Class<?> candidateClass = candidate.loadClass(); // 通過(guò)反射生成一個(gè)ImportSelect對(duì)象 ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry); // 獲取選擇器的額外過(guò)濾器 Predicate<String> selectorFilter = selector.getExclusionFilter(); if (selectorFilter != null) { exclusionFilter = exclusionFilter.or(selectorFilter); } // 判斷引用選擇器是否是DeferredImportSelector接口的實(shí)例 // 如果是則應(yīng)用選擇器將會(huì)在所有的配置類都加載完畢后加載 if (selector instanceof DeferredImportSelector) { // 將選擇器添加到deferredImportSelectorHandler實(shí)例中,預(yù)留到所有的配置類加載完成后統(tǒng)一處理自動(dòng)化配置類 this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { // 獲取引入的類,然后使用遞歸方式將這些類中同樣添加了@Import注解引用的類 // 執(zhí)行 ImportSelector.selectImports String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter); // 遞歸處理,被Import進(jìn)來(lái)的類也有可能@Import注解 processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); } } // 2. // 如果是實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口的bd else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // Candidate class is an ImportBeanDefinitionRegistrar -> // delegate to it to register additional bean definitions // 候選類是ImportBeanDefinitionRegistrar -> 委托給當(dāng)前注冊(cè)器注冊(cè)其他bean Class<?> candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry); /** * 放到當(dāng)前configClass的importBeanDefinitionRegistrars中 * 在ConfigurationClassPostProcessor處理configClass時(shí)會(huì)隨之一起處理 */ configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> // process it as an @Configuration class // 候選類既不是ImportSelector也不是ImportBeanDefinitionRegistrar-->將其作為@Configuration配置類處理 this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); /** * 如果Import的類型是普通類,則將其當(dāng)作帶有@Configuration的類一樣處理 */ processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter); } } } catch (BeanDefinitionStoreException ex) { …… finally { this.importStack.pop(); } } }
上述代碼的核心邏輯無(wú)非就是如下幾個(gè)步驟。
找到被 @Import 修飾的候選類集合,依次循環(huán)遍歷。
如果該類實(shí)現(xiàn)了ImportSelector
接口,就調(diào)用 ImportSelector
的 selectImports()
方法,這個(gè)方法返回的是一批配置類的全限定名,然后遞歸調(diào)用processImports()
繼續(xù)解析這些配置類,比如可以 @Import 的類里面有 @Import 注解,在這里可以遞歸處理。
如果被修飾的類沒(méi)有實(shí)現(xiàn) ImportSelector
接口,而是實(shí)現(xiàn)了ImportBeanDefinitionRegistrar
接口,則把對(duì)應(yīng)的實(shí)例放入importBeanDefinitionRegistrars
這個(gè)Map中,等到ConfigurationClassPostProcessor
處理 configClass 的時(shí)候,會(huì)與其他配置類一同被調(diào)用 ImportBeanDefinitionRegistrar
的 registerBeanDefinitions()
方法,以實(shí)現(xiàn)往 Spring 容器中注入一些 BeanDefinition。
如果以上的兩個(gè)接口都未實(shí)現(xiàn),則進(jìn)入 else 邏輯,將其作為普通的 @Configuration 配置類進(jìn)行解析。
所以到這里,你應(yīng)該明白 @Import 的作用機(jī)制了吧。對(duì)上述邏輯我總結(jié)了一張圖,如下。
繼續(xù)之前提到的 @EnableAsync 作為例子,源碼如下。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AsyncConfigurationSelector.class) public @interface EnableAsync { Class<? extends Annotation> annotation() default Annotation.class; boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default Ordered.LOWEST_PRECEDENCE; } // @Override public final String[] selectImports(AnnotationMetadata importingClassMetadata) { …… // 獲取 Mode AdviceMode adviceMode = attributes.getEnum(getAdviceModeAttributeName()); // 模板方法,由子類去實(shí)現(xiàn) String[] imports = selectImports(adviceMode); if (imports == null) { throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode); } return imports; } public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> { @Override @Nullable public String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: return new String[] {ProxyAsyncConfiguration.class.getName()}; case ASPECTJ: return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME}; default: return null; } } }
它通過(guò) @Import 注解引入了AsyncConfigurationSelector
配置類,它繼承了 AdviceModeImportSelector
類,而后者實(shí)現(xiàn)了 ImportSelector
接口,里面的實(shí)現(xiàn)了一個(gè)由注解指定 mode 屬性來(lái)決定返回的配置類的邏輯,而 mode 的默認(rèn)值就是 AdviceMode.PROXY
。
對(duì)應(yīng) switch 邏輯,將返回 ProxyAsyncConfiguration
類的全限定名。這就對(duì)應(yīng)了 @Import 處理邏輯的第一個(gè) if 邏輯塊,它將會(huì)解析這個(gè)類,然后遞歸調(diào)用processImports()
,再次進(jìn)入此方法,進(jìn)入第三個(gè)else邏輯塊,將其當(dāng)作一個(gè)普通配置類解析??梢钥吹?ProxyAsyncConfiguration
其實(shí)就是 @Configuration 類,它的作用是注冊(cè)一個(gè) Bean 對(duì)象 AsyncAnnotationBeanPostProcessor。
@Configuration @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration { @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public AsyncAnnotationBeanPostProcessor asyncAdvisor() { …… return bpp; } }
以上就是“Spring注解@Import怎么使用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請(qǐng)關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。