Spring Enable 是什么?
本文内容如有错误、不足之处,欢迎技术爱好者们一同探讨,在本文下面讨论区留言,感谢。
文章目录
- 简述
- 分类
- 核心类
- 实现
- 原理
- @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 继承 AdviceModeImportSelector ,AdviceModeImportSelector 实现 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 是什么?相关推荐
- Spring Enable*高级应用及原理
转载自 Spring Enable*高级应用及原理 Enable* 之前的文章用到了一些Enable*开头的注解,比如EnableAsync.EnableScheduling.EnableAspect ...
- How those spring enable annotations work
出处:http://blog.fawnanddoug.com/2012/08/how-those-spring-enable-annotations-work.html(链接已失效,我从Google快 ...
- Spring Enable annotation – writing a custom Enable annotation
原文地址:https://www.javacodegeeks.com/2015/04/spring-enable-annotation-writing-a-custom-enable-annotati ...
- How those spring enable annotations work--转
原文地址:http://blog.fawnanddoug.com/2012/08/how-those-spring-enable-annotations-work.html Spring's Java ...
- Spring Enable* 注解
Spring提供了一系列以Enable开头的注解,这些注解本质上是激活Spring的某些管理功能.比如,EnableWebMvc. 这个注解引入了MVC框架在Spring 应用中需要用到的所有bean ...
- Spring Enable批注–编写自定义的Enable批注
Spring提供了一系列名称以Enable *开头的注释,这些注释本质上使某些Spring管理的功能可以被激活. 这样的注释的一个很好的例子是EnableWebMvc ,它引入了在基于Spring的应 ...
- 最新 Spring 系列教程,都在这了
转载自 最新 Spring 系列教程,都在这了 Spring Boot 系列 什么是 Spring Boot? 公司不用 Spring Boot,果断离职了! 告诉你,Spring Boot 真是个 ...
- Spring MVC Boot Cloud 技术教程汇总
转载自 Spring MVC & Boot & Cloud 技术教程汇总 昨天我们发布了Java成神之路上的知识汇总,今天继续. Java成神之路技术整理(长期更新) 以下是Java技 ...
- Spring MVC Boot Cloud 技术教程汇总(长期更新)
昨天我们发布了Java成神之路上的知识汇总,今天继续. Java成神之路技术整理(长期更新) 以下是Java技术栈微信公众号发布的关于 Spring/ Spring MVC/ Spring Boot/ ...
最新文章
- 如何促使团队紧密协作
- 深度学习和目标检测系列教程 1-300:什么是对象检测和常见的8 种基础目标检测算法
- VTK:图像理想高通用法实战
- Go 标准库: log
- [视频教程]用Unity3d开发跳一跳小游戏
- Linux之python3编译安装
- Cesium:实现漫游飞行
- sony 播放器 android,WALKMAN × 安卓9.0,索尼 NW-ZX505 播放器体验
- 《代码大全》读书笔记之一
- 闲暇之余,纪录片推荐(B站)
- 《经济学人》深度报道:DeepMind和谷歌的AI拉锯战
- [含论文+源码等]SSH超市进销存管理系统
- matlab angle函数
- 窄告:超越搜索引擎关键词模式的精准营销
- (一)python网络爬虫(理论+实战)——爬虫的初步认识
- 高通-LCD驱动框架简述
- 数字多波束相控阵基础知识
- 算法--冒泡,选择,二分
- 计算机界顶流!不懂计算机却被誉为“互联网第一夫人”?
- 高德地图机器学习岗位面试问题
热门文章
- 学习 Bootstrap 5 之 Typography
- boltzmann_推荐系统系列第7部分:用于协同过滤的Boltzmann机器的3个变体
- Docker 03 镜像命令
- 计算机材料学的各种方法,计算机在材料学中的应用.docx
- 出行行业计价模块的设计模式实践
- 标注的尺寸避让lisp_Revit二次开发——标记/标注自动避让思路分享
- VMware--启动服务
- 字体 - 收藏集 - 掘金
- python语言程序设计基础(第2版) 嵩天 礼欣 黄天羽 著
- Ubuntu 18.04 ———(Intel RealSense D435i)安装kalibr + 双目视觉与IMU标定(2022年)