本文内容如有错误、不足之处,欢迎技术爱好者们一同探讨,在本文下面讨论区留言,感谢。
文章目录
- 简述
- 分类
- 核心类
- 实现
- 原理
- @Import 的作用
- @Configuration和@Import
- ImportSelector和@Import
- ImportBeanDefinitionRegistrar和@Import
- @Import和ImportSelector和ImportBeanDefinitionRegistrar
- 例子
- @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
@Nullable
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;
}
}
}
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;
}
@Override
public 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;
}
@Nullable
protected 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自动代理创建器配置类。
*/
@Override
public 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 {
@Bean
public String beanA() {
return "beanA";
}
@Bean
public 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 {
@Bean
public String aBean() {
return "TypeA";
}
}
@Configuration
class SomeBeanConfigurationDefault {
@Bean
public 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注解的所有属性的value
AnnotationAttributes attributes =
AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes
(EnableSomeBeansSelector.class.getName(), false));
//获取criteria属性的value
String 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*注解的工作原理
Spring Boot 自动配置之@Enable* 与@Import注解
Quick Guide to the Spring @Enable Annotations(Spring @Enable注解快速指南)
Spring Enable annotation – writing a custom Enable annotation(Spring Enable注解–编写自定义的Enable注解)
今天室友AJ拆箱::)))