美文网首页Spring
Spring组合注解与@Enable*注解

Spring组合注解与@Enable*注解

作者: elijah777 | 来源:发表于2019-06-10 11:15 被阅读0次

    Spring组合注解与@Enable*注解

    这篇文章主要谈论Spring中的注解,在近端发型的新版本的都是以注解为主,代替了xml配置,这也简化了开发的代码量。

    以及开启某项功能的支持用的@Enable*等,是如何实现的。

    组合注解

    如果一个类或者方法前使用多个注解,也会有些繁琐,Spring设计原则中就是要消除代码

    元注解就是可以注解到别的注解上的注解,被注解的注解称为组合注解,Spring很多注解可以作为元注解,而且有很多的组合注解,如@Configuration就是一个组合@Component注解,

    代码示例

    首先写的小例子字演示一下,通过常用的组合Configuration和ComponentScan元注解为@MyConfiguration

    自定义注解

    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    ​
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    ​
    /**
     * 组合Configuration和ComponentScan元注解
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Configuration
    @ComponentScan
    public @interface MyConfiguration {
     // 覆盖value参数
     String[] value() default {};
    }
    

    配置类

    /**
     * 使用新的注释代替原有 Configuration 和ComponentScan
     */
    @MyConfiguration("con.ch3.annotation")
    public class DemoConfig {
    }
    

    服务类

    @Service
    public class DemoService {
    ​
     public void outputResult() {
     System.out.println("通过组合注释配置获得Bean");
     }
    }
    

    方法调用入口

    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    ​
    /**
     * @description:
     * @author: Shenshuaihu
     * @version: 1.0
     * @data: 2019-06-03 22:17
     */
    public class AnnotationMain {
     public static void main(String[] args) {
     AnnotationConfigApplicationContext context =
     new AnnotationConfigApplicationContext(DemoConfig.class);
     DemoService service = context.getBean(DemoService.class);
     service.outputResult();
     context.close();
     }
    }
    

    打印结果

    onnected to the target VM, address: '127.0.0.1:58117', transport: 'socket'
    六月 10, 2019 10:36:25 上午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
    信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@64616ca2: startup date [Mon Jun 10 10:36:25 CST 2019]; root of context hierarchy
    六月 10, 2019 10:36:26 上午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
    信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@64616ca2: startup date [Mon Jun 10 10:36:25 CST 2019]; root of context hierarchy
    通过组合注释配置获得Bean
    Disconnected from the target VM, address: '127.0.0.1:58117', transport: 'socket'
    ​
    Process finished with exit code 0
    

    这样便可以用自定义的注解代替类或者方法比较繁杂的注解,一般公司的研发框架回大量使用这样的组合注解

    @Enable*

    接下来再介绍另外一种开启某方法的注解,@EnableAsync ,@EnableScheduling等等,分为三类不同的方法,开启特定的功能

    第一类直接导入配置类
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import org.springframework.context.annotation.Import;
    import org.springframework.scheduling.annotation.SchedulingConfiguration;
    ​
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Import({SchedulingConfiguration.class})
    @Documented
    public @interface EnableScheduling {
    }
    
    package org.springframework.scheduling.annotation;
    ​
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Role;
    import org.springframework.scheduling.config.TaskManagementConfigUtils;
    ​
    /**
     * {@code @Configuration} class that registers a {@link
     * ScheduledAnnotationBeanPostProcessor} bean capable of processing Spring's @{@link
     * Scheduled} annotation.
     *
     * <p>This configuration class is automatically imported when using the @{@link
     * EnableScheduling} annotation.  See {@code @EnableScheduling} Javadoc for complete usage
     * details.
     *
     * @author Chris Beams
     * @since 3.1
     * @see EnableScheduling
     * @see ScheduledAnnotationBeanPostProcessor
     */
    @Configuration
    public class SchedulingConfiguration {
    ​
     @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
     @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
     public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
     return new ScheduledAnnotationBeanPostProcessor();
     }
    ​
    }
    

    该注释首先导入了 SchedulingConfiguration,开启了 Configuration,注册了 ScheduledAnnotationBeanPostProcessor Bean

    第二类依据条件选择配置类
    import java.lang.annotation.Annotation;
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import org.springframework.context.annotation.AdviceMode;
    import org.springframework.context.annotation.Import;
    import org.springframework.scheduling.annotation.AsyncConfigurationSelector;
    ​
    /**
     *      第二类:依据条件选择配置类
     *          AsyncConfigurationSelector 通过条件来选择需要导入的配置类
     *          继承 AdviceModeImportSelector  又实现了ImportSelector接口
     *           接口重写selectImports方法 进行事先条件判断
     *            PROXY 或者 ASPECTJ 选择不同的配置类
     *
     * @author ShenShuaihu
     * @version 1.0
     * @date  23:26
     */
    ​
    @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 2147483647;
    }
    

    AsyncConfigurationSelector类

    package org.springframework.scheduling.annotation;
    ​
    import org.springframework.context.annotation.AdviceMode;
    import org.springframework.context.annotation.AdviceModeImportSelector;
    ​
    /**
     * Selects which implementation of {@link AbstractAsyncConfiguration} should be used based
     * on the value of {@link EnableAsync#mode} on the importing {@code @Configuration} class.
     *
     * @author Chris Beams
     * @since 3.1
     * @see EnableAsync
     * @see ProxyAsyncConfiguration
     */
    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;
     }
     }
    ​
    }
    

    ImportSelector 接口

    package org.springframework.context.annotation;
    ​
    import org.springframework.core.type.AnnotationMetadata;
    ​
    public interface ImportSelector {
    ​
     /**
     * Select and return the names of which class(es) should be imported based on
     * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
     */
     String[] selectImports(AnnotationMetadata importingClassMetadata);
    ​
    }
    

    接口实现的方法

    @Override
    public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
     Class<?> annoType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
     AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
     Assert.notNull(attributes, String.format(
     "@%s is not present on importing class '%s' as expected",
     annoType.getSimpleName(), importingClassMetadata.getClassName()));
    ​
     AdviceMode adviceMode = attributes.getEnum(this.getAdviceModeAttributeName());
     String[] imports = selectImports(adviceMode);
     Assert.notNull(imports, String.format("Unknown AdviceMode: '%s'", adviceMode));
     return imports;
    }
    ​
    //  selectImports 方法
    ​
    @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;
     }
     }
    

    AsyncConfigurationSelector 通过条件来选择需要导入的配置类, 继承 AdviceModeImportSelector 又实现了ImportSelector接口,接口重写selectImports方法 进行事先条件判断 PROXY 或者 ASPECTJ 选择不同的配置类

    第三类动态注册Bean
    import org.springframework.context.annotation.Import;
    ​
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    ​
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(AspectJAutoProxyRegistrar.class)
    public @interface EnableAspectJAutoProxy {
    ​
     boolean proxyTargetClass() default false;
    ​
    }
    

    AspectJAutoProxyRegistrar类

    import org.springframework.aop.config.AopConfigUtils;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.annotation.AnnotationConfigUtils;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.annotation.AnnotationAttributes;
    import org.springframework.core.type.AnnotationMetadata;
    ​
    /**
     * Registers an {@link org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
     * AnnotationAwareAspectJAutoProxyCreator} against the current {@link BeanDefinitionRegistry}
     * as appropriate based on a given @{@link EnableAspectJAutoProxy} annotation.
     *
     * @author Chris Beams
     * @since 3.1
     * @see EnableAspectJAutoProxy
     */
    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 enableAJAutoProxy =
     AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
     if (enableAJAutoProxy.getBoolean("proxyTargetClass")) {
     AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
     }
     }
    ​
    }
    

    registerOrEscalateApcAsRequired方法

    private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
     Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
     if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
     BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
     if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
     int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
     int requiredPriority = findPriorityForClass(cls);
     if (currentPriority < requiredPriority) {
     apcDefinition.setBeanClassName(cls.getName());
     }
     }
    ​
     return null;
     } else {
     RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
     beanDefinition.setSource(source);
     beanDefinition.getPropertyValues().add("order", -2147483648);
     beanDefinition.setRole(2);
     registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
     return beanDefinition;
     }
    }
    

    AspectJAutoProxyRegistrar 事先实现了ImportBeanDefinitionRegistrar 接口

    作用是在运行时自动添加Bean到已有的配置类,通过重写 registerBeanDefinitions(AnnotationMetadata var1, BeanDefinitionRegistry var2)

    AnnotationMetadata 获取当前配置类上的注解,BeanDefinitionRegistry 用来注册Bean

    更多参考:

    Springboot @Enable*注解的工作原理 https://www.jianshu.com/p/1241c1079dd6

    相关文章

      网友评论

        本文标题:Spring组合注解与@Enable*注解

        本文链接:https://www.haomeiwen.com/subject/ctgaxctx.html