社区编辑申请
注册/登录
SpringBoot 手动配置 @Enable 的秘密
开发 后端
今天我们就来探究一下这个 @EnableXXX给我们做了哪些工作,或者我们应该怎么通过自定义的方式开发我们自己的功能模块。

在 Spring Boot开发过程中,我们经常会看到使用 @EnableXXX来激活我们某一个功能性的模块,通过类注解激活后我们就能使用所激活的配置给我们带来的功能。

今天我们就来探究一下这个 @EnableXXX给我们做了哪些工作,或者我们应该怎么通过自定义的方式开发我们自己的功能模块。

演示环境

  • IntelliJ IDEA 2020.2.1 (Community Edition)
  • Maven 3.5.4
  • Spring Boot 2.1.1.RELEASE

走进源码

在 SpringBoot中 @Enable的实现方式用两种。

一种是注解驱动的方式,我们以 @EnableWebMvc为例进行探究;

另外一种是接口编程的方式,我们以 @EnableCaching为例进行探究。

1、注解驱动方式(@EnableWebMvc)

在 Spring Boot项目中,当我们可以使用 @EnableWebMvc注解用来激活我们的 Spring MVC相关的配置,接下来进入源码一探究竟。

@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

通过观察上面的源码,我们就可以大胆的猜测,其实使用 @EnableWebMvc注解的作用就是导入 DelegatingWebMvcConfiguration.class这个类,接下来我们就进入这个类看看。

@Configuration  
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
........
@Override
protected void addInterceptors(InterceptorRegistry registry) {
this.configurers.addInterceptors(registry);
}
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
this.configurers.addResourceHandlers(registry);
}
........
}

进入到这个类中我们发现了这个 @Configuration注解,到这儿我们好像明白了写什么。首先这个 DelegatingWebMvcConfiguration类继承了 WebMvcConfigurationSupport类,重写了里面的关于 WebMvc的相关配置,然后作为一个配置类加载到我们的 Spring容器中。至此来实现启动(激活)WebMvc模块。

2、接口编程的方式(@EnableCaching)

在 Spring Boot项目中,当我们可以使用 @EnableCaching注解用来激活我们的缓存相关的配置,接着进入源码看看到底做了什么。

@Target(ElementType.TYPE)  
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}

这里 @EnableCaching同样是使用 @Import导入了一个配置类,而它导入的是 CachingConfigurationSelector,接着我进入这个类看一看。

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<enablecaching>,而这个类又继承了 ImportSelector,并且我们可以看看 ImportSelector的代码:

public interface ImportSelector {  
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}

这个类中只用一个方法,那就是 selectImports。也就是说当我们重写了这个方法之后,我们可以在方法中添加自己的逻辑判断,来决定最后导入哪些配置类。这样就可以实现灵活的加载配置。这个方法的返回值 String[]里面存放的是所有复合条件的配置类的全路径信息。

自定义实现

通过上面的分析我们已经完成了对 @EnableXXX套路的了解,接下来我们自己动手实现下。首先需要准备一个 Spirng Boot的项目,这里我已经准备好了。

1、注解驱动方式的自定义实现

根据我们分析源码的步骤我们首先需要准备一个配置类。接下来在 configuration包下,创建一个 HelloConfiguration。代码如下:

/**  
* Hello模块的配置
*/
@Configuration
public class HelloConfiguration {
@Bean
public String hello() { // method name is bean name
System.out.println("Bean : hello is loading.");
return "hello word !";
}
}

这里被 @Bean标注的方法,方法名会作为 bean对象的名称。而且当该 bean被加载的时候我们还会在控制台输出一段话 Bean : hello is loading.。

配置类我们已经准备好了,接下来我们在 annotation包中编写激活配置的注解。

/**  
* 激活Hello模块配置
*
* @author Jerome Zhu
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(HelloConfiguration.class) // 模块装配:注解驱动实现
public @interface EnableHello {
}

最后我们在 bootstrap包中编写验证我们 @EnableHello模块的启动类。

/**  
* 验证自定义 {@link EnableHello} 模块化装配
*
* @author Jerome Zhu
*/
@EnableHello
public class EnableHelloBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableHelloBootstrap.class)
.web(WebApplicationType.NONE)
.run(args);
String helloBean = context.getBean("hello", String.class);
System.out.println("hello Bean: " + helloBean);
context.close();
}
}

我们在我们启动类上标注了 @EnableHello来激活我们的 Hello模块,并且在 Spring Boot项目启动后,获取到了应用的上下文 ConfigurableApplicationContext。然后我们根据我们注入的 bean的名字 hello来获取 bean,接着打印 bean的内容,最后关闭上下文。

我们启动程序后,可以在控制台中看到这样两行输出:

Bean : hello is loading.  
hello Bean: hello word !

这刚好就是我们配置的内容,到这儿我们就完成了基于注解驱动方式激活(启动)配置。

2、接口编程方式自定义实现

