本文内容如有错误、不足之处,欢迎技术爱好者们一同探讨,在本文下面讨论区留言,感谢。

文章目录

  • 简述
  • 分类
  • 核心类
  • 实现
  • 原理
    • @Import 的作用
    • @Configuration和@Import
      • @EnableScheduling
    • ImportSelector和@Import
      • @EnableAsync
    • ImportBeanDefinitionRegistrar和@Import
      • @EnableAspectJAutoProxy
    • @Import和ImportSelector和ImportBeanDefinitionRegistrar
      • @EnableConfigurationProperties
  • 例子
    • @Import和@Configuration
    • ImportSelector和@Import
    • ImportBeanDefinitionRegistrar和@Import
    • @Import和ImportSelector和ImportBeanDefinitionRegistrar
    • 自定义注解使用方式
  • 结论
  • 参考资料

简述

Spring 提供了一系列名称以 *Enable * 开头的注释,这些注释本质上激活对应的 Spring 管理的功能。

一个很好的例子是 EnableWebMvc,它引入了在基于 Spring 的应用程序中支持 MVC 流所需的所有 Bean。另一个很好的例子是 EnableAsync ,用于激活 Bean 以支持基于 Spring 的应用程序中的异步功能。

分类

下面表格列举部分 Enable 开头的注解

序号 注解名 作用
0 EnableScheduling 开启计划任务的支持
1 EnableAsync 开启异步方法的支持
2 EnableAspectJAutoProxy 开启对 AspectJ 代理的支持
3 EnableTransactionManagement 开启对事务的支持
4 EnableCaching 开启对注解式缓存的支持
5 EnableWebMvc 开启对Spring MVC的支持
6 EnableWebSocket 开启网络套接字请求的处理

核心类

  • @Import
  • @Configuration
  • ImportSelector
  • ImportBeanDefinitionRegistrar

实现

目前 Spring 框架实现 Enable 模式主要有三种:

  • 直接导入确定配置类:@Import + @Configuration 组合

    • 明确知道引入哪个配置类
  • 依据条件选择配置类: @Import + ImportSelector 组合
    • 不明确知道引入哪个配置类,或者需要一定逻辑进行判断引入哪个配置类
  • 自定义注册选择配置类:@Import + ImportBeanDefinitionRegistrar 组合
    • 不明确知道引入哪个配置类,或者以后会增加配置类

当然,还可以进行混合组合:

@Import + ImportSelector + ImportBeanDefinitionRegistrar 组合

原理

@Import 的作用

definitions declared in imported @Configuration classes should be accessed by using @Autowired injection. Either the bean itself can be autowired, or the configuration class instance declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly navigation between @Configuration class methods.

导入的 @Configuration 类中声明的定义应使用 @Autowired 注入进行访问。 Bean 本身可以自动装配,也可以配置类实例
声明可以自动装配 bean 。后一种方法允许显式,IDE友好的通过 @Configuration 类方法之间的注解。

@Import 注解用来导入一个或多个 class,这些类会注入到 Spring 容器中,或者配置类,配置类里面定义的 bean 都会被 Spring 容器托管,成为 Spring 上下文的一部分 。

@Configuration和@Import
@EnableScheduling

@EnableScheduling 是这两个组合实现的实例:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {}
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {return new ScheduledAnnotationBeanPostProcessor();}
}
ImportSelector和@Import
@EnableAsync

@EnableAsync 是这两个组合实现的实例:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {...
}
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME ="org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";/*** * 返回 ProxyAsyncConfiguration 或者 AspectJAsyncConfiguration 作为 EnableAsync#mode() 的值* 根据 adviceMode 参数的值进行判断*/@Override@Nullablepublic 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;}}}
public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector {public static final String DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME = "mode";protected String getAdviceModeAttributeName() {return DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME;}@Overridepublic final String[] selectImports(AnnotationMetadata importingClassMetadata) {Class<?> annType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);Assert.state(annType != null, "Unresolvable type argument for AdviceModeImportSelector");AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);if (attributes == null) {throw new IllegalArgumentException(String.format("@%s is not present on importing class '%s' as expected",annType.getSimpleName(), importingClassMetadata.getClassName()));}AdviceMode adviceMode = attributes.getEnum(this.getAdviceModeAttributeName());String[] imports = selectImports(adviceMode);if (imports == null) {throw new IllegalArgumentException(String.format("Unknown AdviceMode: '%s'", adviceMode));}return imports;}@Nullableprotected abstract String[] selectImports(AdviceMode adviceMode);}

这里可以看到 AsyncConfigurationSelector 继承 AdviceModeImportSelectorAdviceModeImportSelector 实现 ImportSelector

