Spring中@Import的各种用法以及ImportAware接口详解
@Import注解
@Import注解提供了和XML中
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public@interfaceImport{ /** *{@linkConfiguration},{@linkImportSelector},{@linkImportBeanDefinitionRegistrar} *orregularcomponentclassestoimport. */ Class>[]value(); }
注解中只有一个value();。支持导入@Configuration标注的配置类,实现ImportSelector接口的类、实现ImportBeanDefinitionRegistrar接口的类和普通的@component类。
作为元注解使用
@Import可以作为元注解使用,可以在@Import的继承上封装一层。我的理解是,这样做不会对外(使用方)暴露我内部的具体实现细节。
举个例子:例如@EnableAspectJAutoProxy注解。
@Import(AspectJAutoProxyRegistrar.class) public@interfaceEnableAspectJAutoProxy{
@EnableAspectJAutoProxy就是被@Import这个元注解所标志了,我们(程序员)通过使用@EnableAspectJAutoProxy来开启AspectJAutoProxy,而Spring底层是通过@Import导入相应的配置类来实现的。
导入实现ImportSelector接口的类
先来看一下ImportSelector接口,该接口中只有一个方法:
publicinterfaceImportSelector{ String[]selectImports(AnnotationMetadataimportingClassMetadata); }
ImportSelector,输入选择器。该接口就是用来根据给定的条件,选择导入哪些配置类。
举个例子:例如@EnableTransactionManagement注解。
@Import(TransactionManagementConfigurationSelector.class) public@interfaceEnableTransactionManagement{
在@EnableTransactionManagement注解中使用了@Import(TransactionManagementConfigurationSelector.class)注解,其中TransactionManagementConfigurationSelector类就是实现了ImportSelector接口。
publicclassTransactionManagementConfigurationSelectorextendsAdviceModeImportSelector{ @Override protectedString[]selectImports(AdviceModeadviceMode){ switch(adviceMode){ casePROXY: returnnewString[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; caseASPECTJ: returnnewString[]{ TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME}; default: returnnull; } } }
方法的内部实现逻辑也很简单,就是根据不同的AdviceMode导入不同的配置类,来实现事务管理。
导入实现ImportBeanDefinitionRegistrar接口的类
ImportBeanDefinitionRegistrar接口中也只有一个方法:
publicinterfaceImportBeanDefinitionRegistrar{ voidregisterBeanDefinitions(AnnotationMetadataimportingClassMetadata,BeanDefinitionRegistryregistry); }
该接口允许我们根据所给的注解元数据,按需注册额外的BeanDefinition。
举个例子:例如@EnableAspectJAutoProxy注解。
@Import(AspectJAutoProxyRegistrar.class) public@interfaceEnableAspectJAutoProxy{
@EnableAspectJAutoProxy注解引入了AspectJAutoProxyRegistrar.class类,这个类就是实现了ImportBeanDefinitionRegistrar接口。
classAspectJAutoProxyRegistrarimplementsImportBeanDefinitionRegistrar{
@Override publicvoidregisterBeanDefinitions( AnnotationMetadataimportingClassMetadata,BeanDefinitionRegistryregistry){ AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributesenableAspectJAutoProxy= 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);方法,这个方法就是在往传入的BeanDefinitionRegistryregistry中注册BeanDefinition。注册了BeanDefinition之后,Spring就会去实例化这个Bean,从而达到AspectJAutoProxy作用。
导入@Configuration类
这次@Import最常见是使用方法。我们可以拆分配置类,然后在程序中按需导入相应的配置。
举个例子:例如@EnableRetry注解。使用这个注解可以开启retry功能。
@EnableAspectJAutoProxy(proxyTargetClass=false) @Import(RetryConfiguration.class) public@interfaceEnableRetry{
其内部就是导入了RetryConfiguration这个配置类。
ImportAware接口
ImportAware接口是需要和@Import一起使用的。在@Import作为元注解使用时,通过@Import导入的配置类如果实现了ImportAware接口就可以获取到导入该配置类接口的数据配置。有点绕,我们直接上代码。
举个例子:@EnableAsync注解。
@Import(AsyncConfigurationSelector.class) public@interfaceEnableAsync{ //AsyncConfigurationSelector源码 publicclassAsyncConfigurationSelectorextendsAdviceModeImportSelector{ privatestaticfinalStringASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME= "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"; @Override @Nullable publicString[]selectImports(AdviceModeadviceMode){ switch(adviceMode){ casePROXY: returnnewString[]{ProxyAsyncConfiguration.class.getName()}; caseASPECTJ: returnnewString[]{ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME}; default: returnnull; } } }
默认情况下使用AdviceMode为PROXY,导入了ProxyAsyncConfiguration类。
@Configuration @Role(BeanDefinition.ROLE_INFRASTRUCTURE) publicclassProxyAsyncConfigurationextendsAbstractAsyncConfiguration{ @Bean(name=TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) publicAsyncAnnotationBeanPostProcessorasyncAdvisor(){ Assert.notNull(this.enableAsync,"@EnableAsyncannotationmetadatawasnotinjected"); AsyncAnnotationBeanPostProcessorbpp=newAsyncAnnotationBeanPostProcessor(); ClasscustomAsyncAnnotation=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.getNumber("order")); returnbpp; } }
在ProxyAsyncConfiguration的asyncAdvisor方法中需要获取到@EnableAsync上的一些设置值,例如:this.enableAsync.getBoolean("proxyTargetClass"),this.enableAsync.
this.enableAsync是其父类AbstractAsyncConfiguration的属性。AbstractAsyncConfiguration实现了ImportAware接口,从而就可以获取到@EnableAsync上的信息了。
//AbstractAsyncConfiguration#setImportMetadata源码 publicvoidsetImportMetadata(AnnotationMetadataimportMetadata){ this.enableAsync=AnnotationAttributes.fromMap( importMetadata.getAnnotationAttributes(EnableAsync.class.getName(),false)); if(this.enableAsync==null){ thrownewIllegalArgumentException( "@EnableAsyncisnotpresentonimportingclass"+importMetadata.getClassName()); } }
可能这个例子有点复杂的,还有一个稍微简单一点的例子:EnableRedisHttpSession。关于这个,读者可以自己去看一下源码debug学习一下。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。