概述
- 微调自动配置的beans
- 应用程序组件配置
- 使用Spring profiles
对自动配置进行微调
1. Spring环境
Spring环境是所有配置属性的一站式商店,它抽象了配置属性的起源,以便需要这些属性的bean可以直接从Spring本身使用它们;Spring环境是从几个属性源获取的,即将这些属性源聚合到一个源(Spring环境)中,再从这个源注入Spring beans中,因此这些属性都可以直接通过Spring环境进行配置
- JVM系统属性
- 操作系统环境变量
- 命令行参数
- 应用程序属性配置文件
eg. 服务器端口配置
src/main/resources/application.properties配置
server.port=9090
*YAML配置方式: * src/main/ resources/application.yml
server: port: 9090
*命令行参数方式: * 启动时指定端口
$ java -jar tacocloud-0.0.5-SNAPSHOT.jar --server.port=9090
*操作系统环境变量方式: * 应用程序总是在一个端口启动
$ export SERVER_PORT=9090
2. 配置DataSource
- 推荐使用YAML配置文件方式配置
#配置MySQL数据源
spring:
datasource:
url: jdbc:mysql://localhost/tacocloud
username: tacodb
password: tacopassword
# 非必须,spring boot可以自动查找到,如果有问题则指定
driver-class-name: com.mysql.jdbc.Driver
schema:
- order-schema.sql
- ingredient-schema.sql
- taco-schema.sql
- user-schema.sql
data:
- ingredients.sql
initialization-mode: always
#如果使用spring需要设置启动时只更新表,不删除重建表
jpa:
show-sql: true
hibernate:
ddl-auto: update
- JNDI方式
spring:
datasource:
jndi-name: java:/comp/env/jdbc/tacoCloudDS
3. 配置内置服务器
端口配置如果设置为0,则启动时自动检测可用随机端口进行使用
server: port: 0
配置HTTPS访问(配置SSL证书)
使用JDK的keytool命令自建一个证书,或者可以去申请一个SSL证书
keytool -keystore mykeys.jks -genkey -alias tomcat -keyalg RSA
Tomcat支持JKS格式证书,从Tomcat7开始也支持PFX格式证书,其他格式需要转换
实例(tomcat为例)
- 配置https访问
# https监听端口为8443 server: port: 8443 ssl: key-store: classpath:mykeys.jks key-store-password: letmein key-password: letmein
- 配置将http请求(8080端口)重定向到https端口(8443)访问
@Configuration public class TomcatConfiguration { @Bean public Connector connector(){ Connector connector=new Connector("org.apache.coyote.http11.Http11NioProtocol"); connector.setScheme("http"); connector.setPort(8080); connector.setSecure(false); connector.setRedirectPort(8443); return connector; } @Bean public TomcatServletWebServerFactory tomcatServletWebServerFactory(Connector connector){ TomcatServletWebServerFactory tomcat=new TomcatServletWebServerFactory(){ @Override protected void postProcessContext(Context context) { SecurityConstraint securityConstraint=new SecurityConstraint(); securityConstraint.setUserConstraint("CONFIDENTIAL"); SecurityCollection collection=new SecurityCollection(); collection.addPattern("/*"); securityConstraint.addCollection(collection); context.addConstraint(securityConstraint); } }; tomcat.addAdditionalTomcatConnectors(connector); return tomcat; } }
4. 配置日志
默认情况下Spring boot通过
Logback
配置日期记录,在info
级别写入控制台,详情参考(http://logback.qos.ch)如果需要自定义详细配置可以在
src/main/resources
目录下创建logback.xml
文件,如:<configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern> %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n </pattern> </encoder> </appender> <logger name="root" level="INFO"/> <root level="INFO"> <appender-ref ref="STDOUT" /> </root> </configuration>
对于比较常见的属性配置(日志级别和设置日志写入文件)可以直接在
application.yml
文件中配置logging: level: root: WARN org.springframework.security: DEBUG file: path: logs # name: taco.log # file.name 和 file.path同时使用只会生效file.name, # 日志默认生成目录为项目根目录,默认文件名为spring.log # 日志默认上限为10M,达到上限就会切换一个新文件 # 对于包名可以合并为一行或者分开 # org: # springframework: # security: DEBUG
5. 使用${}获取指定属性的值
对配置属性的值,不一定需要设置为硬编码形式的值,可以通过
${}
获取其他配置属性值greeting: welcome: You are using ${spring.application.name}.
创建自定义配置属性
1.概述
通过在配置文件中添加自定义配置属性,可以在代码中进行使用,实现更加灵活的功能
1.1 硬编码方式
//例如在此查询时当前页查询条数Pageable直接使用硬编码方式不够灵活,可以使用从配置文件加载自定义配置属性
//Pageable对象是Spring Data提供的一种选择子集的分页方式
@GetMapping
public String ordersForUser(
@AuthenticationPrincipal User user, Model model) {
Pageable pageable = PageRequest.of(0, 20); //硬编码方式,不灵活
model.addAttribute("orders",
orderRepo.findByUserOrderByPlacedAtDesc(user, pageable));
return "orderList";
}
1.2 使用自定义配置方式
domain
@Data @Entity @Table(name = "POP_IN_STOCK") public class PopInStock { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(name = "TASKID") private Integer task; @Column(name = "POP_CODE") private String code; @Column(name = "POP_NAME") private String name; private String version; private String brand; @Column(name = "IN_STOCK_COUNT") @JsonProperty(value = "count") private Double inStockCount; private String comment; }
properties:
application.yml
/application.properties
stock: page-size: 15 # 与下相同 #stock: # pageSize: 15
Controller
@Controller @RequestMapping("/stock") @ConfigurationProperties(prefix = "stock") //引入前缀为stock的配置属性 stock.pageSize = 15 public class PopStockController { private JpaPopInStockRepository popInStockRepository; @Autowired public void setPopInStockRepository(JpaPopInStockRepository popInStockRepository) { this.popInStockRepository = popInStockRepository; } //会根据自定义的配置属性自动设置pageSize的值 private int pageSize = 1; //默认值为1 public void setPageSize(int pageSize) { this.pageSize = pageSize; } @GetMapping public String show(){ return "jpa/popInStock"; } @ResponseBody //list转json返回 @PostMapping public String process() throws JsonProcessingException { Pageable pageable = PageRequest.of(0,pageSize); List<PopInStock> list = popInStockRepository.findPopInStocksByOrderByTaskDesc(pageable); return new ObjectMapper().writeValueAsString(list); } }
Repository
public interface JpaPopInStockRepository extends CrudRepository<PopInStock,Integer> { List<PopInStock> findPopInStocksByOrderByTaskDesc(Pageable pageable); }
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Stock</title> <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> </head> <body> <div id="app"> <table> <tr> <td>TASKID</td> <td>CODE</td> <td>NAME</td> <td>BRAND</td> <td>COUNT</td> </tr> <tr v-for="item in items"> <td>{{ item.task }}</td> <td>{{ item.code }}</td> <td>{{ item.name }}</td> <td>{{ item.brand }}</td> <td>{{ item.count }}</td> </tr> </table> </div> <script> new Vue({ el: '#app', data () { return { items: null } }, mounted () { axios .post('/stock').then(response => (this.items = response.data)) .catch(function (error) { console.log(error); }); } }) </script> </body> </html>
2. 使用配置类管理自定义属性推荐
虽然直接使用
@ConfigurationProperties
(注解一般放在bean上)可以使用自定义属性,但是更好的方式是使用自定义一个holder类来管理这些自定义属性,并且能对属性的值进行校验,其他对象需要使用时常规的注入此bean即可
配置类holder
@Data @Component @ConfigurationProperties(prefix = "stock") @Validated //数据校验注解 public class ViewProps { @Max(value = 25,message = "必须在5~25之间") @Min(value = 5,message = "必须在5~25之间") private int pageSize = 1; }
Controller: 使用,其他代码同上例
@Controller @RequestMapping("/stock") //@ConfigurationProperties(prefix = "stock") //引入前缀为stock的配置属性 stock.pageSize = 15 public class PopStockController { private JpaPopInStockRepository popInStockRepository; @Autowired public void setPopInStockRepository(JpaPopInStockRepository popInStockRepository) { this.popInStockRepository = popInStockRepository; } //会根据自定义的配置属性自动设置pageSize的值 /* private int pageSize = 1; //默认值为1 public void setPageSize(int pageSize) { this.pageSize = pageSize; }*/ private ViewProps viewProps; @Autowired //注入: 配置类holder的bean public void setViewProps(ViewProps viewProps) { this.viewProps = viewProps; } @GetMapping public String show(){ return "jpa/popInStock"; } @ResponseBody //list转json返回 @PostMapping public String process() throws JsonProcessingException { //Pageable pageable = PageRequest.of(0,pageSize); Pageable pageable = PageRequest.of(0,viewProps.getPageSize()); List<PopInStock> list = popInStockRepository.findPopInStocksByOrderByTaskDesc(pageable); return new ObjectMapper().writeValueAsString(list); } }
3. 声明配置属性元数据
创建配置属性元数据即给自定义的配置属性增加一种注释,可以在使用属性时,工具能根据自定义的注释进行代码提示
虽然不声明配置属性元数据,不影响程序运行,但是还是推荐添加元数据,确保自定义的配置属性有信息提示
增加可读性
引入Maven依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
以
IDEA
为例: 鼠标移动到自定义属性上面,然后点击弹窗的Define configuration key 'xxx'
或快捷键Alt+Shift+Enter
然后会生成文件src/main/resources/META-INF/additional-spring-configuration-metadata.json
在
additional-spring-configuration-metadata.json
中修改description
等即可{ "properties": [ { "name": "stock.page-size", "type": "java.lang.String", "description": "每页记录数." } ] }
配置profiles
通过配置profiles可以实现在不同的环境中加载不同的配置,按条件注册bean,如开发环境和生产环境使用不同的配置
1. 创建profile配置文件
1.1 方式一: 定义一个只在生产环境使用的配置文件
文件命名规则:
application-{profile name}.yml
或application-{profile name}.properties
如
application-prob.yml
logging: level: root: WARN org.springframework.security: WARN file: path: logs
1.2 方式二: 直接在application.yml
中写,只支持YAML配置方式
logging:
level:
root: WARN
org.springframework.security: DEBUG
file:
path: logs
# 使用 --- 分隔,下面的为prod配置,等价于创建一个application-prod.yml文件并写入
---
spring:
profiles: prod
logging:
level:
root: WARN
org.springframework.security: WARN
2. 激活profile配置文件
默认情况下使用的是
application.yml
或application.properties
文件中spring.profiles.default
的配置(可以省略),如果要使用其他的profile则需要激活
spring.profiles.default
默认值,优先级低,当active没有配置时,使用此变量
spring.profiles.active
优先级高,指定当前容器使用哪个profile。
直接在
application.yml
文件配置进行激活(硬编码方式不推荐
)激活单个配置文件
spring: profiles: active: prod
激活多个配置文件
spring: profiles: active: - prod - adult - ha
通过环境变量进行激活(
推荐
)普通方式
% export SPRING_PROFILES_ACTIVE=prod
如果使用可执行的jar运行的应用程序
% java -jar taco-cloud.jar --spring.profiles.active=prod
激活多个配置文件
% export SPRING_PROFILES_ACTIVE=prod,audit,ha
3. 根据不同的profile配置注册不同的bean
对于配置的bean有些可能只在某个profile配置激活的情况下才需要创建,因此需要使用@Profile注解进行按条件注册bean
@Bean
@Profile("dev") //dev激活时创建
public CommandLineRunner dataLoader(IngredientRepository repo,
UserRepository userRepo, PasswordEncoder encoder) {
//...
}
@Bean
@Profile({"dev", "qa"}) //dev或者qa激活时创建
public CommandLineRunner dataLoader(IngredientRepository repo,
UserRepository userRepo, PasswordEncoder encoder) {
//...
}
@Bean
@Profile("!prod") //prod没有激活时创建
public CommandLineRunner dataLoader(IngredientRepository repo,
UserRepository userRepo, PasswordEncoder encoder) {
//...
}
@Profile({"!prod", "!qa"}) //可在类上使用,prob和qa没有激活时创建
@Configuration
public class DevelopmentConfig {
@Bean
public CommandLineRunner dataLoader(IngredientRepository repo,
UserRepository userRepo, PasswordEncoder encoder) {
//...
}
}