public interface ImportSelector {/*** 根据导入的 Configuration 类的 AnnotationMetadata 选择并返回应导入的类的名称* @param AnnotationMetadata:用来获得当前配置类上的注解* @return class的全类名的字符串数组* /String[] selectImports(AnnotationMetadata importingClassMetadata);
}

Spring 会把实现 ImportSelector 接口的类中的 SelectImport 方法返回的值注入到 Spring 容器中。
例如,在 @EnableAsync 注解的 Bean 中 ,将会把 ProxyAsyncConfiguration 或者 AspectJAsyncConfiguration 注入到 Spring 容器中。

ImportBeanDefinitionRegistrar和@Import
@EnableAspectJAutoProxy

@EnableAspectJAutoProxy 是这两个组合实现的实例

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {boolean proxyTargetClass() default false;boolean exposeProxy() default false;}
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {/*** 根据导入时启用AspectJ自动代理属性的值注册,升级和配置AspectJ自动代理创建器配置类。*/@Overridepublic 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);}}}
}

这里可以看到 AspectJAutoProxyRegistrar 实现 ImportBeanDefinitionRegistrar 接口。

public interface ImportBeanDefinitionRegistrar {/*** 根据导入的Configuration类的给定配置类上的注解,注册Bean。请注意,由于与Configuration类相关的生命周期限                        * 制,BeanDefinitionRegistryPostProcessor 在运行时自动添加Bean到已有的配置类。* @param importingClassMetadata 获得当前配置类上的注解* @param registry 注册Bean*/public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);}

下面是 AspectJAutoProxyRegistrar 实现通过 @EnableAspectJAutoProxy 注解中的信息进行判断的逻辑,这里可以参照进行相对应的业务改写。

if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
@Import和ImportSelector和ImportBeanDefinitionRegistrar
@EnableConfigurationProperties

@EnableAspectJAutoProxy 是这三个组合实现的实例

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({EnableConfigurationPropertiesImportSelector.class})
public @interface EnableConfigurationProperties {Class<?>[] value() default {};
}
class EnableConfigurationPropertiesImportSelector implements ImportSelector {private static final String[] IMPORTS = new String[]{EnableConfigurationPropertiesImportSelector.ConfigurationPropertiesBeanRegistrar.class.getName(), ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName()};EnableConfigurationPropertiesImportSelector() {}public String[] selectImports(AnnotationMetadata metadata) {return IMPORTS;}public static class ConfigurationPropertiesBeanRegistrar implements ImportBeanDefinitionRegistrar {public ConfigurationPropertiesBeanRegistrar() {}public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {this.getTypes(metadata).forEach((type) -> {this.register(registry, (ConfigurableListableBeanFactory)registry, type);});}...}
}

这里可以看到 EnableConfigurationPropertiesImportSelector 实现 ImportSelector 接口,同时有个内部静态类 ConfigurationPropertiesBeanRegistrar 实现 ImportBeanDefinitionRegistrar 接口,然后通过 ImportSelector#selectImports 方法将 ConfigurationPropertiesBeanRegistrar 注入。

例子

@Import和@Configuration

EnableSomeBeans 注解

import org.springframework.context.annotation.Import;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 开启自动注入一些Beans注解*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(SomeBeanConfiguration.class)
public @interface EnableSomeBeans {}

SomeBeanConfiguration.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 注入一些Bean的配置*/
@Configuration
public class SomeBeanConfiguration {@Beanpublic String beanA() {return "beanA";}@Beanpublic String beanB() {return "beanA";}
}
ImportSelector和@Import

定义 EnableSomeBeansSelector 注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(SomeBeanConfigurationSelector.class)
public @interface EnableSomeBeansSelector {String criteria() default "default";
}

实现 SomeBeanConfigurationSelector 类,此类实现了 ImportSelector 接口

blic class SomeBeanConfigurationSelector implements ImportSelector {public String[] selectImports(AnnotationMetadata importingClassMetadata) {AnnotationAttributes attributes =AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableSomeBeansSelector.class.getName(), false));String criteria = attributes.getString("criteria");if (criteria.equals("default")) {return new String[]{"com.enable.selectorDemo.SomeBeanConfigurationDefault"};}else {return new String[]{"com.enable.selectorDemo.SomeBeanConfigurationTypeA"};}}
}@Configuration
class SomeBeanConfigurationTypeA {@Beanpublic String aBean() {return "TypeA";}}@Configuration
class SomeBeanConfigurationDefault {@Beanpublic String aBean() {return "Default";}
}
ImportBeanDefinitionRegistrar和@Import

定义 EnableSomeBeansSelector 注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(SomeBeanConfigurationRegistrar.class)
public @interface EnableSomeBeansSelector {String criteria() default "default";
}

实现 SomeBeanConfigurationRegistrar

