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

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

(0)

相关推荐

  • 详解SpringBoot开发使用@ImportResource注解影响拦截器

    问题描述 今天在给SpringBoot项目配置拦截器的时候发现怎么都进不到拦截器的方法里面,在搜索引擎上看了无数篇关于配置拦截器的文章都没有找到解决方案. 就在我准备放弃的时候,在 CSDN 上发现了一篇文章,说的是SpringBoot 用了@ImportResource 配置的拦截器就不起作用了.于是我就赶紧到Application启动类看了一眼,果然项目中使用了@ImportResource 注解用于配置系统的参数. 代码如下: 启动类配置 package com.xx.xxx; impor

  • Springboot @Import 详解

    SpringBoot 的 @Import 用于将指定的类实例注入之Spring IOC Container中. 今天抽空在仔细看了下Springboot 关于 @Import 的处理过程, 记下来以后看. 1. @Import 先看Spring对它的注释 (文档贴过来的), 总结下来作用就是和xml配置的 <import />标签作用一样,允许通过它引入 @Configuration 注解的类 (java config), 引入ImportSelector接口(这个比较重要, 因为要通过它去判

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

    @Import 注解 @Import注解提供了和XML中<import/>元素等价的功能,实现导入的一个或多个配置类.@Import即可以在类上使用,也可以作为元注解使用. @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { /** * {@link Configuration}, {@link ImportSelector}, {@link I

  • Spring中属性文件properties的读取与使用详解

    Spring中属性文件properties的读取与使用详解 实际项目中,通常将一些可配置的定制信息放到属性文件中(如数据库连接信息,邮件发送配置信息等),便于统一配置管理.例中将需配置的属性信息放在属性文件/WEB-INF/configInfo.properties中. 其中部分配置信息(邮件发送相关): #邮件发送的相关配置 email.host = smtp.163.com email.port = xxx email.username = xxx email.password = xxx

  • 基于Spring中各个jar包的作用及依赖(详解)

    先附spring各版本jar包下载链接http://repo.spring.io/release/org/springframework/spring/ spring.jar 是包含有完整发布模块的单个jar 包.但是不包括mock.jar, aspects.jar, spring-portlet.jar, and spring-hibernate2.jar 示例图片为Spring-2.5.6.jar的包目录 下面讲解各个jar包的作用: 1.org.springframework.aop或sp

  • Python中import导入不同目录的模块方法详解

    测试的目录如下: root    ├── module_root.py    ├── package_a    │   ├── child    │   │   ├── __init__.py    │   │   └── child_a.py    │   ├── module.py    │   └── module_a.py    └── package_b        └── module_b.py 每个文件中的内容如下(__init__.py文件可以为空): print(__name

  • PHP中header函数的用法及其注意事项详解

    void header ( string $string [, bool $replace = true [, int $http_response_code ]] ) : Send a raw HTTP header 下面有一些使用header的几种用法: 1.使用header函数进行跳转页面: header('Location:'.$url); 其中$url就是将要跳转的url了. 这种用法的注意事项有以下几点: •Location和":"之间不能有空格,否则会出现错误(注释:我刚

  • 浅谈Spring中@Import注解的作用和使用

    @Import用来导入@Configuration注解的配置类.声明@Bean注解的bean方法.导入ImportSelector的实现类或导入ImportBeanDefinitionRegistrar的实现类. @Import注解的作用 查看Import注解源码 /** * Indicates one or more {@link Configuration @Configuration} classes to import. * * <p>Provides functionality eq

  • Spring Cloud中使用jib进行docker部署的步骤详解

    Jib介绍 Jib 是 Google 开发的可以直接构建 Java 应用的 Docker 和 OCI 镜像的类库,以 Maven 和 Gradle 插件形式提供. 通过 Jib,Java 开发者可以使用他们熟悉的 Java 工具来构建容器.Jib 是一个快速而简单的容器镜像构建工具,它负责处理将应用程序打包到容器镜像中所需的所有步骤.它不需要你编写 Dockerfile 或安装 Docker,而且可以直接集成到 Maven 和 Gradle中 -- 只需要将插件添加到构建中,就可以立即将 Jav

  • Spring解密之XML解析与Bean注册示例详解

    为什么开始看spring的源码 半路转行写代码快一年半了,从开始工作就在使用spring框架,虽然会用,会搭框架,但是很多时候不懂背后的原理,比如:spring是怎样控制事务的,springmvc是怎样处理请求的,aop是如何实现的...这让人感觉非常不踏实,那就开始慢慢边看书边研究spring的源码吧!!! 怎样高效的看源码 我的答案是带着具体的问题去看源码,不然非常容易陷入源码细节中不能自拔,然后就晕了,最后你发现这看了半天看的是啥玩意啊. 引言 Spring是一个开源的设计层面框架,解决了

  • Java JDK动态代理(AOP)用法及实现原理详解

    Java-JDK动态代理(AOP)使用及实现原理分析 第一章:代理的介绍 介绍:我们需要掌握的程度 动态代理(理解) 基于反射机制 掌握的程度: 1.什么是动态代理? 2.动态代理能够做什么? 后面我们在用Spirng和Mybatis的时候,要理解怎么使用的. 1.什么是代理? 代理,在我们日常生活之中就有体现,代购,中介,换ip,商家等等. 比如有一家美国的大学,可以对全世界招生.留学中介(代理 ) 留学中介(代理):帮助这家美国的学校招生,中介是学校的代理中介是代替学校完成招生功能 代理特点

  • Spring整合MyBatis(Maven+MySQL)图文教程详解

    一. 使用Maven创建一个Web项目 为了完成Spring4.x与MyBatis3.X的整合更加顺利,先回顾在Maven环境下创建Web项目并使用MyBatis3.X,第一.二点内容多数是回顾过去的内容 . 1.2.点击"File"->"New"->"Other"->输入"Maven",新建一个"Maven Project",如下图所示: 1.2.请勾选"Create a si

随机推荐