Spring Boot 配置优先级顺序

通常在一个项目中,老是会有好多个环境。好比:javascript

开发环境 -> 测试环境 -> 预发布环境【验证环境】 -> 生产环境html

每一个环境上的配置文件老是不同的,甚至开发环境中每一个开发者的环境可能也会有一点不一样,配置读取但是一个让人有点伤脑筋的问题。java

Spring Boot提供了一种优先级配置读取的机制来帮助咱们从这种困境中走出来。mysql

常规状况下,咱们都知道Spring Boot的配置会从application.properties中读取。实际上,从resource目录下的application.properties文件读取是Spring Boot配置链中的一环而已。linux

外部化的配置

在应用中管理配置并非一个容易的任务,尤为是在应用须要部署到多个环境中时。一般会须要为每一个环境提供一个对应的属性文件,用来配置各自的数据库链接信息、服务器信息和第三方服务帐号等。一般的应用部署会包含开发、测试和生产等若干个环境。不一样的环境之间的配置存在覆盖关系。测试环境中的配置会覆盖开发环境,而生产环境中的配置会覆盖测试环境。Spring 框架自己提供了多种的方式来管理配置属性文件。
Spring 3.1 以前可使用 PropertyPlaceholderConfigurer【继承PropertyPlaceholderConfigurer读取配置文件,相关示例代码见下】
Spring 3.1 引入了新的环境(Environment)和概要信息(Profile)API,是一种更加灵活的处理不一样环境和配置文件的方式。不过 Spring 这些配置管理方式的问题在于选择太多,让开发人员无所适从。Spring Boot 提供了一种统一的方式来管理应用的配置,容许开发人员使用属性文件、YAML 文件、环境变量和命令行参数来定义优先级不一样的配置值。web

Spring Boot 所提供的配置优先级顺序比较复杂。按照优先级从高到低的顺序,具体的列表以下所示。spring

  1. 命令行参数。
  2. 经过 System.getProperties() 获取的 Java 系统参数。
  3. 操做系统环境变量。
  4. 从 java:comp/env 获得的 JNDI 属性。
  5. 经过 RandomValuePropertySource 生成的“random.*”属性。
  6. 应用 Jar 文件之的属性文件。(经过spring.config.location参数)
  7. 应用 Jar 文件内部的属性文件。
  8. 在应用配置 Java 类(包含“@Configuration”注解的 Java 类)中经过“@PropertySource”注解声明的属性文件。
  9. 经过“SpringApplication.setDefaultProperties”声明的默认属性。

Spring Boot 的这个配置优先级看似复杂,实际上是很合理的。好比命令行参数的优先级被设置为最高。
这样的好处是能够在测试或生产环境中快速地修改配置参数值,而不须要从新打包和部署应用。sql

SpringApplication 类默认会把以“--”开头的命令行参数转化成应用中可使用的配置参数,如 “--name=Alex” 会设置配置参数 “name” 的值为 “Alex”。若是不须要这个功能,能够经过 “SpringApplication.setAddCommandLineProperties(false)” 禁用解析命令行参数。数据库

RandomValuePropertySource 能够用来生成测试所须要的各类不一样类型的随机值,从而免去了在代码中生成的麻烦。RandomValuePropertySource 能够生成数字和字符串。数字的类型包含 int 和 long,能够限定数字的大小范围。以“random.”做为前缀的配置属性名称由 RandomValuePropertySource 来生成,如代码清单 3 所示。tomcat

清单 3. 使用 RandomValuePropertySource 生成的配置属性
user.id=${random.value}
user.count=${random.int}
user.max=${random.long}
user.number=${random.int(100)}
user.range=${random.int[100, 1000]}

属性文件

属性文件是最多见的管理配置属性的方式。Spring Boot 提供的 SpringApplication 类会搜索并加载 application.properties 文件来获取配置属性值。SpringApplication 类会在下面位置搜索该文件。

  • 当前目录的“/config”子目录。
  • 当前目录。
  • classpath 中的“/config”包。
  • classpath

上面的顺序也表示了该位置上包含的属性文件的优先级。优先级按照从高到低的顺序排列。能够经过“spring.config.name”配置属性来指定不一样的属性文件名称。也能够经过“spring.config.location”来添加额外的属性文件的搜索路径。若是应用中包含多个 profile,能够为每一个 profile 定义各自的属性文件,按照“application-{profile}”来命名。

