Spring 3.1新特性之二:@Enable*注解的源码以及很多enable注解

@enable*注解
分析SpringBoot的自动化配置原理的时候,可以观察下这些@Enable*注解的源码,可以发现所有的注解都有一个@Import注解。@Import注解是用来导入配置类的,这也就是说这些自动开启的实现其实是导入了一些自动配置的Bean。
如:freemarker的自动化配置类FreeMarkerAutoConfiguration,这个自动化配置类需要classloader中的一些类需要存在并且在其他的一些配置类之后进行加载。

但是还存在一些自动化配置类,它们需要在使用一些注解开关的情况下才会生效。比如spring-boot-starter-batch里的@EnableBatchProcessing注解、@EnableCaching等。
一、自动注入例子
定义一个Annotation,EnableContentService 让使用了这个注解的类地注入一些类或者做一些底层的事情。
使用Spring提供的@Import注解配合一个配置类来完成。

@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");
    }
}

然后在应用程序的入口加上@EnableContentService注解。

这样的话,ContentService就被注入进来了。 SpringBoot也就是用这个完成的。只不过它用了更加高级点的ImportSelector。
@Import的使用方法。
第一类:直接导入配置类
例如,@EnableScheduling中直接导入配置类SchedulingConfiguration,这个类注解了@Configuration,且注册了一个scheduledAnnotationProcessor的Bean

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

第二类:依据条件选择配置类
例如在@EnableAsync中,通过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通过条件来选择需要导入的配置类,AsyncConfigurationSelector的根接口为ImportSelector,这个接口需要重写selectImports方法,在此方法内进行事先条件判断。

若adviceMode为PORXY,则返回ProxyAsyncConfiguration这个配置类。

若activeMode为ASPECTJ,则返回AspectJAsyncConfiguration配置类。

关键方法如下:

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

}

第三类:动态注册Bean
spring中的EnableAspectJAutoProxy.java

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

AspectJAutoProxyRegistrar 实现了ImportBeanDefinitionRegistrar接口,ImportBeanDefinitionRegistrar的作用是在运行时自动添加Bean到已有的配置类,通过重写方法:

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

}

其中,AnnotationMetadata参数用来获得当前配置类上的注解;

BeanDefinittionRegistry参数用来注册Bean。

ImportSelector在SpringBoot中的使用
SpringBoot里的ImportSelector是通过SpringBoot提供的@EnableAutoConfiguration这个注解里完成的。

这个@EnableAutoConfiguration注解可以显式地调用,否则它会在@SpringBootApplication注解中隐式地被调用。
@EnableAutoConfiguration注解中使用了EnableAutoConfigurationImportSelector作为ImportSelector。下面这段代码就是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);
    }
}

其中getCandidateConfigurations方法将获取配置类:

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

SpringFactoriesLoader.loadFactoryNames方法会根据FACTORIES_RESOURCE_LOCATION这个静态变量从所有的jar包中读取META-INF/spring.factories文件信息:

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

getCandidateConfigurations方法中的getSpringFactoriesLoaderFactoryClass方法返回的是EnableAutoConfiguration.class,所以会过滤出key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值。

下面这段配置代码就是autoconfigure这个jar包里的spring.factories文件的一部分内容(有个key为org.springframework.boot.autoconfigure.EnableAutoConfiguration,所以会得到这些AutoConfiguration):
在这里插入图片描述

# 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,\......

当然了,这些AutoConfiguration不是所有都会加载的,会根据AutoConfiguration上的@ConditionalOnClass等条件判断是否加载。

上面这个例子说的读取properties文件的时候只会过滤出key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值。

SpringBoot内部还有一些其他的key用于过滤得到需要加载的类:

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

spring中的@Enable*

@EnableAspectJAutoProxy
@EnableAspectJAutoProxy注解 激活Aspect自动代理,使用@EnableAspectJAutoProxy相当于<aop:aspectj-autoproxy />开启对AspectJ自动代理的支持。

@EnableAsync
@EnableAsync注解开启异步方法的支持。

见《@Async实现异步调用》添加链接描述

@EnableScheduling
@EnableScheduling注解开启计划任务的支持。

示例见《Spring的@Scheduled任务调度》添加链接描述

@EnableWebMVC
@EnableWebMVC注解用来开启Web MVC的配置支持。

也就是写Spring MVC时的时候会用到。

@EnableConfigurationProperties
@EnableConfigurationProperties注解是用来开启对@ConfigurationProperties注解配置Bean的支持。

@EnableJpaRepositories
@EnableJpaRepositories注解开启对Spring Data JPA Repostory的支持。
Spring Data JPA 框架,主要针对的就是 Spring 唯一没有简化到的业务逻辑代码,至此,开发者连仅剩的实现持久层业务逻辑的工作都省了,唯一要做的,就只是声明持久层的接口,其他都交给 Spring Data JPA 来帮你完成!
简单的说,Spring Data JPA是用来持久化数据的框架。

@EnableTransactionManagement
@EnableTransactionManagement注解开启注解式事务的支持。

注解@EnableTransactionManagement通知Spring,@Transactional注解的类被事务的切面包围。这样@Transactional就可以使用了。

@EnableCaching
@EnableCaching注解开启注解式的缓存支持

猜你喜欢

转载自blog.csdn.net/u010010600/article/details/114117469