SpringBoot的条件装配,彻底爱了!

开发 架构
在实际的项目开发中,我们往往需要根据不同的环境做出不同的配置,例如:在开发环境下,我们会使用内存数据库以便快速启动服务并进行开发调试,在test环境、生产环境,会使用对应环境的数据库。

[[406072]]

一、介绍

在实际的项目开发中,我们往往需要根据不同的环境做出不同的配置,例如:在开发环境下,我们会使用内存数据库以便快速启动服务并进行开发调试,在test环境、生产环境,会使用对应环境的数据库。

如果我们的应用程序可以根据自身的环境做一些这样的适配,那么我们的程序开发无疑将更加灵活、高效。

在过去的应用程序开发中,我们常常会将这些环境变量写在某个指定的配置文件中,每次服务器启动的时候,会读取服务器中指定的配置文件,从而实现根据不同的环境,应用程序能做出对应的适配。

但是这样的工作,对于运维来说,非常苦逼,尤其是应用程序到达50个以上的时候,会非常不好维护,每次上线改配置,全靠人肉,想想都觉得反人类~

当我们在使用SpringBoot来开发应用程序的时候,这些工作量将大大简化。

SpringBoot为开发者提供了三种可选的条件装配方式。

  • Profile
  • Conditional
  • ConditionalOnProperty

下面,我们一起来了解一下具体的应用实践。

二、程序实践

2.1、Profile

SpringBoot 为应用程序提供了Profile这一概念,用来表示不同的环境。例如,我们分别定义开发、测试和生产这3个环境

  • dev:开发环境
  • test:测试环境
  • production:生产环境

以上传文件为例,在开发环境下,我们将文件上传到本地,而在测试环境、生产环境,我们将文件上传到云端服务商。