public class SomeBeanConfigurationRegistrar implements ImportBeanDefinitionRegistrar {public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {//获取EnableSomeBeansSelector注解的所有属性的valueAnnotationAttributes attributes =AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableSomeBeansSelector.class.getName(), false));//获取criteria属性的valueString criteria = attributes.getString("criteria");if (criteria.equals("default")) {this.registerConfigurationDefault(registry);}else {this.registerConfigurationTypeA(registry);}}private void registerConfigurationDefault(BeanDefinitionRegistry registry) {GenericBeanDefinition definition = new GenericBeanDefinition();definition.setBeanClass(SomeBeanConfigurationDefault.class);definition.setRole(2);registry.registerBeanDefinition(SomeBeanConfigurationDefault.class.getName(), definition);}private void registerConfigurationTypeA(BeanDefinitionRegistry registry) {GenericBeanDefinition definition = new GenericBeanDefinition();definition.setBeanClass(SomeBeanConfigurationTypeA.class);definition.setRole(2);registry.registerBeanDefinition(SomeBeanConfigurationTypeA.class.getName(), definition);}
}class SomeBeanConfigurationTypeA {}class SomeBeanConfigurationDefault {}

这里通过 GenericBeanDefinition 实现类的定义,BeanDefinitionRegistry#registerBeanDefinition 完成对类的注册。

@Import和ImportSelector和ImportBeanDefinitionRegistrar

EnableSomeBeansSelector 注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(SomeBeanConfigurationSelector.class)
public @interface EnableSomeBeansSelector {String criteria() default "default";
}

SomeBeanConfigurationSelector 混合模式

public class SomeBeanConfigurationSelector implements ImportSelector {private static final String[] IMPORTS = new String[]{com.enable.selectorAndRegistarDemo.SomeBeanConfigurationSelector.SomeBeanConfigurationBeanRegistrar.class.getName(), ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName()};SomeBeanConfigurationSelector() {}public String[] selectImports(AnnotationMetadata metadata) {return IMPORTS;}public static class SomeBeanConfigurationBeanRegistrar implements ImportBeanDefinitionRegistrar {public SomeBeanConfigurationBeanRegistrar() {}public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {this.getTypes(metadata).forEach((type) -> {this.register(registry, (ConfigurableListableBeanFactory)registry, type);});}private List<Class<?>> getTypes(AnnotationMetadata metadata) {MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(EnableSomeBeansSelector.class.getName(), false);return this.collectClasses(attributes != null ? (List)attributes.get("criteria") : Collections.emptyList());}private List<Class<?>> collectClasses(List<?> values) {return (List)values.stream().flatMap((value) -> {return Arrays.stream((Object[])((Object[])value));}).map((o) -> {return (Class)o;}).filter((type) -> {return Void.TYPE != type;}).collect(Collectors.toList());}private void register(BeanDefinitionRegistry registry, ConfigurableListableBeanFactory beanFactory, Class<?> type) {String name = this.getName(type);if (!this.containsBeanDefinition(beanFactory, name)) {this.registerBeanDefinition(registry, name, type);}}private String getName(Class<?> type) {ConfigurationProperties annotation = (ConfigurationProperties)AnnotationUtils.findAnnotation(type, ConfigurationProperties.class);String prefix = annotation != null ? annotation.prefix() : "";return StringUtils.hasText(prefix) ? prefix + "-" + type.getName() : type.getName();}private boolean containsBeanDefinition(ConfigurableListableBeanFactory beanFactory, String name) {if (beanFactory.containsBeanDefinition(name)) {return true;} else {BeanFactory parent = beanFactory.getParentBeanFactory();return parent instanceof ConfigurableListableBeanFactory ? this.containsBeanDefinition((ConfigurableListableBeanFactory)parent, name) : false;}}private void registerBeanDefinition(BeanDefinitionRegistry registry, String name, Class<?> type) {this.assertHasAnnotation(type);GenericBeanDefinition definition = new GenericBeanDefinition();definition.setBeanClass(type);registry.registerBeanDefinition(name, definition);}private void assertHasAnnotation(Class<?> type) {Assert.notNull(AnnotationUtils.findAnnotation(type, ConfigurationProperties.class), () -> {return "No " + ConfigurationProperties.class.getSimpleName() + " annotation found on  '" + type.getName() + "'.";});}}
}
自定义注解使用方式
@EnableSomeBeansSelector(criteria = "TypeA")
public class Application {}

注解在对应的 bean 上就可以了。

结论

通过本文内容,研究了一些 @Enable Spring 注解原理,和如何手动创建自定义的 @Enable 注解, 开发人员在实际开发过程中,不需要手动创建 Enable 注解,而是使用 @Configuration 注解和 Spring Bean 配置文件搭建应用程序,这种简单的机制。

代码地址:github

参考资料

Spring-Boot之@Enable*注解的工作原理

