《SpringBoot2从入门到工程实战》第二篇:深入配置实战

在本章中,我会详细介绍除了SpringBoot默认加载的配置外,如何加载自定义的配置,以及按不同环境不同业务需求加载不同的配置文件。

在第一篇文章中,我们说过,SpringBoot默认加载的配置文件名称为:application*.yml,application*.yaml,application*.properties,但是从工作实战中,我们是不可能把所有的项目配置都放置在一个配置文件,也不可能把所有的配置文件都起名为application*.properties这样的形式,原因很简单:太难看、太难维护了。所以本章,我们就来学习在SpringBoot中如何使用默认的配置文件和加载自定义的配置文件。

本章示例工程名称:springboot_worker_config

代码地址:https://github.com/stamhe/SpringBoot-Work-Example

目录结构如下:

注:如果没有src/main/resources目录,新建一个就行了。

pom.xml内容:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.stamhe</groupId>
  <artifactId>springboot_worker_config</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>springboot_worker_config</name>
  <url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
	</properties>
  
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

  	<dependencies>
    	<dependency>
        	<groupId>org.springframework.boot</groupId>
        	<artifactId>spring-boot-starter-test</artifactId>
        	<scope>test</scope>
    	</dependency>
    
	    <dependency>
	        <groupId>org.springframework.boot</groupId>
	        <artifactId>spring-boot-starter-web</artifactId>
	    </dependency>
    
	    <dependency>
	      <groupId>junit</groupId>
	      <artifactId>junit</artifactId>
	      <scope>test</scope>
	    </dependency>
	</dependencies>

	<!-- 
	使用Spring Boot微服务搭建框架,在eclipse和Idea下能正常运行,但是在打成jar包部署或者直接使用java -jar命令的时候,
	提示了xxxxxx.jar中没有主清单属性.
	添加 spring-boot-maven-plugin然后再执行mvn install 或者 mvn clean package 即可解决.
	-->
	<build>
	  <plugins>
	  	<plugin>
	  		<groupId>org.springframework.boot</groupId>
	 		<artifactId>spring-boot-maven-plugin</artifactId>
	  	</plugin>
	  </plugins>
 	</build>
</project>

pom.xml的内容,跟第一章的内容完全一样,不再赘述。

App.java内容:

package com.stamhe.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App 
{
    public static void main( String[] args )
    {
    	SpringApplication.run(App.class, args);
    }
}

发现跟第一章内容还是完全一样,因为这是简单示例,基本是定式的。

CommonConfig.java内容:

package com.stamhe.springboot.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix="siteinfo")
public class CommonConfig {
	private String url;

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}
}

SiteConfig.java内容:

package com.stamhe.springboot.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource("classpath:/site.properties")
@ConfigurationProperties(prefix="siteinfo")
public class SiteConfig {
	private String url1;
	private String url2;
	public String getUrl1() {
		return url1;
	}
	public void setUrl1(String url1) {
		this.url1 = url1;
	}
	public String getUrl2() {
		return url2;
	}
	public void setUrl2(String url2) {
		this.url2 = url2;
	}
}

CommonConfig和SiteConfig都是两个配置类,他们都是从配置文件中加载相关的配置参数,变化为Java Bean,供其他业务端使用。

@PropertySource可以指定加载的配置文件名称,比如你的mysql、redis、memcache配置文件,可以分别使用mysql.properties、redis.properties、memcache.properties这样的形式,这样各种业务的配置分离开,便于维护、一目了然。这种使用,我们后续在介绍整合mysql、redis、memcache的时候,都会使用到。

@PropertySource也可以指定配置加载的绝对路径以及一次加载多个配置文件,如下:

@PropertySource({
	"classpath:/memcache.properties", 
	"file:/opt/conf/mysql.properties", 
	"file:/opt/conf/redis.properties"
})

注意:

这种加载是有顺序的,后面key,会覆盖掉前面的同名key的值。