1、首先编写两套上传服务

  1. /** 
  2.  * 上传文件到本地 
  3.  * @since 2021-06-13 
  4.  */ 
  5. public class FileUploader implements Uploader { 
  6.  
  7.     @Override 
  8.     public String upload(File file) { 
  9.         //上传文件到本地,并返回绝对路径 
  10.         return null
  11.     } 
  1. /** 
  2.  * 上传文件到OSS 
  3.  * @since 2021-06-13 
  4.  */ 
  5. public class OSSUploader implements Uploader { 
  6.  
  7.     @Override 
  8.     public String upload(File file) { 
  9.         //上传文件到云端,并返回绝对路径 
  10.         return null
  11.     } 

2、然后编写一个服务配置类,根据不同的环境,创建不同的实现类

  1. @Configuration 
  2. public class AppConfig { 
  3.  
  4.     @Bean 
  5.     @Profile("dev"
  6.     public Uploader initFileUploader() { 
  7.         System.out.println("初始化一个上传到本地的bean"); 
  8.         return new FileUploader(); 
  9.     } 
  10.  
  11.     @Bean 
  12.     @Profile("!dev"
  13.     public Uploader initOSSUploader() { 
  14.         System.out.println("初始化一个上传到云端的bean"); 
  15.         return new OSSUploader(); 
  16.     } 
  17.  

3、最后,运行程序

在运行程序时,加上JVM参数-Dspring.profiles.active=dev就可以指定以dev环境启动。

如果当前的Profile设置为dev,则Spring容器会调用initFileUploader()创建FileUploader,否则,调用initOSSUploader()创建OSSUploader。

注意:@Profile("!dev")表示非dev环境。

当然,你还可以在application.properties文件中加上如下配置,一样可以指定环境进行运行。

  1. spring.profiles.active=dev 

2.2、Conditional

除了可以根据@Profile条件来决定是否创建某个Bean外,Spring还可以根据@Conditional决定是否创建某个Bean。

以发短信为例,在生产环境,我们会提供发短信服务,而在其他环境,我们不会向运营商发短信。

1、创建一个条件配置类SMSEnvCondition

  1. public class SMSEnvCondition implements Condition { 
  2.  
  3.     @Override 
  4.     public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { 
  5.         return "true".equalsIgnoreCase(context.getEnvironment().getProperty("enable.sms")); 
  6.     } 

2、创建一个发短信的服务

  1. @Component 
  2. @Conditional(SMSEnvCondition.class) 
  3. public class SendMessageService { 
  4.  
  5.     //... 

3、在application.properties文件中,添加配置变量enable.sms

  1. enable.sms=true 

当enable.sms为true的时候,会创建SendMessageService对象,否则不创建。

2.3、ConditionalOnProperty

Spring提供的条件装配@Conditional,灵活性非常强,但是具体判断逻辑还需要我们自己实现,比较麻烦。

实际上,Spring Boot为开发者提供了很多使用起来更简单的条件注解,例如:

  • ConditionalOnProperty:如果有指定的配置,条件生效
  • ConditionalOnBean:如果有指定的Bean,条件生效
  • ConditionalOnMissingBean:如果没有指定的Bean,条件生效
  • ConditionalOnMissingClass:如果没有指定的Class,条件生效
  • ConditionalOnWebApplication:在Web环境中条件生效
  • ConditionalOnExpression:根据表达式判断条件是否生效

我们以最常用的@ConditionalOnProperty注解为例,将上面的代码改成如下方式即可实现按照条件进行加载。

  1. @Component 
  2. @ConditionalOnProperty(name="enable.sms", havingValue="true"
  3. public class SendMessageService { 
  4.  
  5.     //... 

当enable.sms的值等于true时,会实例化SendMessageService对象;反之,不会创建对象。

是不是超级简单~~~

当然@ConditionalOnProperty的参数还不仅仅限于此,以上面上传文件为例,在开发环境,我们总是上传到本地;在测试环境、生产环境,我们将文件上传到云端,改造过程如下:

  1. @Component 
  2. @ConditionalOnProperty(name = "file.storage", havingValue = "file", matchIfMissing = true
  3. public class FileUploader implements Uploader { 
  4.  
  5.     @Override 
  6.     public String upload(File file) { 
  7.         //上传文件到本地,并返回绝对路径 
  8.         return null
  9.     } 
  1. @Component 
  2. @ConditionalOnProperty(name = "file.storage", havingValue = "oss"
  3. public class OSSUploader implements Uploader { 
  4.  
  5.  
  6.     @Override 
  7.     public String upload(File file) { 
  8.         //上传文件到云端,并返回绝对路径 
  9.         return null
  10.     } 

当file.storage配置值为file,会加载FileUploader类;当file.storage配置值为oss,会加载OSSUploader类。

其中@ConditionalOnProperty中的matchIfMissing参数表示,当没有找到对应配置参数时,会默认加载当前类,也就是FileUploader类。

三、小结

虽然,@Profile、@Conditional、@ConditionalOnProperty三个注解都能实现按照条件进行适配,但是@Profile注解控制比较粗糙,很难实现精细化控制。

在实际的使用过程中,使用最多的是@Conditional、@ConditionalOnProperty,可以很灵活的实现条件装配。

其中,@ConditionalOnProperty是@Conditional的一种具体扩展实现,提供了很多非常实用的操作,在使用中,推荐大家使用@ConditionalOnProperty。

 

如果不够,可以根据@Conditional条件装配,编写一套控制开关实现类。

 

责任编辑:武晓燕 来源: Java极客技术
相关推荐

2022-08-08 07:33:11

自动装配Java容器

2020-10-15 14:50:24

寄存器CPU部件

2020-11-03 09:14:21

高性能开发内存

2021-01-26 00:32:59

chrome插件浏览器

2021-04-02 10:07:19

Chrome浏览器插件

2023-09-28 09:17:18

SpringBootBean

2022-04-24 11:06:54

SpringBootjar代码

2023-11-01 09:07:01

Spring装配源码

2020-12-29 14:47:01

人工智能机器学习交通

2010-02-23 11:10:42

CentOS PPPO

2021-05-04 17:51:23

Linux Linux发行版操作系统

2011-03-25 13:40:28

Cacti安装配置

2011-03-30 15:05:40

MRTG安装

2011-11-08 21:55:58

MRTG 配置

2011-04-02 15:26:51

Cacti安装

2021-01-15 07:22:51

APP时间规划局证件照相机

2021-01-04 08:09:30

女程序行业同行

2020-10-20 17:15:05

Java故障排查命令

2011-03-24 13:00:30

2021-12-29 17:29:07

KubernetesEvents集群
点赞
收藏

51CTO技术栈公众号