引入@Enable模块驱动的意义在于,可以实现"按需实现",并且可以屏蔽组件集合装备的细节
本文基于小马哥-《springboot编程思想》总结,其中代码和结论均出自《springboot编程思想》
@EnableHelloWorld
注解类,并将需要注入的配置类通过@Import
方式注入@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(HelloWorldConfiguration.class)
public @interface EnableHelloWorld {
}
HelloWorldConfiguration
类如下,是一个Configuration类@Configuration
public class HelloWorldConfiguration {
@Bean
public String helloWorld() {
return "Hello,World";
}
}
@EnableHelloWorld
注解成功注入helloWorld的实例对象@EnableHelloWorld
@Configuration
public class EnableHelloWorldBootstrap {
public static void main(String[] args) {
// 构建 Annotation 配置驱动 Spring 上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 注册 当前引导类(被 @Configuration 标注) 到 Spring 上下文
context.register(EnableHelloWorldBootstrap.class);
// 启动上下文
context.refresh();
// 获取名称为 "helloWorld" Bean 对象
String helloWorld = context.getBean("helloWorld", String.class);
// 输出用户名称:"Hello,World"
System.out.printf("helloWorld = %s \n", helloWorld);
// 关闭上下文
context.close();
}
}
helloWorld = Hello,World
可以看到,基于注解方式实现@Enable模块驱动还是比较简单的,下面看看基于接口编程的方式来实现@Enable模块驱动
@Enable
模块接口编程需要实现ImportSelector
或ImportBeanDefinitionRegistar
接口
ImportSeletor
接口相对简单,使用Spring注解元信息抽象AnnotaionMetaData
作为方法参数,该参数的内容为导入ImportSelector实现的@Configuration
类元信息,进而动态地选择一个或者多个其他@Configuration
类进行导入。ImportBeanDefinitionRegistar
相对于ImportSelector
而言,编程复杂度更高,除注解源信息AnnotationData
作为入参外,接口将Bean定义(BeanDefinition
)的注册交给开发人员。ImportSelector
接口的实现方式(参考Spring中的@EnableCaching
注解)Server.Type
public interface Server {
/**
* 启动服务器
*/
void start();
/**
* 关闭服务器
*/
void stop();
/**
* 服务器类型
*/
enum Type {
HTTP, // HTTP 服务器
FTP // FTP 服务器
}
}
HttpServer
和FtpServer
@Component // 根据 ImportSelector 的契约,请确保是实现为 Spring 组件
public class HttpServer implements Server {
@Override
public void start() {
System.out.println("HTTP 服务器启动中...");
}
@Override
public void stop() {
System.out.println("HTTP 服务器关闭中...");
}
}
@Component // 根据 ImportSelector 的契约,请确保是实现为 Spring 组件
public class FtpServer implements Server {
@Override
public void start() {
System.out.println("FTP 服务器启动中...");
}
@Override
public void stop() {
System.out.println("FTP 服务器关闭中...");
}
}
@EnableServer
,同时将枚举类型Server.Type作为@EnableServer
的属性值@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ServerImportSelector.class) // 导入 ServerImportSelector
public @interface EnableServer {
/**
* 设置服务器类型
* @return non-null
*/
Server.Type type();
}
public class ServerImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 读取 EnableServer 中的所有的属性方法,本例中仅有 type() 属性方法
// 其中 key 为 属性方法的名称,value 为属性方法返回对象
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnableServer.class.getName());
// 获取名为"type" 的属相方法,并且强制转化成 Server.Type 类型
Server.Type type = (Server.Type) annotationAttributes.get("type");
// 导入的类名称数组
String[] importClassNames = new String[0];
switch (type) {
case HTTP: // 当设置 HTTP 服务器类型时,返回 HttpServer 组件
importClassNames = new String[]{HttpServer.class.getName()};
break;
case FTP: // 当设置 FTP 服务器类型时,返回 FtpServer 组件
importClassNames = new String[]{FtpServer.class.getName()};
break;
}
return importClassNames;
}
}
@Configuration
@EnableServer(type = Server.Type.HTTP) // 设置 HTTP 服务器
public class EnableServerBootstrap {
public static void main(String[] args) {
// 构建 Annotation 配置驱动 Spring 上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 注册 当前引导类(被 @Configuration 标注) 到 Spring 上下文
context.register(EnableServerBootstrap.class);
// 启动上下文
context.refresh();
// 获取 Server Bean 对象,实际为 HttpServer
Server server = context.getBean(Server.class);
// 启动服务器
server.start();
// 关闭服务器
server.stop();
// 关闭上下文
context.close();
}
}
控制台结果如下:
HTTP 服务器启动中...
HTTP 服务器关闭中...
引导类启动后可以正常获取Server Bean,并且可以通过@EnableServer注解中的属性Server.Type来切换Server Bean的实际注入对象
ImportBeanDefinitionRegistar
接口的实现方式可以基于ImportSelector接口案例做调整即可
ImportBeanDefinitionRegistar
接口:ServerImportBeanDefinitionRegistrar
public class ServerImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 复用 {@link ServerImportSelector} 实现,避免重复劳动
ImportSelector importSelector = new ServerImportSelector();
// 筛选 Class 名称集合
String[] selectedClassNames = importSelector.selectImports(importingClassMetadata);
// 创建 Bean 定义
Stream.of(selectedClassNames)
.map(BeanDefinitionBuilder::genericBeanDefinition) // 转化为 BeanDefinitionBuilder 对象
.map(BeanDefinitionBuilder::getBeanDefinition) // 转化为 BeanDefinition
.forEach(beanDefinition ->
// 注册 BeanDefinition 到 BeanDefinitionRegistry
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry)
);
}
}
@EnableServer
注解中元注解@Import
的属性@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//@Import(ServerImportSelector.class) // 导入 ServerImportSelector
@Import(ServerImportBeanDefinitionRegistrar.class) // 替换 ServerImportSelector
public @interface EnableServer {
/**
* 设置服务器类型
* @return non-null
*/
Server.Type type();
}
EnableServerBootstrap
,观察控制台输出HTTP 服务器启动中...
HTTP 服务器关闭中...
运行结果表明重构是成功的