对于配置属性,能够在代码中经过“@Value”来使用,如代码清单 4 所示。

清单 4. 经过“@Value”来使用配置属性
@RestController
@EnableAutoConfiguration
public class Application {
 @Value("${name}")
 private String name;
 @RequestMapping("/")
 String home() {
 return String.format("Hello %s!", name);
 }
}

代码清单 4 中,变量 name 的值来自配置属性中的“name”属性。

YAML

相对于属性文件来讲,YAML 是一个更好的配置文件格式。YAML 在 Ruby on Rails 中获得了很好的应用。SpringApplication 类也提供了对 YAML 配置文件的支持,只须要添加对 SnakeYAML 的依赖便可。代码清单 5 给出了 application.yml 文件的示例。

清单 5. 使用 YAML 表示的配置属性
spring:
 profiles: development
db:
 url: jdbc:hsqldb:file:testdb
 username: sa
 password:
---
spring:
 profiles: test
db:
 url: jdbc:mysql://localhost/test
 username: test
 password: test

代码清单 5 中的 YAML 文件同时给出了 development 和 test 两个不一样的 profile 的配置信息,这也是 YAML 文件相对于属性文件的优点之一。
除了使用“@Value”注解绑定配置属性值以外,还可使用更加灵活的方式。
代码清单 6 给出的是使用代码清单 5 中的 YAML 文件的 Java 类。
经过“@ConfigurationProperties(prefix="db")”注解,配置属性中以“db”为前缀的属性值会被自动绑定到 Java 类中同名的域上,如 url 域的值会对应属性“db.url”的值。
只须要在应用的配置类中添加“@EnableConfigurationProperties”注解就能够启用该自动绑定功能。

清单 6. 使用 YAML 文件的 Java 类
@Component
@ConfigurationProperties(prefix="db")
public class DBSettings {
 private String url;
 private String username;
 private String password;
}

http://www.ibm.com/developerworks/cn/java/j-lo-spring-boot/index.html

 

 

这意味着,若是Spring Boot在优先级更高的位置找到了配置,那么它就会无视优先级低的配置

好比,我在application.properties目录中,写入本地的MySQL的配置:

db.jdbc.driver=com.mysql.jdbc.Driver
db.jdbc.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8
db.jdbc.username=username
db.jdbc.password=password

在本身项目调试的阶段,项目老是会使用本地的MySQL数据库。而一旦打包以后,在外部声明一个test_evn.properties.

启动Jar包的时候, 指定一个外部配置文件:

java -jar demo.jar --spring.config.location=/path/test_evn.properties

这样一来,咱们在开发者的机器上老是使用本身的配置,而一到对应的环境,就会使用高级的位置所作的配置。

在代码中读取这些配置也是很是方便的,在代码的逻辑中,实际上是无需去关心这个配置是从什么地方来的,只用关注能获取什么配置就够了。

public class ApplicationConfigure {

    @Value("${db.jdbc.driver}")
    private String jdbcDriver;
    @Value("${db.jdbc.url}")
    private String jdbcUrl;
    @Value("${db.jdbc.username}")
    private String jdbcUsername;
    @Value("${db.jdbc.password}")
    private String jdbcPassword;
    
    // mysql config class
    // ..... 
    
}

 

有时候咱们在项目启动的时候,老是须要先启动一些初始化的类,之前比较常见的作法是写再static块中,Spring Boot提供了一个CommandLineRunner接口,实现这个接口的类老是会被优先启动,并优先执行CommandLineRunner接口中提供的run()方法。

public class ApplicationConfigure implements CommandLineRunner  {

    @Value("${db.jdbc.driver}")
    private String jdbcDriver;
    @Value("${db.jdbc.url}")
    private String jdbcUrl;
    @Value("${db.jdbc.username}")
    private String jdbcUsername;
    @Value("${db.jdbc.password}")
    private String jdbcPassword;
    
    // mysql config class
    // ..... 
    @Override
    public void run(String... strings) throws Exception {
        // 预先加载的一些方法,类,属性。
    }
}

 

若是有多个CommandLineRunner接口实现类,那么能够经过注解@Order来规定全部实现类的运行顺序。

经过这一系列API的帮助,Spring Boot让环境配置变得轻松不少。

http://www.cnblogs.com/whthomas/p/5270917.html

http://www.ibm.com/developerworks/cn/java/j-lo-spring-boot/index.html

