5-配置文件

作者: 中_中_ | 来源:发表于2018-10-02 23:13 被阅读2次

    Spring boot使用具有顺序的PropertySource加载配置,以便于配置属性的覆盖。
    1,home目录下的devtools全局设置属性( ~/.spring-bootdevtools.properties ,如果使用devtools)。
    2,测试用例上的@TestPropertySource注解
    3,测试用例上的@SpringBootTest#properties注解
    4,命令行参数
    5,SPRING_APPLICATION_JSON 的属性(环境变量或系统属性中内嵌的内联
    JSON)
    6,ServletConfig 初始化参数
    7,ServletContext 初始化参数
    8,java:comp/env下的JNDI属性
    9,System.getProperties()属性
    10,操作系统环境变量
    11,random.*当中RandomValuePropertySource属性
    12,没有打进jar包的Profile-specific应用属性
    13,打进jar包中的Profile-specific应用属性
    14,没有打进jar包的应用配置( application.properties 和YAML变量)
    15,打进jar包中的应用配置( application.properties 和YAML变量)
    16,@Configuration 类上的 @PropertySource 注解
    17,默认属性(使用 SpringApplication.setDefaultProperties 指定) 。


    全局devTool配置

    在home目录下添加一个.spring-boot-devtools.properties文件,该文件中的内容会作用于该设备上所有spring boot应用。Spring boot应用启动时会到设备的home目录查找这个文件,如果这个文件存在,则将文件中的内容设置到ConfigurableEnvironment对象当中。
    devTool 属性针对Spring boot应用重启、以什么方式重启、加载那些类路径下的文件及视图缓存和session持久化等属性的配置。

    public void postProcessEnvironment(ConfigurableEnvironment environment,
                SpringApplication application) {
            File home = getHomeFolder();
            File propertyFile = (home != null) ? new File(home, FILE_NAME) : null;
            if (propertyFile != null && propertyFile.exists() && propertyFile.isFile()) {
                FileSystemResource resource = new FileSystemResource(propertyFile);
                Properties properties;
                try {
                    properties = PropertiesLoaderUtils.loadProperties(resource);
                    environment.getPropertySources().addFirst(
                            new PropertiesPropertySource("devtools-local", properties));
                }
                catch (IOException ex) {
                    throw new IllegalStateException("Unable to load " + FILE_NAME, ex);
                }
            }
        }
    

    默认配置如下

            devToolsProperties.put("spring.thymeleaf.cache", "false");
            devToolsProperties.put("spring.freemarker.cache", "false");
            devToolsProperties.put("spring.groovy.template.cache", "false");
            devToolsProperties.put("spring.mustache.cache", "false");
            devToolsProperties.put("server.servlet.session.persistent", "true");
            devToolsProperties.put("spring.h2.console.enabled", "true");
            devToolsProperties.put("spring.resources.cache.period", "0");
            devToolsProperties.put("spring.resources.chain.cache", "false");
            devToolsProperties.put("spring.template.provider.cache", "false");
            devToolsProperties.put("spring.mvc.log-resolved-exception", "true");
            devToolsProperties.put("server.servlet.jsp.init-parameters.development", "true");
            devToolsProperties.put("spring.reactor.stacktrace-mode.enabled", "true");
    

    配置随机值

    RandomValuePropertySource可以产生整数,long或则字符串。
    在application.properties文件中添加如下随机值(也可用于配置其他属性值):

    my.secret=${random.value}
    my.number=${random.int}
    my.bignumber=${random.long}
    my.less=${random.int(10)}
    my.range=${random.int[1024,65536]}
    

    定义一个bean,为了能够看到bean在初始化时完成属性设置,让bean实现org.springframework.beans.factory.InitializingBean接口。

    @Component
    public class MyRandomValue implements InitializingBean{
    
        @Value("${my.secret}")
        private String secret;
        @Value("${my.number}")
        private String number;
        @Value("${my.bignumber}")
        private String bignumber;
        @Value("${my.less}")
        private String less;
        @Value("${my.range}")
        private String range;
        
        
        @Override
        public void afterPropertiesSet() throws Exception {
            // TODO Auto-generated method stub
            System.out.println(this.toString());
        }
    
        @Override
        public String toString() {
            return "MyRandomValue [secret=" + secret + ", number=" + number
                    + ", bignumber=" + bignumber + ", less=" + less + ", range="
                    + range + "]";
        }
    
        public String getSecret() {
            return secret;
        }
    
        public void setSecret(String secret) {
            this.secret = secret;
        }
    
        public String getNumber() {
            return number;
        }
    
        public void setNumber(String number) {
            this.number = number;
        }
    
        public String getBignumber() {
            return bignumber;
        }
    
        public void setBignumber(String bignumber) {
            this.bignumber = bignumber;
        }
    
        public String getLess() {
            return less;
        }
    
        public void setLess(String less) {
            this.less = less;
        }
    
        public String getRange() {
            return range;
        }
    
        public void setRange(String range) {
            this.range = range;
        }
    }
    

    启动程序,观察输出:


    image.png

    命令行参数

    新建一个bean同样实现org.springframework.beans.factory.InitializingBean接口,并实现接口方法;为了能够访问命令行参数,在bean中添加一个org.springframework.boot.ApplicationArguments属性,该接口提供对原始String[]参数的访问,提供对option和non-option参数的访问。

    @Component
    public class MyCommandLineValue implements InitializingBean{
    
        @Autowired
        private ApplicationArguments arg;
        
        @Override
        public void afterPropertiesSet() throws Exception {
            // TODO Auto-generated method stub
            System.out.println(this.getClass().getName() +" has been created ");
            System.out.println(arg.containsOption("debug"));
            System.out.println(arg.getNonOptionArgs());
        }
    }
    

    在启动配置中添加--debug logfile.txt,然后运行程序,可以看到在bean被成功创建之后的输出:


    image.png

    在默认情况下,SpringApplication会将所有命令行参数,即以--开头的参数,转化为一个property,并添加到Spring Environment当中,所以可以通过@Value注解使用单个值。如果想忽略命令行参数对属性值的影响,可以通过SpringApplication.setAddCommandLineProperties(false)禁用。


    application.properties属性文件

    SpringApplication从当前目录下的/config子目录,当前目录,classpath下的config目录,classpath根目录加载配置文件并添加到Spring Environment当中。如果使用spring.config.location指定文件的加载路径则会按照classpath:,classpath:/config,file:,;file:config/顺序,指定的路径会优先于所有默认路径。通过设置spring.config.name可以指定加载的文件名,但必须将spring.config.name和spring.config.location配置为系统属性或命令行参数,以便于程序启动。如果spring.config.location包含子目录,则应该以“/”结尾。

    使用@ConfigurationProperties注解

    @ConfigurationProperties在类上使用,设置prefix属性。
    application.properties中添加如下配置

    test.name=test
    test.id=this is for test
    test.value=success
    

    创建MyProperties并实现org.springframework.beans.factory.InitializingBean接口。

    @Component
    @ConfigurationProperties(prefix="test")
    public class MyProperties implements InitializingBean{
    
        private String name;
        private String id;
        private String value;
        
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getValue() {
            return value;
        }
    
        public void setValue(String value) {
            this.value = value;
        }
        
    
        @Override
        public String toString() {
            return "MyProperties [name=" + name + ", id=" + id + ", value=" + value
                    + "]";
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            // TODO Auto-generated method stub
            System.out.println(this.getClass().getName()+" bean has bean created "+this.toString());
        }
    }
    

    启动程序,观察类的属性值是否被成功映射。


    image.png

    Spring boot 将Environment属性绑定到@configurationProperties注解的bean时并不要求属性完全匹配,以下列举都可以实现匹配。


    image.png
    其次属性在匹配时会尝试进行属性的校验和类型转换。
    属性校验

    使用外部属性校验JSR-303 javax.validation;使用@Valid出发校验。
    例如:

    @Component
    @ConfigurationProperties(prefix="connection")
    public class ConnectionProperties{
    
        @NotNull
        @Valid
        private RemoteAddress remoteAddress;
        
        
        public RemoteAddress getRemoteAddress() {
            return remoteAddress;
        }
    
        public void setRemoteAddress(RemoteAddress remoteAddress) {
            this.remoteAddress = remoteAddress;
        }
    
        public static class RemoteAddress{
            
            @NotNull(message="url must be exist")
            private String url;
            @NotEmpty
            private int port;
            public String getUrl() {
                return url;
            }
            public void setUrl(String url) {
                this.url = url;
            }
            public int getPort() {
                return port;
            }
            public void setPort(int port) {
                this.port = port;
            }
            @Override
            public String toString() {
                return "RemoteAddress [url=" + url + ", port=" + port + "]";
            }
        }
        
        @Override
        public String toString() {
            return "ConnectionProperties [remoteAddress=" + remoteAddress + "]";
        }
    }
    

    使用org.springframework.validation.Validator接口实现属性值的校验。

    public class SamplePropertiesValidator implements Validator{
    
       @Override
       public boolean supports(Class<?> arg0) {
           // TODO Auto-generated method stub
           return arg0 == ConnectionProperties.class;
       }
    
       @Override
       public void validate(Object arg0, Errors arg1) {
           // TODO Auto-generated method stub
           ValidationUtils.rejectIfEmpty(arg1, "url", "url.empty");
           ValidationUtils.rejectIfEmpty(arg1, "port", "port.empty");
           ConnectionProperties properties = (ConnectionProperties) arg0;
           if (properties.getRemoteAddress().getUrl() != null
                   && !properties.getRemoteAddress().getUrl().equals("")) {
               arg1.rejectValue("url", "Invalid url");
           }
       }
    
    }
    

    在@SpringBootApplication注解的类上中添加Validator bean

       @Bean
       public Validator configurationPropertiesValidator(){
           
           return new SamplePropertiesValidator();
       }
    

    运行Spring boot程序,观察结果:


    image.png

    使用@PropertySource注解

    property Source注解同样提供将配置文件添加到Spring Environment的功能。
    在classpath下添加一个属性配置文件,例如app.properties,内容如下:

    app.name=spring boot
    app.author=zhongzhong
    app.detail=test
    

    创建AppConfig类并实现InitializingBean用来查看创建情况。

    @Configuration
    @PropertySource("classpath:app.properties")
    public class AppConfig implements InitializingBean{
    
        @Autowired
        Environment environment;
        
        @Bean
        public App app(){
            
            App app = new App();
            app.setName(environment.getProperty("app.name"));
            app.setDetail(environment.getProperty("app.detail"));
            app.setAuthor(environment.getProperty("app.author"));
            
            System.out.println("app is created");
            return app;
        }
        
        private static class App{
            
            private String name;
            private String author;
            private String detail;
            public String getName() {
                return name;
            }
            public void setName(String name) {
                this.name = name;
            }
            public String getAuthor() {
                return author;
            }
            public void setAuthor(String author) {
                this.author = author;
            }
            public String getDetail() {
                return detail;
            }
            public void setDetail(String detail) {
                this.detail = detail;
            }
            @Override
            public String toString() {
                return "App [name=" + name + ", author=" + author + ", detail="
                        + detail + "]";
            }
            
        }
    
        @Override
        public String toString() {
            return "AppConfig [app()=" + app() + "]";
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            // TODO Auto-generated method stub
            System.out.println(this.getClass().getName() + " "+this.toString());
        }
    }
    

    运行程序:


    image.png

    @propertySource在指定配置文件路径时可以使用占位符,例如:

    @PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
    

    :default 为可选项。使用前提是my.placeholder已经在其他配置文件中定义,如果没有则使用默认路径/path,如果没有默认路径并且my.plcaeholder也不存在,则会抛出IllegalArgumentException。
    如果程序当中有多个配置文件,且文件中key值可能重复需要考虑属性覆盖的问题,根据在Application Context中注册的先后顺序,先注册的配置类中的属性会被后注册的配置类中的属性覆盖。

     AnnotationConfigApplicationContext ctx =
         new AnnotationConfigApplicationContext();
     ctx.register(ConfigA.class);
     ctx.register(ConfigB.class);
     ctx.refresh();
    

    相关文章

      网友评论

        本文标题:5-配置文件

        本文链接:https://www.haomeiwen.com/subject/vmnjoftx.html