是否可以使用Spring Boot的@ConfigurationProperties
注释创建不可变(最终)字段?以下示例
@ConfigurationProperties(prefix = "example")
public final class MyProps {
private final String neededProperty;
public MyProps(String neededProperty) {
this.neededProperty = neededProperty;
}
public String getNeededProperty() { .. }
}
我到目前为止尝试过的方法:
@Bean
类的MyProps
neededProperty
参数new MyProps()
null
@ComponentScan
和@Component
提供MyProps
bean。
BeanInstantiationException
中的结果 - &gt; NoSuchMethodException: MyProps.<init>()
我唯一有效的方法是为每个非最终字段提供getter / setter。
答案 0 :(得分:13)
我必须经常解决这个问题,并且我使用了一种不同的方法,这允许我在类中使用final
变量。
首先,我将所有配置保存在一个地方(类),比如称为ApplicationProperties
。该类具有带有特定前缀的@ConfigurationProperties
注释。它还在@EnableConfigurationProperties
注释中列出了配置类(或主类)。
然后我将ApplicationProperties
作为构造函数参数提供,并对构造函数中的final
字段执行赋值。
示例:
主要类:
@SpringBootApplication
@EnableConfigurationProperties(ApplicationProperties.class)
public class Application {
public static void main(String... args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
ApplicationProperties
类
@ConfigurationProperties(prefix = "myapp")
public class ApplicationProperties {
private String someProperty;
// ... other properties and getters
public String getSomeProperty() {
return someProperty;
}
}
具有最终属性的类
@Service
public class SomeImplementation implements SomeInterface {
private final String someProperty;
@Autowired
public SomeImplementation(ApplicationProperties properties) {
this.someProperty = properties.getSomeProperty();
}
// ... other methods / properties
}
我更喜欢这种方法,原因很多,例如:如果我必须在构造函数中设置更多属性,我的构造函数参数列表不是&#34;巨大的&#34;因为我总是有一个论点(ApplicationProperties
在我的情况下);如果需要添加更多final
属性,我的构造函数保持不变(只有一个参数) - 这可能会减少其他地方的更改次数等。
我希望这会有所帮助
答案 1 :(得分:1)
最后,如果你想要一个不可变对象,你也可以“破解”
的setter@ConfigurationProperties(prefix = "myapp")
public class ApplicationProperties {
private String someProperty;
// ... other properties and getters
public String getSomeProperty() {
return someProperty;
}
public String setSomeProperty(String someProperty) {
if (someProperty == null) {
this.someProperty = someProperty;
}
}
}
显然,如果属性不仅仅是一个String,那是一个可变对象,事情就更复杂了,但这是另一个故事。
更好的是,您可以创建配置容器
@ConfigurationProperties(prefix = "myapp")
public class ApplicationProperties {
private final List<MyConfiguration> configurations = new ArrayList<>();
public List<MyConfiguration> getConfigurations() {
return configurations
}
}
现在配置是没有
的claspublic class MyConfiguration {
private String someProperty;
// ... other properties and getters
public String getSomeProperty() {
return someProperty;
}
public String setSomeProperty(String someProperty) {
if (this.someProperty == null) {
this.someProperty = someProperty;
}
}
}
和application.yml为
myapp:
configurations:
- someProperty: one
- someProperty: two
- someProperty: other
答案 2 :(得分:0)
您可以通过@Value
批注设置字段值。这些可以直接放在字段上,不需要任何设置器:
@Component
public final class MyProps {
@Value("${example.neededProperty}")
private final String neededProperty;
public String getNeededProperty() { .. }
}
这种方法的缺点是:
答案 3 :(得分:0)
我的想法是通过内部类封装属性组,并仅使用getter公开接口。
属性文件:
myapp.security.token-duration=30m
myapp.security.expired-tokens-check-interval=5m
myapp.scheduler.pool-size=2
代码:
@Component
@ConfigurationProperties("myapp")
@Validated
public class ApplicationProperties
{
private final Security security = new Security();
private final Scheduler scheduler = new Scheduler();
public interface SecurityProperties
{
Duration getTokenDuration();
Duration getExpiredTokensCheckInterval();
}
public interface SchedulerProperties
{
int getPoolSize();
}
static private class Security implements SecurityProperties
{
@DurationUnit(ChronoUnit.MINUTES)
private Duration tokenDuration = Duration.ofMinutes(30);
@DurationUnit(ChronoUnit.MINUTES)
private Duration expiredTokensCheckInterval = Duration.ofMinutes(10);
@Override
public Duration getTokenDuration()
{
return tokenDuration;
}
@Override
public Duration getExpiredTokensCheckInterval()
{
return expiredTokensCheckInterval;
}
public void setTokenDuration(Duration duration)
{
this.tokenDuration = duration;
}
public void setExpiredTokensCheckInterval(Duration duration)
{
this.expiredTokensCheckInterval = duration;
}
@Override
public String toString()
{
final StringBuffer sb = new StringBuffer("{ ");
sb.append("tokenDuration=").append(tokenDuration);
sb.append(", expiredTokensCheckInterval=").append(expiredTokensCheckInterval);
sb.append(" }");
return sb.toString();
}
}
static private class Scheduler implements SchedulerProperties
{
@Min(1)
@Max(5)
private int poolSize = 1;
@Override
public int getPoolSize()
{
return poolSize;
}
public void setPoolSize(int poolSize)
{
this.poolSize = poolSize;
}
@Override
public String toString()
{
final StringBuilder sb = new StringBuilder("{ ");
sb.append("poolSize=").append(poolSize);
sb.append(" }");
return sb.toString();
}
}
public SecurityProperties getSecurity() { return security; }
public SchedulerProperties getScheduler() { return scheduler; }
@Override
public String toString()
{
final StringBuilder sb = new StringBuilder("{ ");
sb.append("security=").append(security);
sb.append(", scheduler=").append(scheduler);
sb.append(" }");
return sb.toString();
}
}
答案 4 :(得分:0)
从Spring Boot 2.2开始,最后可以定义用@ConfigurationProperties
装饰的不可变类。
The documentation显示了一个示例。
您只需要声明一个带有绑定字段的构造函数(而不是setter方法)。
因此,您现在无需任何设置程序的实际代码就可以了:
@ConfigurationProperties(prefix = "example")
public final class MyProps {
private final String neededProperty;
public MyProps(String neededProperty) {
this.neededProperty = neededProperty;
}
public String getNeededProperty() { .. }
}
答案 5 :(得分:0)
使用与https://stackoverflow.com/a/60442151/11770752中的方法类似的方法
但是您可以使用AllArgsConstructor
代替RequiredArgsConstructor
。
考虑关注applications.properties
myprops.example.firstName=Peter
myprops.example.last-name=Pan
myprops.example.age=28
注意:使用与您的属性一致的属性,我只是想证明它们都是正确的(fistName
和last-name
)。
Java类获取属性
@Getter
@ConstructorBinding
@RequiredArgsConstructor
@ConfigurationProperties(prefix = "myprops.example")
public class StageConfig
{
private final String firstName;
private final Integer lastName;
private final Integer age;
// ...
}
此外,您还必须在构建工具中添加依赖项。
build.gradle
annotationProcessor('org.springframework.boot:spring-boot-configuration-processor')
或
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring.boot.version}</version>
</dependency>
如果您进一步采取措施为您的配置提供了精美而准确的描述,请考虑在目录additional-spring-configuration-metadata.json
中创建文件src/main/resources/META-INF
。
{
"properties": [
{
"name": "myprops.example.firstName",
"type": "java.lang.String",
"description": "First name of the product owner from this web-service."
},
{
"name": "myprops.example.lastName",
"type": "java.lang.String",
"description": "Last name of the product owner from this web-service."
},
{
"name": "myprops.example.age",
"type": "java.lang.Integer",
"description": "Current age of this web-service, since development started."
}
}
(清理并编译以生效)
至少在IntelliJ中,将鼠标悬停在application.propoerties
内的属性上时,您会清楚地看到自定义属性。对其他开发人员非常有用。
这为我的财产提供了一个简洁明了的结构,我将在春天的服务中使用它。
答案 6 :(得分:-1)
使用Lombok批注,代码如下所示:
@ConfigurationProperties(prefix = "example")
@AllArgsConstructor
@Getter
@ConstructorBinding
public final class MyProps {
private final String neededProperty;
}
此外,如果您想直接自动装配该属性类,而不使用@Configuration
类和@EnableConfigurationProperties
,则需要将@ConfigurationPropertiesScan
添加到用{{1}注释的主应用程序类中}。