Spring boot 编程思想|@Enable 模块驱动
文章目录
github示例代码
github对应代码仓库地址:https://github.com/huajiexiewenfeng/spring-cloud-project-learn
spring-application 手写spring cloud系列
spring-cloud-client-appliaction 客户端
eventBus
spring-cloud-server-application 服务端
spring-boot-2.0-samples SpringBoot编程思想系列
spring-cloud-application 基础组件
- 网关
1.Spring @Enable 模块驱动
从Spring Framework3.1开始支持@Enable 模块驱动
- web mvc模块
- AspectJ代理模块
- Caching 缓存模块
- JMX java管理扩展
- Async 异步处理
- …
引入
@Enable 模块驱动
的意义:
- 于能够简化装配步骤
- 实现了“按需配置”
- 同时屏蔽组件集合装配的细节
@Enable 模块驱动在 Spring Framework
->Spring Boot
->Spring Cloud
中的应用
框架实现 | @Enable 注解模块 | 激活模块 |
---|---|---|
Spring Framework | @EnableWebMvc | web mvc 模块 |
Spring Framework | @EnableTransactionManagement | 事务管理模块 |
Spring Framework | @EnableCaching | 缓存模块 |
Spring Framework | @EnableMBeanExport | JMX模块 |
Spring Framework | @EnableAsync | 异步处理模块 |
Spring Framework | @EnableWebFlux | web flux模块 |
Spring Framework | @EnableAspectJAutoProxy | AspectJ模块 |
Spring Boot | @EnableAutoConfiguration | 自动装配 |
Spring Boot | @EnableManagementContext | actuator模块 |
Spring Boot | @EnableConfigurationProperties | 配置属性绑定 |
Spring Boot | @EnableOAuth2Sso | OAuth2单点登录 |
Spring Cloud | @EnableEurekaServer | Eureka注册中心 |
Spring Cloud | @EnableConfigServer | 配置中心 |
Spring Cloud | @EnableFeignClients | feign客户端 |
Spring Cloud | @EnableZuulProxy | zuul网关 |
Spring Cloud | @EnableCircuitBreaker | 熔断限流 |
2.@Enable 模块驱动实现
2.1注解驱动
参考
可以参考Spring中的EnableWebMvc
的方式
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
DelegatingWebMvcConfiguration
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
...
}
手动实现
1.新建配置
@Configuration
public class HelloWorldConfiguration {
@Bean
public String helloWorld(){
return "hello world";
}
}
2.Enable 注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(HelloWorldConfiguration.class)
public @interface EnableHelloWorld {
}
3.启动类
@EnableHelloWorld
@Configuration
public class EnableHelloWorldBootstrap {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(EnableHelloWorldBootstrap.class);
//启动上下文
applicationContext.refresh();
//获取容器中的bean对象
String helloWorld = applicationContext.getBean("helloWorld", String.class);
System.out.println(helloWorld);
applicationContext.close();
}
}
执行结果:
2.2接口编程
2.2.1 ImportSelector接口
参考
可以参考 Spring 中的EnableCaching
的方式
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
CachingConfigurationSelector
中并没有直接实现ImportSelector#ImportSelector
接口
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
...
@Override
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return getProxyImports();
case ASPECTJ:
return getAspectJImports();
default:
return null;
}
}
...
}
但是父类 AdviceModeImportSelector
实现该接口,并重写了selectImports
方法
public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector {
...
@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;
}
手动实现
1.定义服务器接口Server以及服务器类型:Server.Type
public interface Server {
//启动服务器
void start();
//关闭服务器
void stop();
enum Type{
HTTP,
FTP
}
}
2.实现HTTP和FTP服务器
@Component
public class FtpServer implements Server {
@Override
public void start() {
System.out.println("Ftp服务器启动");
}
@Override
public void stop() {
System.out.println("Ftp服务器关闭");
}
}
@Component
public class HttpServer implements Server {
@Override
public void start() {
System.out.println("Http服务器启动");
}
@Override
public void stop() {
System.out.println("Http服务器关闭");
}
}
3.Enable 注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(ServerImportSelector.class)
public @interface EnableServer {
//设置服务器类型
Server.Type type() default Server.Type.HTTP;
}
4.实现ImportSelector
接口
public class ServerImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//读取EnableServer所有的属性方法 本例中的方法为type()
//key为方法的属性名 value为属性的值
Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(EnableServer.class.getName());
//获取注解中设置的值
Server.Type type = (Server.Type) annotationAttributes.get("type");
//根据类型选择不同的服务器
String[] importClassNames = new String[0];
switch (type) {
case HTTP:
importClassNames = new String[]{HttpServer.class.getName()};
break;
case FTP:
importClassNames = new String[]{FtpServer.class.getName()};
break;
}
return importClassNames;
}
}
5.启动类
设置服务器类型为FTP
@Configuration
@EnableServer(type = Server.Type.FTP)
public class EnableServerBootstrap {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(EnableServerBootstrap.class);
//启动上下文
applicationContext.refresh();
//获取容器中的bean对象
Server server = applicationContext.getBean(Server.class);
server.start();
server.stop();
applicationContext.close();
}
}
执行结果:
2.2.2 ImportBeanDefinitionRegistrar接口
将上面例子中的ServerImportSelector
修改为ImportBeanDefinitionRegistrar
接口的实现即可
public class ServerImportDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//复用之前的逻辑
ImportSelector importSelector = new ServerImportSelector();
//获取选择的ClassName
String[] selectedClassNames = importSelector.selectImports(annotationMetadata);
//创建bean定义
Stream.of(selectedClassNames)
//转化为BeanDefinitionBuilder对象
.map(BeanDefinitionBuilder::genericBeanDefinition)
//转化为BeanDefinition
.map(BeanDefinitionBuilder::getBeanDefinition)
.forEach(beanDefinition -> {
//注册到BeanDefinitionRegister
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, beanDefinitionRegistry);
});
}
}
3.参考
- 《Spring Boot 编程思想》-小马哥