Spring Enable 是什么?

本文深入探讨了Spring框架中@Enable注解的原理,包括@Import、@Configuration、ImportSelector和ImportBeanDefinitionRegistrar的作用及其实现机制。通过实例展示了如何自定义@Enable注解,为开发者提供了理解和应用@Enable系列注解的全面指南。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

简述

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

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

分类

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

序号注解名作用
0EnableScheduling开启计划任务的支持
1EnableAsync开启异步方法的支持
2EnableAspectJAutoProxy开启对 AspectJ 代理的支持
3EnableTransactionManagement开启对事务的支持
4EnableCaching开启对注解式缓存的支持
5EnableWebMvc开启对Spring MVC的支持
6EnableWebSocket开启网络套接字请求的处理
核心类
  • @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 继承 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自动代理创建器配置类。
	 */
	@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拆箱::)))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值