Categories
Uncategorized

Spring in the various uses of @Import and ImportAware Interface

@Import comment

@Import and annotations provided in XML element equivalent functions, to achieve one or more import configuration class. @Import i.e. may be used in class, it can be used as meta-annotation.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

    /**
     * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
     * or regular component classes to import.
     */
    Class[] value();

}

Notes is only one value () ;. Support introduced @Configuration annotation configuration class, ImportSelector interface implementation class, interface and implementation class ImportBeanDefinitionRegistrar @component ordinary class.

Use as a meta-annotation

Annotations can be used as @Import element can be encapsulated in a layer of @Import succession. My understanding is that doing so will not be made (consumer) expose my internal specific implementation details.

For example: e.g. @EnableAspectJAutoProxy annotations.

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

@EnableAspectJAutoProxy @Import this is to be marked by the annotation element, we (programmer) to open by use of @EnableAspectJAutoProxy AspectJAutoProxy, while the bottom is introduced into the respective Spring configurations Classes @Import achieved.

Introducing class implements the interface ImportSelector

First look at ImportSelector interface, which is only one method:

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata importingClassMetadata);
}

ImportSelector, input selector. This interface is used in accordance with the given conditions, which select the import configuration class.

For example: e.g. @EnableTransactionManagement annotations.

@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

@EnableTransactionManagement annotation in the @Import (TransactionManagementConfigurationSelector.class) annotations, which is to achieve ImportSelector TransactionManagementConfigurationSelector class interface.

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector {
    @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;
        }
    }
}

The method of implementation of the internal logic is very simple, is introduced into different classes depending on the configuration of AdviceMode, to implement transaction management.

Introducing class implements the interface ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar interface is only one way:

public interface ImportBeanDefinitionRegistrar {
    void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}

This interface allows us according to metadata annotation, on-demand registration of additional BeanDefinition.

For example: e.g. @EnableAspectJAutoProxy annotations.

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

@EnableAspectJAutoProxy notes introduced AspectJAutoProxyRegistrar.class class, which is to achieve a ImportBeanDefinitionRegistrar interface.

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 call the AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary (registry); method, which is registered in BeanDefinition to the incoming BeanDefinitionRegistry registry. After registering BeanDefinition, Spring will go to instantiate Bean, so as to achieve AspectJAutoProxy role.

Class introduced @Configuration

The @Import The most common method is to use. We can split configuration class, and then introduced as needed in the appropriate configuration programs.

For example: e.g. @EnableRetry annotations. Using this annotation can turn retry function.

@EnableAspectJAutoProxy(proxyTargetClass = false)
@Import(RetryConfiguration.class)
public @interface EnableRetry {

It is introduced inside the RetryConfiguration configuration class.

ImportAware Interface

ImportAware interfaces are required and @Import used together. When used as @Import annotation element, you can get to the configuration class data import interface configuration @Import if imported configuration class implements ImportAware interface. Around a bit, we directly on the code.

For example: @EnableAsync comment.

@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
//AsyncConfigurationSelector源码
public class AsyncConfigurationSelector extends AdviceModeImportSelector {

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

The default is to use AdviceMode PROXY, introduced ProxyAsyncConfiguration class.

@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 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.getNumber("order"));
        return bpp;
    }
}

In asyncAdvisor ProxyAsyncConfiguration method needs to acquire the set value of some @EnableAsync, for example: this.enableAsync.getBoolean ( “proxyTargetClass”), this.enableAsync getNumber ( “order”)..

this.enableAsync its parent class AbstractAsyncConfiguration properties. AbstractAsyncConfiguration realized ImportAware interface so you can get to the information on the @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());
    }
}

This example may be a bit more complicated, there is a slightly simpler example: EnableRedisHttpSession. On this, the reader can see for yourself at the source debug learn about.


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

Leave a Reply