说明
Spring或Spring Boot中我们经常会用到很多@EnableXXX 注解,加上这些注解之后我们就可以 ‘启用’ 某一个功能,或者可以使用某些Bean,比如:
@EnableAsync 使用异步执行、
@EnableTransactionManagement 启用事务、
@EnableAutoConfiguration 开启自动装配
等等,那么你知道背后的原理是什么样的吗?本文简要窥探一下。
原理
无论是Spring内建的,还是我们自定义的@Enable 模块,基本就3种实现方式,其目标都是将指定的类定位为Spring Bean:
导入类为@Configuration Class。导入类为 ImportSelector实现;导入类为 ImportBeanDefinitionRegistrar实现;实例1:
引导类为 @Configuration ,声明Enable注解
<code>@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(HelloworldConfiguration.class) public @interface EnableHelloWorld { }/<code>
@Configuration Class中定义Bean
@Configuration
public class HelloworldConfiguration {
@Bean
public String helloWorld() {
return "Hello, world";
}
}
开启EnableHelloWorld注解,获取 helloWorld Bean
<code>@Configuration@EnableHelloWorldpublic class EnableHelloWorldBootstrap { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 注册当前引导类(被@Configuration标注)到Spring上下文 context.register(EnableHelloWorldBootstrap.class); // 启动上下文 context.refresh(); // 获取Bean String helloWorld = context.getBean("helloWorld", String.class); System.out.printf("helloWorld = %s \n", helloWorld); // 关闭上下文 context.close(); } }/<code>
执行结果:
helloWorld = Hello, world
实例2:导入类为 ImportSelector实现
<code>public interface Server { void start(); void close(); enum ServerType { HTTP, TCP } }/<code>
<code>@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(ServerImportSelector.class) public @interface EnableServer { Server.ServerType type(); } /<code>
根据注解属性,选择要定义的Bean
<code>public class ServerImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { Map attributes = annotationMetadata.getAnnotationAttributes(EnableServer.class.getName()); Server.ServerType serverType = (Server.ServerType)attributes.get("type"); String[] importClassNames = new String[0]; switch (serverType) { case HTTP: importClassNames = new String[]{HttpServer.class.getName()}; break; case TCP: importClassNames = new String[]{TcpServer.class.getName()}; break; } return importClassNames; } }/<code>
启用Server,获取对应的功能:
<code>@Configuration @EnableServer(type = Server.ServerType.TCP) public class EnableServerBootsrap { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(EnableServerBootsrap.class); context.refresh(); // 获取Bean Server server = context.getBean(Server.class); // 启用功能 server.start(); server.close(); context.close(); } }/<code>
执行结果:
TcpServer start ...
TcpServer close ....
实例3:
导入类为 ImportBeanDefinitionRegistrar实现
<code>@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(ServerImportBeanDefinitionRegistrar.class) public @interface EnableServer { Server.ServerType type(); }/<code>
注册Bean
<code>public class ServerImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { ImportSelector importSelector = new ServerImportSelector(); // 筛选Class名称集合 String[] selectedClassNames = importSelector.selectImports(importingClassMetadata); // 创建Bean定义 Stream.of(selectedClassNames) // 转化为BeanDefinitionBuilder对象 .map(BeanDefinitionBuilder::genericBeanDefinition) // 转化为BeanDefinition .map(BeanDefinitionBuilder::getBeanDefinition) .forEach(beanDefinition -> { // 注册BeanDefinition到BeanDefinitionRegistry BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry); }); } }/<code>
启用Server,获取对应的功能:见实例2。
内建模块
Spring 内建模块
<code>@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AsyncConfigurationSelector.class) public @interface EnableAsync { }/<code>
Dubbo 内建模块
<code>@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited@Documented @Import(DubboConfigConfigurationRegistrar.class) public @interface EnableDubboConfig { }/<code>
具体实现可以看下源码,都是相同的原理。