SonarQube报单例类非同步写入字段错误的原因咨询
SonarQube 单例类非同步写字段问题解答
问题根因
- Spring 框架中,标注了
@Component、@Configuration等注解的类默认是单例(Singleton) 实例,整个应用运行周期内只会初始化一次,多线程场景下会被多个线程同时访问。 - 你的代码中
UrlItems被@Component标注为单例类,其中的url、location是普通成员变量,对应的set方法没有任何同步控制机制。多线程同时调用set方法写变量时,会出现可见性、竞态条件等线程安全问题,这就是SonarQube触发严重级别Bug的原因。
修复方案
方案1:修正@ConfigurationProperties的标准用法(最推荐)
@ConfigurationProperties绑定的属性类不需要标注@Component注册为单例Bean,你只需要在项目启动类增加@ConfigurationPropertiesScan注解即可完成属性自动绑定,从根源消除单例并发写的风险。
修改后代码示例:
// 启动类增加配置属性扫描注解 @SpringBootApplication @ConfigurationPropertiesScan public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } } @Configuration @ConfigurationProperties(prefix = "app") public class UrlConfigs { private List<UrlItems> item; // 保留Getter & Setter } // 去掉@Component注解,作为普通属性POJO使用 public class UrlItems { private String url; private String location; // 保留Getter & Setter }
方案2:增加同步控制(适用业务要求UrlItems必须为单例且支持运行时修改的场景)
如果业务确实需要将UrlItems注册为单例Bean且存在运行时修改属性的需求,可以通过两种方式实现同步控制:
- 给set方法增加
synchronized关键字,保证同一时间只有一个线程可以执行写操作:
public synchronized void setUrl(String url){ this.url = url; } public synchronized void setLocation(String location) { this.location = location; }
- 如果只有赋值操作没有复合读写操作,可以将成员变量声明为
volatile保证修改的可见性:
private volatile String url; private volatile String location;
方案3:禁止运行时修改属性
如果业务上配置属性不需要在运行时修改,直接删除UrlItems的set方法,仅保留getter即可,从逻辑上禁止写操作,也可以消除SonarQube告警。
内容的提问来源于stack exchange,提问作者ima.technophyle
相关产品推荐
相关产品推荐

