Java 杂谈Spring学习笔记

5-配置文件

2018-10-02  本文已影响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();
上一篇下一篇

猜你喜欢

热点阅读