作者:汤圆
个人博客:javalover.cc
核心功能
- 起步依赖:各种starter
- 自动配置:是一个运行时(应用程序启动时)的过程
- 比如Spring的JdbcTemplate是不是在classPath里?如果是,并且有DataSource的Bean,则自动配置一个JdbcTemplate的Bean
- 类似的还有安全、继承、持久化、测试等都是自动配置
- 核心依赖: SpringBoot 的条件化配置
- 比如:@ConditionalOnClass({DataSource.class})这个注解意味着,只有classpath中包含DataSource类时,才会定义当前类(举的这个例子是DataSourceAutoConfiguration类的注解的一部分)
自定义配置
-
多种配置方式:
- 添加命令行参数
- JNDI属性
- JVM系统属性
- 操作系统环境变量
- properties / yaml 配置文件
-
优先级:
- 上面的顺序就是按照优先级从高到低排的
- properties / yaml 配置文件放在不同目录,优先级也不同,从高到低依次是
- 外置,运行应用程序目录/config目录
- 外置,运行应用程序的目录
- 内置,config包内
- 内置,classpath根目录
- 如果同一优先级,同时有 application.properties 和 application.yaml,那么 properties 会被 yaml 覆盖
-
配置文件的简单使用
- thymeleaf缓存,默认开启,可以通过yaml或者环境变量关闭
- 配置https访问(本地生成密钥进行访问)
- 配置日志
- 配置数据源
-
加载配置属性
- 通过Bean加载,示例代码如下:
// FactoryProperties.java
@ConfigurationProperties(prefix = "factory")
public class FactoryProperties {
private String name;
// 这里set注入的name就是factory.name,即application.properties中的factory.name
public void setName(String name){
this.name = name;
}
}
# application.properties
factory.name = "大工厂"
-
通过@value()注解加载
public class ApplicationProperty { @Value("${application.name}") private String name; @Value("${application.version}") private String version; }
-
Profile配置:不同的运行环境,进行动态配置
比如,生产环境想显示warn级别的日志,而开发环境想显示debug级别的日志,有多种方式
- 定义两个application-{profile}.properties文件,profile分别为development和production,然后在application.properties中指定用哪个配置文件的属性
# application.properties # 默认打印级别为info spring: profiles: active: production
# application-development.properties # 开发环境打印级别为 debug logging.level.root=debug
# application-production.properties # 生产环境打印级别为 warn logging.level.root=warn
- 通过运行环境设置,可以通过增加
-D
运行参数或者spring.profiles.active
指定
通过 -D 指定运行环境
# 注意 -D 要放在主类或者jar包前面 $ java -jar -Dspring.profiles.active=production demo-0.0.1-SNAPSHOT.jar
- 使用一个yml配置会更简洁,直接把3个配置都写在一个文件里
server: port: 9000 spring: profiles: active: production # 此时会才用生产环境的配置 --- spring: profiles: development server: port: 9001 --- spring: profiles: production server: port: 9002
测试
测试控制层
-
@WebMvcTest 配合 @MockBean 一起使用,如果没有@MockBean,则无法启动应用程序
-
只会运行在控制层,不会涉及到真实的Service以及Mapper层,不启动内置服务器
import static org.mockito.BDDMockito.given; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(FactoryController.class) class FactoryControllerTest{ @MockBean private FactoryMapper mapper; @Test void findAll(@Autowired MockMvc mockMvc) throws Exception { given(this.mapper.findByName("a")).willReturn(new Factory()); // get请求 mockMvc.perform(get("/factory2/api/find-all-factory")).andExpect(status().isOk()).andExpect(content().string("Hello World")); // post请求 // 注:必须指定contentType,否则返回415 Unsupported Media Type // 例:下面这个是post请求,内容类型为application/json,序列化是alibaba的fastjson包 mockMvc.perform(post("/factory2/api/save-factory").contentType(MediaType.APPLICATION_JSON).content(JSON.toJSONString(factory))) .andExpect(content().string("1")); } }
测试服务层
类似测试整个应用,也是要启动web服务,只是不调用控制层,而是直接调用服务层
-
配置web环境:
@SpringBootTest
-
注入对应的服务
import com.v11jie.gps.entity.Fence; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import static org.junit.jupiter.api.Assertions.*; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class FenceServiceTest { @Autowired FenceService service; @Test void add() { int res = service.add(Fence.builder().id(1).name("围栏001").build()); assertEquals(res, 1); } }
测试整个应用
-
注入
TestRestTemplate
,并执行相应的请求(Spring5.0以后建议使用WebClient) -
配置web环境,设置一个随机端口或固定端口,启动整个应用服务器:(默认的webEnvironment不启动整个应用)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
注:如果不启动整个应用服务器,则无法注入TestRestTemplate,报错
NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.test.web.client.TestRestTemplate' available;
package com.v11j.demo;
import com.v11j.demo.entity.Factory;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class FactoryApplicationTests {
@Test
public void contextLoads() {
}
@Test
void exampleTest(@Autowired TestRestTemplate client){
Factory object = Factory.builder().name("jalon").number("001").build();
String res = client.postForObject("/api/insert-factory", object, String.class);
assertEquals(res, "1");
}
}
深入 Actuator
Actuator内部提供了许多端点(包括web端点和JMX端点),通过这些端点可以了解应用程序的内部状况,比如Bean的装配关系、程序的环境属性等
1. 开启 Actuator 功能
通过引入spring-boot-starter-actuator
依赖来自动开启
2. 核心功能
端点访问时需加/actuator前缀,比如/health的访问路径为/actuator/health
2.1. 开启端点
端点默认都是开启的,除了shutdown
开启命令如下
management.endpoint.shutdown.enabled=true
也可以反向开启,就是先关闭所有的端点,然后选择需要的进行开启
management.endpoints.enabled-by-default=false
management.endpoint.info.enabled=trueo
2.2. 暴露端点
web访问时,默认暴露的端点只有2个:health、info
暴露端点的写法如下
management.endpoints.web.exposure.include=health,info
如果想暴露所有的端点,则可以使用*
management.endpoints.web.exposure.include=*
如果是在yml中使用*
,则需要加引号,因为*
在yml中有特殊的含义
management:
endpoints:
web:
exposure:
include: "*"
2.3. 加密 HTTP 端点
建议:如果你的应用是对外公开的,那么强烈建议加密端点
如果配置了Spring Security,那么端点也会自动加密
如果想自定义配置端点的加密,则可以通过RequestMatcher
+Spring Security
来实现
@Configuration(proxyBeanMethods = false)
public class ActuatorSecurity extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests((requests) ->
requests.anyRequest().hasRole("ENDPOINT_ADMIN"));
http.httpBasic();
}
}
2.4. 配置属性
一般Actuator会单独分配一个端口和地址,方便和程序本身区分开
server:
port: 8080
servlet:
context-path: /demo
# 若要访问端点信息,需要配置用户名和密码
spring:
security:
user:
name: xkcoding
password: 123456
management:
# 端点信息接口使用的端口,为了和主系统接口使用的端口进行分离
server:
port: 8090
servlet:
context-path: /sys
# 端点健康情况,默认值"never",设置为"always"可以显示硬盘使用情况和线程情况
endpoint:
health:
show-details: always
# 设置端点暴露的哪些内容,默认["health","info"],设置"*"代表暴露所有可访问的端点
endpoints:
web:
exposure:
include: '*'