@ConfigurationProperties(prefix=”siteinfo”) 注解的使用,是用来标识当前类里面的属性,都使用前面为prefix指定的前缀做拼接,达到简化代码的目的。当然了,我们还是可以继续用传统的@Value指定的方式,如下:

@Value("${siteinfo.url1}")
private String url1;

@Value("${siteinfo.url2}")
private String url2;

HelloController.java内容:

package com.stamhe.springboot.controller;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.stamhe.springboot.config.CommonConfig;
import com.stamhe.springboot.config.SiteConfig;

@RestController
@RequestMapping("/hello")
public class HelloController {
	
	@Autowired
	private SiteConfig siteConfig;
	
	@Autowired
	private CommonConfig commonConfig;

	@RequestMapping("/world")
	public String worldAction()
	{
		return commonConfig.getUrl();
	}
	
	@RequestMapping("/list")
	public Map<String, String> listAction()
	{
		HashMap<String, String> map = new HashMap<>();
		map.put("url1", siteConfig.getUrl1());
		map.put("url2", siteConfig.getUrl2());
		
		return map;
	}
}

基本跟第一章的内容一样,只是新增了一个函数,返回Map数据,其实最终就是返回json数据了。

application.properties内容:

siteinfo.url=https://www.google.com

site.properties内容:

siteinfo.url1=http://www.stamhe.com
siteinfo.url2=https://www.qq.com

各种代码和配置准备完毕,启动起来吧。

访问:http://localhost:8080/hello/world 你会得到:

https://www.google.com

访问 http://localhost:8080/hello/list 你会得到:

{"url1":"http://www.stamhe.com","url2":"https://www.qq.com"}

怎么样?是不是很简单?好了,简单的介绍完了,下面我们说点儿不一样的。

一般的开发流程,都是会有开发环境、生产环境,多数还会有测试环境、灰度环境等很多不同的环境,SpringBoot为我们提供了 spring.profiles.active=dev参数来指定环境(在application.properties中指定或者java -jar xxx.jar –spring.profiles.active=dev指定),同时不同的工作环境,会加载不同的配置文件,如你指定 spring.profiles.active=dev,则会加载application-dev.properties,如你指定spring.profiles.active=test,则会加载application-test.properties。这种对于这种简单的场景开发,都是有用的,比如用来放置不同的数据库连接信息、不同的redis连接信息、不同的回调url。很多文章、博客、甚至很多工程实践,都是这么说、这么做的。

但是,对于大型工程项目来说,这样是不对的,主要有以下几点原因:

  • 安全性无法保障,我们都知道,如果直接把配置项放置在src/main/resources目录下,这样每个有代码权限的人,都可以拿到配置文件,各种数据库的连接信息,秘钥文件,一览无余,这个在大型工程项目中,根本就不应该这样做,也不应该允许这么做。
  • 不管是Java这种静态编译型语言,还是PHP这种动态脚本语言,从项目的工程实践上说,业务代码逻辑,应该是脱离配置文件工作的、应该是无状态的,这样方便打包、方便发布、方便备份、方便回滚。
  • application-dev.properties、application-test.properties的使用,又回到了我们前面说的,这种配置太冗余了、太难看了、太难维护了,不是项目的初始化开发者,压根不知道应该直接去哪个配置文件找配置项,只能挨个找,不是一种对的工程实践。

综上,这也是这一系列文章和其他教程的本质区别,我更趋向于用一种实际工程的实战来写这个系列文章。

那么,这些问题怎么解决呢?

答案就是使用绝对路径的统一配置文件名,如前面的例子:

@PropertySource({
	"file:/opt/conf/mysql.properties", 
	"file:/opt/conf/redis.properties"
})

这种配置模式,你会发现,在各个不同环境的服务器,只需要配置好不同的配置文件,那么,打包好的jar包,线上线下各个环境,都可以用同一个,压根不用关注环境问题,也满足安全性要求,更方便维护和让配置文件的结构清晰。

这些,我们在后面的系列文章中,都会有详细的体现。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据