当前位置: 首页 > 科技观察

我喜欢SpringBoot的条件汇编!

时间:2023-03-21 19:34:16 科技观察

1。简介在实际的项目开发中,我们经常需要根据不同的环境进行不同的配置。比如在开发环境中,我们会使用内存数据库来快速启动服务,进行开发调试。在测试环境下,生产环境会使用环境对应的数据库。如果我们的应用程序能够根据自身的环境进行一些这样的适配,那么我们的程序开发无疑会更加灵活和高效。在以往的应用开发中,我们往往会将这些环境变量写在指定的配置文件中。服务器每次启动时,都会读取服务器中指定的配置文件,从而实现针对不同环境的应用。程序可以进行相应的适配。但是这种工作对于运维来说是非常困难的,尤其是当应用数量达到50个以上的时候,维护起来就会非常困难。每次网上改配置,都是靠人肉。想想就觉得反人类~当我们使用SpringBoot开发应用时,这些工作量会大大简化。SpringBoot为开发者提供了三种可选的条件装配方法。ProfileConditionalConditionalOnProperty接下来我们来看看具体的应用实践。2.程序实践2.1.ProfileSpringBoot为应用程序提供了Profile的概念来表示不同的环境。比如我们分别定义开发、测试、生产三个环境。dev:开发环境test:测试环境production:生产环境以上传文件为例。环境,我们将文件上传到云服务提供商。1.先写两套上传服务/***上传文件到本地*@since2021-06-13*/publicclassFileUploaderimplementsUploader{@OverridepublicStringupload(Filefile){//上传文件到本地并返回绝对路径returnnull;}}/***上传文件到OSS*@since2021-06-13*/publicclassOSSUploaderimplementsUploader{@OverridepublicStringupload(Filefile){//上传文件到云端,返回绝对路径returnnull;}}2.然后写一个服务配置类,根据针对不同的Environment,创建不同的实现类@ConfigurationpublicclassAppConfig{@Bean@Profile("dev")publicUploaderinitFileUploader(){System.out.println("初始化一个bean上传到本地");returnnewFileUploader();}@Bean@Profile("!dev")publicUploaderinitOSSUploader(){System.out.println("初始化一个bean上传到云端");returnnewOSSUploader();}}3、最后在运行程序的时候,加上JVM参数-Dspring。profiles.active=dev可以指定在dev环境启动。如果当前Profile设置为dev,Spring容器会调用initFileUploader()创建FileUploader,否则调用initOSSUploader()创建OSSUploader。注意:@Profile("!dev")表示非开发环境。当然你也可以在application.properties文件中添加如下配置,也可以指定运行环境。spring.profiles.active=dev2.2,Conditional除了根据@Profile条件决定是否创建Bean,Spring还可以根据@Conditional决定是否创建Bean。以短信为例。在生产环境中,我们会提供短信服务,但在其他环境中,我们不会向运营商发送短信。1.创建条件配置类SMSEnvConditionpublicclassSMSEnvConditionimplementsCondition{@Overridepublicbooleanmatches(ConditionContextcontext,AnnotatedTypeMetadatametadata){return"true".equalsIgnoreCase(context.getEnvironment().getProperty("enable.sms"));}}2.创建短信服务@Component@Conditional(SMSEnvCondition.class)publicclassSendMessageService{//...}3.在application.properties文件中,添加配置变量enable.smsenable.sms=true。当enable.sms为真时,将创建一个SendMessageService对象。否则不创建。2.3、ConditionalOnPropertySpring提供的条件组件@Conditional非常灵活,但是具体的判断逻辑需要我们自己实现,比较麻烦。其实SpringBoot为开发者提供了很多更容易使用的条件注解,比如:ConditionalOnProperty:如果有指定配置,条件生效ConditionalOnBean:如果有指定bean,条件生效ConditionalOnMissingBean:如果没有指定bean,条件生效ConditionalOnMissingClass:如果没有指定Class,条件生效ConditionalOnWebApplication:条件在web环境生效ConditionalOnExpression:根据expression我们以最常用的@ConditionalOnProperty注解为例,将上面的代码改成下面的方法,实现条件加载。@Component@ConditionalOnProperty(name="enable.sms",havingValue="true")publicclassSendMessageService{//...}当enable.sms的值等于true时,将实例化SendMessageService对象;否则,不会创建该对象。是不是超级简单~~~当然@ConditionalOnProperty的参数不仅限于此。以上面上传的文件为例。在开发环境中,我们总是在本地上传;在测试环境和生产环境中,我们将文件上传到云端。转换过程如下:@Component@ConditionalOnProperty(name="file.storage",havingValue="file",matchIfMissing=true)publicclassFileUploaderimplementsUploader{@OverridepublicStringupload(Filefile){//本地上传文件并返回绝对路径returnnull;}}@Component@ConditionalOnProperty(name="file.storage",havingValue="oss")publicclassOSSUploaderimplementsUploader{@OverridepublicStringupload(Filefile){//上传文件到云端并返回绝对路径returnnull;}}file.storage配置值为file,会加载FileUploader类;当file.storage配置值为oss时,会加载OSSUploader类。@ConditionalOnProperty中的matchIfMissing参数表示当没有找到对应的配置参数时,默认加载当前类,也就是FileUploader类。3、总结@Profile、@Conditional、@ConditionalOnProperty这三个注解虽然可以根据条件进行适配,但是@Profile注解的控制比较粗糙,难以做到精细控制。在实际使用中,使用最多的是@Conditional和@ConditionalOnProperty,可以灵活实现条件汇编。其中@ConditionalOnProperty是@Conditional的具体扩展实现,提供了很多非常实用的操作。在使用中,我们推荐你使用@ConditionalOnProperty。如果还不行,可以根据@Conditional条件组装,写一套控制开关实现类。