如何保护@ConfigurationProperties 类免受更改?
Posted
技术标签:
【中文标题】如何保护@ConfigurationProperties 类免受更改?【英文标题】:How to protect @ConfigurationProperties classes from changes? 【发布时间】:2017-11-27 16:50:43 【问题描述】:要使用 @ConfigurationProperties
注释,必须创建一个具有类似 getter 和 setter 的类:
@ConfigurationProperties(prefix = "some")
public class PropertiesConfig
private boolean debug;
public boolean isDebug()
return debug;
public void setDebug(boolean debug)
this.debug = debug;
但这会导致有人试图通过调用来修改此值:
@Autowire
private PropertiesConfig config;
//....
config.setDebug(true);
有没有办法在没有设置器和外部解析器/读取器类的情况下创建 @ConfigurationProperties
注释类?
【问题讨论】:
【参考方案1】:使用尽可能少的样板代码的一种方法是使用仅带有 getter 的接口
public interface AppProps
String getNeededProperty();
并借助 Lombok 的 @Getter
和 @Setter
注释在实现中摆脱样板的 getter 和 setter:
@ConfigurationProperties(prefix = "props")
@Getter
@Setter
public class AppPropsImpl implements AppProps
private String neededProperty;
然后,对于只能通过接口访问其他 bean 的 bean bo,可以考虑将其放入配置中,而不是将其标记为 @Component
或在主应用程序类上使用 @EnableConfigurationProperties(AppPropsImpl.class)
按界面:
@Configuration
@EnableConfigurationProperties
public class PropsConfiguration
@Bean
public AppProps appProps()
return new AppPropsImpl();
现在这个 bean 只能通过接口注入,这使得其他 bean 无法使用 setter:
public class ApplicationLogicBean
@Autowired
AppProps props;
public void method()
log.info("Got " + props.getNeededProperty());
使用 Spring Boot 1.5.3 和 Lombok 1.16.16 测试。
【讨论】:
【参考方案2】:这样的东西很好用
@Configuration
class MyAppPropertiesConfiguration
@Bean
@ConfigurationProperties(prefix = "some")
public PropertiesConfig propertiesConfig ()
return new PropertiesConfigImpl();
public interface PropertiesConfig
public boolean isDebug();
private static class PropertiesConfigImpl implements PropertiesConfig
private boolean debug;
@Override
public boolean isDebug()
return debug;
public void setDebug(boolean debug)
this.debug = debug;
然后
@Autowired PropertiesConfig properties;
【讨论】:
我不得不将接口移动到它自己的文件中。然后效果很好。【参考方案3】:从 Spring Boot 2.2 开始,终于可以定义用@ConfigurationProperties
修饰的不可变类了。非常感谢 Spring 开发人员不断改进他们的框架。The documentation 显示了一个示例。
您只需要声明一个带有要绑定的字段的构造函数(而不是 setter 方式):
@ConstructorBinding
@ConfigurationProperties(prefix = "some")
public class PropertiesConfig
private boolean debug;
public AcmeProperties(boolean enabled)
this.enabled = enabled;
public boolean isDebug()
return debug;
注意 1 :您必须定义一个且只有一个带有要绑定参数的构造函数:
在这个设置中,只有一个构造函数必须用 您希望绑定的属性列表,而不是其他属性 构造函数中的那些是绑定的。
注意 2:引入了 @DefaultValue
来定义不可变属性绑定的默认值。
可以使用@DefaultValue 指定默认值,同样的 将应用转换服务将字符串值强制转换为 缺失属性的目标类型。
这是从官方文档中获取的更详细的示例:
import java.net.InetAddress;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DefaultValue;
import org.springframework.boot.context.properties.ConstructorBinding;
@ConstructorBinding
@ConfigurationProperties("acme")
public class AcmeProperties
private final boolean enabled;
private final InetAddress remoteAddress;
private final Security security;
public AcmeProperties(boolean enabled, InetAddress remoteAddress, Security security)
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
public boolean isEnabled() ...
public InetAddress getRemoteAddress() ...
public Security getSecurity() ...
public static class Security
private final String username;
private final String password;
private final List<String> roles;
public Security(String username, String password,
@DefaultValue("USER") List<String> roles)
this.username = username;
this.password = password;
this.roles = roles;
public String getUsername() ...
public String getPassword() ...
public List<String> getRoles() ...
【讨论】:
【参考方案4】:不可能开箱即用。 @ConfigurationProperties
bean 必须有标准的 getter 和 setter。您可能需要考虑此答案中描述的方法:Immutable @ConfigurationProperties
或者是这样的:
@Component
public class ApplicationProperties
private final String property1;
private final String property2;
public ApplicationProperties(
@Value("$some.property1") String property1,
@Value("$some.other.property2) String property2)
this.property1 = property1;
this.property2 = property1;
//
// ... getters only ...
//
【讨论】:
不幸的是,有几个极端情况不适用于这种方法。例如,使用@Value
将application.yaml
中的对象作为Map<String, Object>
注入将无法正常工作,而@ConfigurationProperties
方法则可以正常工作。以上是关于如何保护@ConfigurationProperties 类免受更改?的主要内容,如果未能解决你的问题,请参考以下文章