  • https://www.jianshu.com/p/3da069bd865c

Spring Boot 自动配置之@Enable* 与@Import注解

  • https://juejin.im/post/5c761c096fb9a049b41d2299

Quick Guide to the Spring @Enable Annotations(Spring @Enable注解快速指南)

  • https://www.baeldung.com/spring-enable-annotations

Spring Enable annotation – writing a custom Enable annotation(Spring Enable注解–编写自定义的Enable注解

  • https://www.javacodegeeks.com/2015/04/spring-enable-annotation-writing-a-custom-enable-annotation.html

今天室友AJ拆箱::)))

Spring Enable 是什么?相关推荐

  1. Spring Enable*高级应用及原理

    转载自 Spring Enable*高级应用及原理 Enable* 之前的文章用到了一些Enable*开头的注解,比如EnableAsync.EnableScheduling.EnableAspect ...

  2. How those spring enable annotations work

    出处:http://blog.fawnanddoug.com/2012/08/how-those-spring-enable-annotations-work.html(链接已失效,我从Google快 ...

  3. Spring Enable annotation – writing a custom Enable annotation

    原文地址:https://www.javacodegeeks.com/2015/04/spring-enable-annotation-writing-a-custom-enable-annotati ...

  4. How those spring enable annotations work--转

    原文地址:http://blog.fawnanddoug.com/2012/08/how-those-spring-enable-annotations-work.html Spring's Java ...

  5. Spring Enable* 注解

    Spring提供了一系列以Enable开头的注解,这些注解本质上是激活Spring的某些管理功能.比如,EnableWebMvc. 这个注解引入了MVC框架在Spring 应用中需要用到的所有bean ...

  6. Spring Enable批注–编写自定义的Enable批注

    Spring提供了一系列名称以Enable *开头的注释,这些注释本质上使某些Spring管理的功能可以被激活. 这样的注释的一个很好的例子是EnableWebMvc ,它引入了在基于Spring的应 ...

  7. 最新 Spring 系列教程,都在这了

    转载自  最新 Spring 系列教程,都在这了 Spring Boot 系列 什么是 Spring Boot? 公司不用 Spring Boot,果断离职了! 告诉你,Spring Boot 真是个 ...

  8. Spring MVC Boot Cloud 技术教程汇总

    转载自 Spring MVC & Boot & Cloud 技术教程汇总 昨天我们发布了Java成神之路上的知识汇总,今天继续. Java成神之路技术整理(长期更新) 以下是Java技 ...

  9. Spring MVC Boot Cloud 技术教程汇总(长期更新)

    昨天我们发布了Java成神之路上的知识汇总,今天继续. Java成神之路技术整理(长期更新) 以下是Java技术栈微信公众号发布的关于 Spring/ Spring MVC/ Spring Boot/ ...

最新文章

  1. 如何促使团队紧密协作
  2. 深度学习和目标检测系列教程 1-300:什么是对象检测和常见的8 种基础目标检测算法
  3. VTK:图像理想高通用法实战
  4. Go 标准库: log
  5. [视频教程]用Unity3d开发跳一跳小游戏
  6. Linux之python3编译安装
  7. Cesium:实现漫游飞行
  8. sony 播放器 android,WALKMAN × 安卓9.0,索尼 NW-ZX505 播放器体验
  9. 《代码大全》读书笔记之一
  10. 闲暇之余,纪录片推荐(B站)
  11. 《经济学人》深度报道:DeepMind和谷歌的AI拉锯战
  12. [含论文+源码等]SSH超市进销存管理系统
  13. matlab angle函数
  14. 窄告:超越搜索引擎关键词模式的精准营销
  15. (一)python网络爬虫(理论+实战)——爬虫的初步认识
  16. 高通-LCD驱动框架简述
  17. 数字多波束相控阵基础知识
  18. 算法--冒泡,选择,二分
  19. 计算机界顶流!不懂计算机却被誉为“互联网第一夫人”?
  20. 高德地图机器学习岗位面试问题

热门文章

  1. 学习 Bootstrap 5 之 Typography
  2. boltzmann_推荐系统系列第7部分:用于协同过滤的Boltzmann机器的3个变体
  3. Docker 03 镜像命令
  4. 计算机材料学的各种方法,计算机在材料学中的应用.docx
  5. 出行行业计价模块的设计模式实践
  6. 标注的尺寸避让lisp_Revit二次开发——标记/标注自动避让思路分享
  7. VMware--启动服务
  8. 字体 - 收藏集 - 掘金
  9. python语言程序设计基础(第2版) 嵩天 礼欣 黄天羽 著
  10. Ubuntu 18.04 ———(Intel RealSense D435i)安装kalibr + 双目视觉与IMU标定(2022年)