Tomcat
Tomcat为Spring Boot的默认容器,下面是几个经常使用配置:

# tomcat最大线程数,默认为200
server.tomcat.max-threads=800
# tomcat的URI编码
server.tomcat.uri-encoding=UTF-8
# 存放Tomcat的日志、Dump等文件的临时文件夹,默认为系统的tmp文件夹(如:C:\Users\Shanhy\AppData\Local\Temp)
server.tomcat.basedir=H:/springboot-tomcat-tmp
# 打开Tomcat的Access日志,并能够设置日志格式的方法:
#server.tomcat.access-log-enabled=true
#server.tomcat.access-log-pattern=
# accesslog目录,默认在basedir/logs
#server.tomcat.accesslog.directory=
# 日志文件目录
logging.path=H:/springboot-tomcat-tmp
# 日志文件名称,默认为spring.log
logging.file=myapp.log

Jetty 

若是你要选择Jetty,也很是简单,就是把pom中的tomcat依赖排除,并加入Jetty容器的依赖,以下:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
      <exclusion>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
      </exclusion>
    </exclusions>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
  </dependency>
<dependencies> 

打包 

打包方法:
CMD进入项目目录,使用 mvn clean package 命令打包,以个人项目工程为例:

E:\spring-boot-sample>mvn clean package

能够追加参数 -Dmaven.test.skip=true (-DskipTests)跳过测试。 

打包后的文件存放于项目下的target目录中,如:spring-boot-sample-0.0.1-SNAPSHOT.jar
若是pom配置的是war包,则为spring-boot-sample-0.0.1-SNAPSHOT.war

2、部署到JavaEE容器

  1. 修改启动类,继承 SpringBootServletInitializer 并重写 configure 方法
    public class SpringBootSampleApplication extends SpringBootServletInitializer{
    
        private static final Logger logger = LoggerFactory.getLogger(SpringBootSampleApplication.class);
    
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
            return builder.sources(this.getClass());
        }
    
    }
  1. 修改pom文件中jar 为 war
<!-- <packaging>jar</packaging> -->
<packaging>war</packaging>
  1. 修改pom,排除tomcat插件

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>

<exclusions>

<exclusion>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-tomcat</artifactId>

</exclusion>

</exclusions>

</dependency>

  1. 打包部署到容器
    使用命令 mvn clean package 打包后,同通常J2EE项目同样部署到web容器。

3、使用Profile区分环境

spring boot 能够在 “配置文件”、“Java代码类”、“日志配置” 中来配置profile区分不一样环境执行不一样的结果

一、配置文件
使用配置文件application.yml 和 application.properties 有所区别
以application.properties 为例,经过文件名来区分环境 application-{profile}.properties
application.properties

app.name=MyApp
server.port=8080
spring.profiles.active=dev

application-dev.properties

server.port=8081

application-stg.properties

server.port=8082

在启动程序的时候经过添加 –spring.profiles.active={profile} 来指定具体使用的配置 

例如咱们执行 java -jar demo.jar spring.profiles.active=dev 那么上面3个文件中的内容将被如何应用?
Spring Boot 会先加载默认的配置文件,而后使用具体指定的profile中的配置去覆盖默认配置。

app.name 只存在于默认配置文件 application.properties 中,由于指定环境中不存在一样的配置,因此该值不会被覆盖 
server.port 默认为8080,可是咱们指定了环境后,将会被覆盖。若是指定stg环境,server.port 则为 8082 
spring.profiles.active 默认指定dev环境,若是咱们在运行时指定 –spring.profiles.active=stg 那么将应用stg环境,最终 server.port 的值为8082

二、Java类中@Profile注解
下面2个不一样的类实现了同一个接口,@Profile注解指定了具体环境

// 接口定义
public interface SendMessage {

    // 发送短信方法定义
    public void send();

}
 
// Dev 环境实现类
@Component
@Profile("dev")
public class DevSendMessage implements SendMessage {

    @Override
    public void send() {
        System.out.println(">>>>>>>>Dev Send()<<<<<<<<");
    }

}

 

// Stg环境实现类
@Component
@Profile("stg")
public class StgSendMessage implements SendMessage {

    @Override
    public void send() {
        System.out.println(">>>>>>>>Stg Send()<<<<<<<<");
    }

}
// 启动类
@SpringBootApplication
public class ProfiledemoApplication {

    @Value("${app.name}")
    private String name;