根据我们读过源码后,我们首先最重要的是有一个继承 ImportSelector的实现类。在 annotation包中创建一个 HelloImportSelector配置类。

/**  
* Hello {@link ImportSelector} 的实现
*/
public class HelloImportSelector implements ImportSelector {
private final String key = "jerome";
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
if ("jerome".equals(key)) {
return new String[]{HelloJeromeConfiguration.class.getName()};
}
return new String[]{HelloConfiguration.class.getName()};
}
}

这里为了体现出 selectImports方法的作用,我们在本类中添加一个 key字段。当 key的值为 jerome的时候我们去加载 HelloJeromeConfiguration这个配置类。首先看一个这个配置类的内容。

/**  
* Hello模块的配置
*
* @author Jerome Zhu
*/
@Configuration
public class HelloJeromeConfiguration {
@Bean
public String hello() { // method name is bean name
System.out.println("Bean : hello is loading.");
return "hello jerome !";
}
}

加载这个配置类后 bean的名字还是 hello但是内容变成了 hello jerome !。接着我们对 @EnableHello注解进行改造改造成 @EnableHellos。

/**  
* 激活Hellos模块配置
*
* @author Jerome Zhu
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(HelloImportSelector.class) // 模块装配:接口驱动实现 优点可选bean
public @interface EnableHellos {
}

这里使用了 HelloImportSelector来实现我们的模块装配。

这里 @EnableHellos可能不符合命名规范,这里只做演示使用,不要喷我。

最后我们在 bootstrap包中编写验证我们 @EnableHello模块的启动类。

/**  
* 验证自定义 {@link EnableHellos} 模块化装配
*
* @author Jerome Zhu
*/
@EnableHellos
public class EnableHellosBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableHellosBootstrap.class)
.web(WebApplicationType.NONE)
.run(args);
String helloBean = context.getBean("hello", String.class);

System.out.println("hello Bean: " + helloBean);
context.close();
}
}

这里和基于注解驱动方式激活(启动)配置的验证方法一样,我就是直接拷贝的,主要修改了 @EnableHellos这个激活注解。

我们启动程序后,可以在控制台中看到这样两行输出:  

Bean : hello is loading.  
hello Bean: hello jerome !

这刚好就是我们配置的内容,到这儿我们就完成了基于接口编程方式激活(启动)配置。

总结

我们通过上面了解了 @EnableXXX的两种实现方式。其中基于注解驱动方式激活(启动)配置的方式是相对方便的一个实现,需要激活什么就直接导入该配置类即可;而基于接口编程方式激活(启动)配置的方式相对更加灵活,可以根据自定义的规则来选择激活哪些配置类,不激活哪些配置类,这是一种比较灵活的方式,方便我们对其进行扩展。

总之这两种方式都是手动激活的方式,换言之我们需要在启动类上添加 @Enable***的注解,来手动激活配置,这种方式在某些场景并不是很方便。

责任编辑:庞桂玉 来源: Java知音
相关推荐

2022-06-20 22:37:25

Linux操作系统命令

2022-06-14 11:01:48

SpringBootTomcatUndertow

2022-06-17 07:32:39

策略模式SpringBoot

2022-06-01 07:33:21

ES查询搜索

2022-05-23 07:48:10

zabbix监控CentOS7

2022-06-14 23:34:10

Linux安全服务器

2022-06-28 09:26:25

Python配置文件

2022-06-23 12:03:00

网络安全网络安全事故

2022-06-28 12:35:21

DockerPython

2022-04-28 07:31:41

2022-06-03 09:41:03

DockerKubernetes容器

2022-02-23 15:30:28

SpringBoot后端流程

2022-06-23 11:42:22

MySQL数据库

2022-06-22 05:53:49

城域网广域网VXLAN

2022-04-12 09:48:22

云计算安全云服务云安全

2022-06-23 09:22:57

Vue技巧前端

2022-04-18 17:28:14

React前端

2022-06-24 10:16:59

Python精选库

2022-06-02 07:13:12

Python3.11编程语言

2022-02-21 07:48:54

Mysql数据库SpringBoot

同话题下的热门内容

源码探秘:Python 中对象是如何被调用的?使用Java和Python进行数据统计和分析C++与Java“相爱相杀”:一个步步紧逼,一个节节败退裁员真能拯救中国互联网?吐血推荐17个提升开发效率的“轮子”Flask vs Django: 该如何选择Python框架?哪个版本的JVM最快?编写完10万行代码,我发了篇长文吐槽Rust

编辑推荐

2017年9月编程语言排行榜:Java、C与C++三巨头还能统治排行榜多久?2017年最受欢迎的5个前端框架比较2017年11月编程语言排行榜:脚本语言怎么了?2017年3月编程语言排行榜:Swift首次进入前十最近租房有点烦!技术人如何用Python找到称心如意的“小窝”?
我收藏的内容
点赞
收藏

51CTO技术栈公众号