Spring 3.1 new feature two: @Enable* annotation source code and many enable annotations

@enable* annotation When
analyzing SpringBoot's automatic configuration principle, you can observe the source code of these @Enable* annotations, and you can find that all annotations have an @Import annotation. The @Import annotation is used to import configuration classes, which means that these auto-open implementations actually import some auto-configured Beans.
Such as: FreeMarker's automatic configuration class FreeMarkerAutoConfiguration, this automatic configuration class requires some classes in the classloader to exist and be loaded after some other configuration classes.

But there are also some automated configuration classes, which will only take effect when some annotation switches are used. For example, @EnableBatchProcessing annotation and @EnableCaching in spring-boot-starter-batch.
1. An example of automatic injection
Define an Annotation, EnableContentService allows the class using this annotation to inject some classes or do some low-level things.
Use the @Import annotation provided by Spring with a configuration class to complete.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(ContentConfiguration.class)
public @interface EnableContentService {
    
    }

public interface ContentService {
    
    
    void doSomething();
}

public class SimpleContentService implements ContentService {
    
    
    @Override
    public void doSomething() {
    
    
        System.out.println("do some simple things");
    }
}

Then add the @EnableContentService annotation to the entry of the application.

In this case, ContentService is injected. SpringBoot is also done with this. It just uses the more advanced ImportSelector.
How to use @Import.
The first category: directly import the configuration class.
For example, @EnableScheduling directly imports the configuration class SchedulingConfiguration, this class is annotated with @Configuration, and a Bean of scheduledAnnotationProcessor is registered

@Target({
    
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({
    
    SchedulingConfiguration.class})
@Documented
public @interface EnableScheduling {
    
    
}

The second type: select the configuration class according to the conditions.
For example, in @EnableAsync, the configuration class configuration is selected by AsyncConfigurationSelector.class.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
    
    
    Class<? extends Annotation> annotation() default Annotation.class;
    boolean proxyTargetClass() default false;
    AdviceMode mode() default AdviceMode.PROXY;
    int order() default Ordered.LOWEST_PRECEDENCE;

}

AsyncConfigurationSelector selects the configuration class that needs to be imported through conditions. The root interface of AsyncConfigurationSelector is ImportSelector. This interface needs to rewrite the selectImports method, and pre-condition judgments are made in this method.

If the adviceMode is PORXY, the configuration class ProxyAsyncConfiguration is returned.

If activeMode is ASPECTJ, the AspectJAsyncConfiguration configuration class is returned.

The key methods are as follows:

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
    
    

    private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
            "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";

    /**
     * {@inheritDoc}
     * @return {@link ProxyAsyncConfiguration} or {@code AspectJAsyncConfiguration} for
     * {@code PROXY} and {@code ASPECTJ} values of {@link EnableAsync#mode()}, respectively
     */
    @Override
    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 third category: Dynamic registration
of EnableAspectJAutoProxy.java in Bean spring

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    
    
    boolean proxyTargetClass() default false;
    boolean exposeProxy() default false;
}

AspectJAutoProxyRegistrar implements the ImportBeanDefinitionRegistrar interface. The role of ImportBeanDefinitionRegistrar is to automatically add Bean to the existing configuration class at runtime, by rewriting the method:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    
    

    /**
     * Register, escalate, and configure the AspectJ auto proxy creator based on the value
     * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
     * {@code @Configuration} class.
     */
    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
    

        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
    
    
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }
        if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
    
    
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
        }
    }

}

Among them, the AnnotationMetadata parameter is used to obtain the annotations on the current configuration class;

The BeanDefinittionRegistry parameter is used to register the Bean.

The use of ImportSelector in SpringBoot The ImportSelector in SpringBoot
is completed through the annotation @EnableAutoConfiguration provided by SpringBoot.

This @EnableAutoConfiguration annotation can be called explicitly, otherwise it will be implicitly called in the @SpringBootApplication annotation.
@EnableAutoConfiguration annotation uses EnableAutoConfigurationImportSelector as ImportSelector. The following code is the specific code for selection in EnableAutoConfigurationImportSelector:

@Override
public String[] selectImports(AnnotationMetadata metadata) {
    
    
    try {
    
    
        AnnotationAttributes attributes = getAttributes(metadata);
        List<String> configurations = getCandidateConfigurations(metadata,
                attributes);
        configurations = removeDuplicates(configurations); // 删除重复的配置
        Set<String> exclusions = getExclusions(metadata, attributes); // 去掉需要exclude的配置
        configurations.removeAll(exclusions);
        configurations = sort(configurations); // 排序
        recordWithConditionEvaluationReport(configurations, exclusions);
        return configurations.toArray(new String[configurations.size()]);
    }
    catch (IOException ex) {
    
    
        throw new IllegalStateException(ex);
    }
}

The getCandidateConfigurations method will get the configuration class:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
        AnnotationAttributes attributes) {
    
    
    return SpringFactoriesLoader.loadFactoryNames(
            getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
}

The SpringFactoriesLoader.loadFactoryNames method will read the META-INF/spring.factories file information from all jar packages according to the static variable FACTORIES_RESOURCE_LOCATION:

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    
    
    String factoryClassName = factoryClass.getName();
    try {
    
    
        Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        List<String> result = new ArrayList<String>();
        while (urls.hasMoreElements()) {
    
    
            URL url = urls.nextElement();
            Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
            String factoryClassNames = properties.getProperty(factoryClassName); // 只会过滤出key为factoryClassNames的值
            result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
        }
        return result;
    }
    catch (IOException ex) {
    
    
        throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
                "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

The getSpringFactoriesLoaderFactoryClass method in the getCandidateConfigurations method returns EnableAutoConfiguration.class, so the value of the key org.springframework.boot.autoconfigure.EnableAutoConfiguration will be filtered out.

The following configuration code is part of the spring.factories file in the autoconfigure jar package (there is a key of org.springframework.boot.autoconfigure.EnableAutoConfiguration, so you will get these AutoConfigurations):
Insert picture description here

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\......

Of course, not all of these AutoConfigurations will be loaded. It will be determined whether to load according to conditions such as @ConditionalOnClass on AutoConfiguration.

In the above example, when reading the properties file, only the value whose key is org.springframework.boot.autoconfigure.EnableAutoConfiguration will be filtered out.

There are some other keys inside SpringBoot that are used to filter the classes that need to be loaded:

org.springframework.test.context.TestExecutionListener

org.springframework.beans.BeanInfoFactory

org.springframework.context.ApplicationContextInitializer

org.springframework.context.ApplicationListener

org.springframework.boot.SpringApplicationRunListener

org.springframework.boot.env.EnvironmentPostProcessor

org.springframework.boot.env.PropertySourceLoader

@Enable * in spring

@EnableAspectJAutoProxy
@EnableAspectJAutoProxy annotation activates Aspect automatic proxy, using @EnableAspectJAutoProxy is equivalent to <aop:aspectj-autoproxy /> to enable AspectJ automatic proxy support.

@EnableAsync
@EnableAsync annotation enables support for asynchronous methods.

See "@Async Implement Asynchronous Calls" to add a link description

@EnableScheduling
@EnableScheduling annotation enables support for scheduled tasks.

For examples, see "Spring's @Scheduled Task Scheduling" to add a link description

@EnableWebMVC
@EnableWebMVC annotation is used to enable Web MVC configuration support.

It is used when writing Spring MVC.

@EnableConfigurationProperties
@EnableConfigurationProperties annotation is used to enable support for @ConfigurationProperties annotation configuration beans.

@EnableJpaRepositories
@EnableJpaRepositories annotation enables support for Spring Data JPA Repostory.
The Spring Data JPA framework is mainly aimed at the only business logic code that Spring has not simplified. So far, developers have saved the remaining work to implement the business logic of the persistence layer. The only thing to do is to declare the interface of the persistence layer. , And Spring Data JPA will do the rest for you!
Simply put, Spring Data JPA is a framework for persisting data.

@EnableTransactionManagement
@EnableTransactionManagement annotation enables support for annotated transactions.

The annotation @EnableTransactionManagement informs Spring that the @Transactional annotated class is surrounded by transactional aspects. Then @Transactional can be used.

@EnableCaching
@EnableCaching annotations enable annotation-style caching support

Guess you like

Origin blog.csdn.net/u010010600/article/details/114117469