Spring中@Import的各種用法以及ImportAware接口詳解
更新時間:2019-10-08 14:01:56 作者:佚名 我要評論(0)
@Import注解提供了和XML中<import/>元素等價的功能,實現導入的一個或多個配置類。@Import即可以在類上使用,也可以作為元注解使用。
@Tar
@Import 注解
@Import注解提供了和XML中<import/>元素等價的功能,實現導入的一個或多個配置類。@Import即可以在類上使用,也可以作為元注解使用。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { /** * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar} * or regular component classes to import. */ Class<?>[] value(); }
注解中只有一個value();。支持導入@Configuration標注的配置類,實現ImportSelector接口的類、實現ImportBeanDefinitionRegistrar接口的類和普通的@component類。
作為元注解使用
@Import可以作為元注解使用,可以在@Import的繼承上封裝一層。我的理解是,這樣做不會對外(使用方)暴露我內部的具體實現細節。
舉個例子:例如@EnableAspectJAutoProxy注解。
@Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy {
@EnableAspectJAutoProxy就是被@Import這個元注解所標志了,我們(程序員)通過使用@EnableAspectJAutoProxy來開啟AspectJAutoProxy,而Spring底層是通過@Import導入相應的配置類來實現的。
導入實現ImportSelector接口的類
先來看一下ImportSelector接口,該接口中只有一個方法:
public interface ImportSelector { String[] selectImports(AnnotationMetadata importingClassMetadata); }
ImportSelector,輸入選擇器。該接口就是用來根據給定的條件,選擇導入哪些配置類。
舉個例子:例如@EnableTransactionManagement注解。
@Import(TransactionManagementConfigurationSelector.class) public @interface EnableTransactionManagement {
在@EnableTransactionManagement注解中使用了@Import(TransactionManagementConfigurationSelector.class)注解,其中TransactionManagementConfigurationSelector類就是實現了ImportSelector接口。
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> { @Override protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; case ASPECTJ: return new String[] { TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME}; default: return null; } } }
方法的內部實現邏輯也很簡單,就是根據不同的AdviceMode導入不同的配置類,來實現事務管理。
導入實現ImportBeanDefinitionRegistrar接口的類
ImportBeanDefinitionRegistrar接口中也只有一個方法:
public interface ImportBeanDefinitionRegistrar { void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry); }
該接口允許我們根據所給的注解元數據,按需注冊額外的BeanDefinition。
舉個例子:例如@EnableAspectJAutoProxy注解。
@Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy {
@EnableAspectJAutoProxy注解引入了AspectJAutoProxyRegistrar.class類,這個類就是實現了ImportBeanDefinitionRegistrar接口。
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy != null) { if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } }
registerBeanDefinitions中調用了AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);方法,這個方法就是在往傳入的BeanDefinitionRegistry registry中注冊BeanDefinition。注冊了BeanDefinition之后,Spring就會去實例化這個Bean,從而達到AspectJAutoProxy作用。
導入@Configuration類
這次@Import最常見是使用方法。我們可以拆分配置類,然后在程序中按需導入相應的配置。
舉個例子:例如@EnableRetry注解。使用這個注解可以開啟retry功能。
@EnableAspectJAutoProxy(proxyTargetClass = false) @Import(RetryConfiguration.class) public @interface EnableRetry {
其內部就是導入了RetryConfiguration這個配置類。
ImportAware接口
ImportAware接口是需要和@Import一起使用的。在@Import作為元注解使用時,通過@Import導入的配置類如果實現了ImportAware接口就可以獲取到導入該配置類接口的數據配置。有點繞,我們直接上代碼。
舉個例子:@EnableAsync注解。
@Import(AsyncConfigurationSelector.class) public @interface EnableAsync { //AsyncConfigurationSelector源碼 public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> { private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"; @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; } } }
默認情況下使用AdviceMode為PROXY,導入了ProxyAsyncConfiguration類。
@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() { Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected"); AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor(); Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation"); if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) { bpp.setAsyncAnnotationType(customAsyncAnnotation); } if (this.executor != null) { bpp.setExecutor(this.executor); } if (this.exceptionHandler != null) { bpp.setExceptionHandler(this.exceptionHandler); } bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass")); bpp.setOrder(this.enableAsync.<Integer>getNumber("order")); return bpp; } }
在ProxyAsyncConfiguration的asyncAdvisor方法中需要獲取到@EnableAsync上的一些設置值,例如:this.enableAsync.getBoolean("proxyTargetClass"),this.enableAsync.<Integer>getNumber("order")。
this.enableAsync是其父類AbstractAsyncConfiguration的屬性。AbstractAsyncConfiguration實現了ImportAware接口,從而就可以獲取到@EnableAsync上的信息了。
// AbstractAsyncConfiguration#setImportMetadata 源碼 public void setImportMetadata(AnnotationMetadata importMetadata) { this.enableAsync = AnnotationAttributes.fromMap( importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false)); if (this.enableAsync == null) { throw new IllegalArgumentException( "@EnableAsync is not present on importing class " + importMetadata.getClassName()); } }
可能這個例子有點復雜的,還有一個稍微簡單一點的例子:EnableRedisHttpSession。關于這個,讀者可以自己去看一下源碼debug學習一下。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
您可能感興趣的文章:
- 詳解SpringBoot開發使用@ImportResource注解影響攔截器
- Springboot @Import 詳解
您可能感興趣的文章:
相關文章
Spring中@Import的各種用法以及ImportAware接口詳解
@Import 注解 @Import注解提供了和XML中<import/>元素等價的功能,實現導入的一個或多個配置類。@Import即可以在類上使用,也可以作為元注解使用。 @Tar2019-10-08IntelliJ IDEA(2019)之mybatis反向生成的實現
mybatis的逆向工程是非常便捷的操作,能夠顯著的提高我們的開發效率,之前介紹過Eclipse的操作,本文來介紹下在idea中怎么處理。 mybatis逆向工程 1.配置文2019-10-08Idea2019創建Spingboot Web項目的方法步驟
1、Idea及Java版本:Idea2019.1 + jdk1.8 2、File > Peoject 3、Spring Initializr: 4、之后會自動下載需要的相關文件 5、src > main > com.xx2019-10-08
最新評論