转载

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;
        }
    }
}

默认情况下使用 AdviceModePROXY ,导入了 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;
    }
}

ProxyAsyncConfigurationasyncAdvisor 方法中需要获取到 @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学习一下。

欢迎关注公众号,大家一起学习成长。

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

原文  https://segmentfault.com/a/1190000020604912
正文到此结束
Loading...