Spring @Enable 模块驱动原理

说明

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>

具体实现可以看下源码,都是相同的原理。