如何保护@ConfigurationProperties类免受更改?

要使用注释,必须创建一个包含 getter 和 setter 的类,如下所示:@ConfigurationProperties

@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);

有没有办法在没有 setter 和外部解析器/读取器类的情况下创建带注释的类?@ConfigurationProperties


答案 1

使用尽可能少的样板代码的一种方法是仅使用getters的接口

public interface AppProps {
    String getNeededProperty();
}

并在龙目岛和注释的帮助下摆脱了实现中的样板 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只能通过使用接口来注入,这使得setters不能用于其他bean:

public class ApplicationLogicBean {
    @Autowired
    AppProps props;

    public void method(){
        log.info("Got " + props.getNeededProperty());
    }
}

使用Spring Boot 1.5.3和龙目岛1.16.16进行测试。


答案 2

从 Spring Boot 2.2 开始,终于可以定义用 .真的要感谢Spring开发人员不断改进他们的框架。
文档显示了一个示例。
你只需要声明一个构造函数,其中包含要绑定的字段(而不是 setter 方式):@ConfigurationProperties

@ConstructorBinding
@ConfigurationProperties(prefix = "some")
public class PropertiesConfig {
    private boolean debug;

    public AcmeProperties(boolean enabled) {
        this.enabled = enabled;   
    }

    public boolean isDebug() {
        return debug;
    }

}

注意1:您必须定义一个且只有一个带有要绑定的参数的构造函数:

在此设置中,必须使用要绑定的属性列表定义一个且只有一个构造函数,并且不能使用构造函数中的属性以外的其他属性进行绑定。

注2:引入了a来定义不可变属性绑定的默认值。@DefaultValue

可以使用@DefaultValue指定默认值,并且将应用相同的转换服务将 String 值强制转换为缺少属性的目标类型。

以下是从官方文档中获取的更详细的示例:

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() { ... }

    }

}

推荐