    @Autowired
    private SendMessage sendMessage;

    @PostConstruct
    public void init(){
        sendMessage.send();// 会根据profile指定的环境实例化对应的类
    }

}

三、logback-spring.xml也支持有节点来支持区分

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml" />
    <logger name="org.springframework.web" level="INFO"/>

    <springProfile name="default">
        <logger name="org.springboot.sample" level="TRACE" />
    </springProfile>

    <springProfile name="dev">
        <logger name="org.springboot.sample" level="DEBUG" />
    </springProfile>

    <springProfile name="staging">
        <logger name="org.springboot.sample" level="INFO" />
    </springProfile>

</configuration>

再说一遍文件名不要用logback.xml 请使用logback-spring.xml

4、指定外部的配置文件

有些系统,关于一些数据库或其余第三方帐户等信息,因为安全问题,其配置并不会提早配置在项目中暴露给开发人员。
对于这种状况,咱们在运行程序的时候,能够经过参数指定一个外部配置文件。
以 demo.jar 为例,方法以下:

java -jar demo.jar --spring.config.location=/opt/config/application.properties

其中文件名随便定义,无固定要求。

5、建立一个Linux 应用的sh脚本

下面几个脚本仅供参考,请根据本身须要作调整
start.sh

#!/bin/sh

rm -f tpid

nohup java -jar /data/app/myapp.jar --spring.profiles.active=stg > /dev/null 2>&1 &

echo $! > tpid

 

stop.sh

tpid=`cat tpid | awk '{print $1}'`

tpid=`ps -aef | grep $tpid | awk '{print $2}' |grep $tpid`
if [ ${tpid} ]; then
   kill -9 $tpid
fi

check.sh

tpid=`cat tpid | awk '{print $1}'`
tpid=`ps -aef | grep $tpid | awk '{print $2}' |grep $tpid`
if [ ${tpid} ]; then
    echo App is running.
else
    echo App is NOT running.
fi

kill.sh

#!/bin/sh
# kill -9 `ps -ef|grep 项目名称|awk '{print $2}'`
kill -9 `ps -ef|grep demo|awk '{print $2}'`


继承PropertyPlaceholderConfigurer.从spring容器获取取配置文件属性

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
 
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
 
 
public class CustomPropertyConfig extends  PropertyPlaceholderConfigurer{
    
    private static Map<String, Object> ctxPropertiesMap;
    
    @Override  
    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,Properties props) throws BeansException {  
        super.processProperties(beanFactoryToProcess, props);  
        ctxPropertiesMap = new HashMap<String, Object>();  
        for (Object key : props.keySet()) {
            String keyStr = key.toString();
            String value = props.getProperty(keyStr);
            ctxPropertiesMap.put(keyStr, value);
        }
    }
  
    public static Object getContextProperty(String name) {
        return ctxPropertiesMap.get(name);
    }
}

 

<bean id="propertyConfigurer"  
        class="com.test.utils.CustomPropertyConfig">  
        <property name="locations">
            <list>
                <!-- org.springframework.beans.factory.config.PropertyPlaceholderConfigurer这里支持多种寻址方式:classpath和file -->
                <!-- 推荐使用file的方式引入,这样能够将配置和代码分离 -->
<!--                 <value>classpath:jdbc.properties</value> -->
                <value>classpath:dbconfig.properties</value>
            </list>
        </property>  
    </bean>

https://blog.csdn.net/y_index/article/details/79893765


实现BeanFactoryAware来达到Spring静态方法获取Bean对象的BeanUtil工具类

在spring里自己是没有提供这类方法的。

因此咱们只能经过重写一些类来达到咱们想要的效果
在容器初始化时注入Bean工厂,并提供一些列静态方法,用于运行期间任何地方均可以用过他来获许对应Bean

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

public class BeanUtil implements  BeanFactoryAware{
     private static BeanFactory beanFactory;  
        // private static ApplicationContext context;  
      
     @Override
        public void setBeanFactory(BeanFactory factory) throws BeansException {  
            this.beanFactory = factory;  
        }  
      
        /** 
         * 根据beanName名字取得bean 
         *  
         * @param beanName 
         * @return 
         */  
        public static <T> T getBean(String beanName) {  
            if (null != beanFactory) {  
                return (T) beanFactory.getBean(beanName);  
            }  
            return null;  
        }  
      
}

https://www.hellojava.com/a/27.html

相关文章
相关标签/搜索