Spring中@Import的各种用法以及ImportAware接口详解

 更新时间:2019年10月08日 11:15:15   作者:coder小黑  
这篇文章主要介绍了Spring中@Import的各种用法以及ImportAware接口详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

@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学习一下。

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

相关文章

  • 推荐几本学习java的书籍

    推荐几本学习java的书籍

    本文给大家推荐了几本学习Java的书籍,非常适合Java的初学者,有需要的朋友可以看看
    2014-10-10
  • springboot自定义yml配置文件及其外部部署过程

    springboot自定义yml配置文件及其外部部署过程

    这篇文章主要介绍了springboot自定义yml配置文件及其外部部署过程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • SpringBoot如何用java生成静态html

    SpringBoot如何用java生成静态html

    这篇文章主要介绍了SpringBoot如何用java生成静态html,文章围绕主题展开详细的内容介绍,需要的朋友可以参考一下
    2022-06-06
  • 出现java.lang.NoSuchMethodException异常的解决(靠谱)

    出现java.lang.NoSuchMethodException异常的解决(靠谱)

    这篇文章主要介绍了出现java.lang.NoSuchMethodException异常的解决方案(靠谱),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • Java实现求数组最长子序列算法示例

    Java实现求数组最长子序列算法示例

    这篇文章主要介绍了Java实现求数组最长子序列算法,涉及java针对数组的递归遍历、判断相关操作技巧,需要的朋友可以参考下
    2018-07-07
  • Spring动态注册多数据源的实现方法

    Spring动态注册多数据源的实现方法

    这篇文章主要介绍了Spring动态注册多数据源的实现方法,小编觉的挺不错的,现分享到脚本之家平台,需要的朋友可以参考下
    2018-01-01
  • springmvc中进行数据保存以及日期参数的保存过程解析

    springmvc中进行数据保存以及日期参数的保存过程解析

    这篇文章主要介绍了springmvc中进行数据保存以及日期参数的保存过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • SpringBoot thymeleaf实现饼状图与柱形图流程介绍

    SpringBoot thymeleaf实现饼状图与柱形图流程介绍

    这篇文章主要介绍了SpringBoot thymeleaf实现饼状图与柱形图流程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-12-12
  • Java基础之ClassLoader详解

    Java基础之ClassLoader详解

    这篇文章主要介绍了Java基础之ClassLoader详解,文中对ClassLoader有非常详细的解说,对正在学习java基础的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-04-04
  • SpringBoot如何优雅地处理全局异常详解

    SpringBoot如何优雅地处理全局异常详解

    这篇文章主要给大家介绍了关于SpringBoot如何优雅地处理全局异常的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用SpringBoot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-08-08

最新评论