草庐IT

结合springboot条件注入@ConditionalOnProperty以及@ConfigurationProperties来重构优化代码

buguge - Keep it simple,stupid 2023-04-16 原文

@ConditionalOnProperty实现按需注入bean

短信工具类 SmsUtil

zhenghe-common是一个基础包。

SmsUtil坐落在zhenghe-common里。先看看SmsUtil的面目。

package com.emax.zhenghe.common.util;
 
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
 
@Slf4j
@Configuration
public class SmsUtil {
    @Value("${smtp.config.username}")
    private String userName;
 
    @Value("${smtp.config.requestAddress}")
    private String requestAddress;
 
    @Value("${smtp.config.password}")
    private String passWord;
 
    @Value("${smtp.config.oem.username:oem}")
    private String oemUserName;
 
    @Value("${smtp.config.oem.password:oemsdfaaskdlkfk15673%!@4}")
    private String oemPassWord;
 
    /**
     * 发送短信
     * @param phone 手机号
     * @param msg 短信内容
     * @return
     */
    public String sendSMS(String phone, String msg) {
        StringBuilder sbParam = new StringBuilder();
        StringBuilder url = new StringBuilder();
 
        try {
            sbParam.append("?account=").append(userName);
            sbParam.append("&pwd=").append(URLEncoder.encode(passWord, "UTF-8")).append("&mphone=").append(phone).append("&content=")
                    .append(URLEncoder.encode(msg, "UTF-8"));
            url.append(requestAddress)
                    .append(sbParam.toString());
            Integer result = HttpClientUtils.requestByGetMethod(url.toString());
            return result.toString();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("发送短信异常", e);
        }
 
        return "信息发送成功!";
    }
 
    /**
     * 发送短信
     */
    public String sendSMS(String phone, String msg,String userName,String passWord) {
        ...
    }
 
    /**
     * 发送短信
     */
    public String sendOemSMS(String phone, String msg, String smsSign) {
        StringBuilder smsSignBuilder = new StringBuilder();
        smsSignBuilder.append("【")
                .append(smsSign)
                .append("】")
                .append(msg);
        return sendSMS(phone, smsSignBuilder.toString(), oemUserName, oemPassWord);
    }
}

 

这里要说的是上面通过@Value注入的field。

存在的问题是:这要求所有依赖了zhenghe-common并扫描com.emax.zhenghe.common.util包的应用,都必须定义那些短信相关的properties配置。

需要改进的是:这些properties都以smtp.config开头,所以使用spring-beans的@Value显得啰嗦,不如使用spring-boot的@ConfigurationProperties。

 

先看问题-有的应用不涉及到收发短信,可以不用配置短信相关的这些properties。

解决办法就是spring-boot-autoconfigure的@ConditionalOnProperty注解。

@ConditionalOnProperty注解是spring-boot-autoconfigure下“条件注入”的重要成员,根据配置参数,来决定是否需要创建bean。它主要是通过自身的两个属性来控制自动配置是否生效,这两个属性分别是name、havingValue。只有当配置文件(application.properties或者bootstrap.yml)中和name相同的属性的值和注解上havingValue的值相同时,该配置文件才会生效。

spring-boot-autoconfigure“条件注入”在spring-boot-autoconfigure-XXX.jar包里,package是org.springframework.boot.autoconfigure.condition,“条件注入”常用成员还有ConditionalOnResource、ConditionalOnBean、ConditionalOnClass、ConditionalOnMissingBean、ConditionalOnMissingClass。

敲黑板,@ConditionalOnProperty并不继承@Configuration,它只是控制bean是否生效的,所以@Configuration注解还是需要加在SmsUtil头上的。

如下是@ConditionalOnProperty的javadoc

仔细看javadoc,就能找到答案。这里,我们的改造方案是

@ConditionalOnProperty(prefix = "smtp.config",name = "requestAddress")

下面方式也行,但是不如上面的易读。

@ConditionalOnProperty( name = "smtp.config.requestAddress")

 

@Configuration是spring-context-**.jar的成员,继承@Component。

 

 

 

 

spring-context包里下面的这些注解,你一定知道。

+- context
| +- annotation
| | +- Bean
| | +- ComponentScan
| | +- Configuration
| | +- Import
| | +- Lazy
| | +- Primary
| | +- Profile
| | +- Scope
+- stereotype
| +- Service
| +- Component
| +- Controller
| +- Repository

而 @Value @Autowired 在 spring-beans-**.jar包里(package:org.springframework.beans.factory.annotation); @Mapping @PutMapping @ResponseBody @RestController etc., 在spring-web-**.jar包里(package:org.springframework.web.bind.annotation)。

 

BTW,@Service 与 @Component 的区别

 

再说改进项:@ConfigurationProperties取代@Value

org.springframework.boot.context.properties.ConfigurationProperties在spring-boot-**.jar里。用法很easy,指定prefix即可。
需要指明的是,setter方法还是要有的。这里使用lombok的@Setter注解。
其中,oemUserName与property配置项的名称不一致,需要单独用@Value指定,并且value必须用全名 : @Value("${smtp.config.oemName:null}")。

改造后的SmsUtil

package com.emax.zhenghe.common.util;
 
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
 
@Slf4j
//↓↓ 声明bean
@Configuration(value = "smsUtil")
@ConditionalOnProperty(prefix = "smtp.config", name = "requestAddress")
//↓↓ properties配置
@ConfigurationProperties(prefix = "smtp.config")
@Setter//必须有setter才能为field赋上值
public class SmsUtil {
    private String userName;
 
    private String requestAddress;
 
    private String password;
 
    @Value("${smtp.config.oemName:null}")
    private String oemUserName;
 
    private String oemPassWord;
 
    /**
     * 发送短信
     */
    public String sendSMS(String phone, String msg) {
        ...
    }
 
    /**
     * 发送短信
     */
    public String sendSMS(String phone, String msg,String userName,String passWord) {
        ...
    }
 
    /**
     * 发送短信
     */
    public String sendOemSMS(String phone, String msg, String smsSign) {
        ...
    }
}

 

有关结合springboot条件注入@ConditionalOnProperty以及@ConfigurationProperties来重构优化代码的更多相关文章

  1. ruby-on-rails - 结合 meta_search 与 acts_as_taggable_on - 2

    我在开发的Rails3网站的一些搜索功能上遇到了一个小问题。我有一个简单的Post模型,如下所示:classPost我正在使用acts_as_taggable_on来更轻松地向我的帖子添加标签。当我有一个标记为“rails”的帖子并执行以下操作时,一切正常:@posts=Post.tagged_with("rails")问题是,我还想搜索帖子的标题。当我有一篇标题为“Helloworld”并标记为“rails”的帖子时,我希望能够通过搜索“hello”或“rails”来找到这篇帖子。因此,我希望标题列的LIKE语句与acts_as_taggable_on提供的tagged_with方法

  2. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  5. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  6. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  7. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  8. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  9. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  10. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

随机推荐