Springboot 的一些默认配置规则
说明
本文样例说明仅适用 maven
环境和语法,但所述内容也适用 gradle
原文地址:https://www.cnblogs.com/qnlcy/p/15905544.html
一、日志
1. logback
日志默认为 slf4j
+ logback
框架,引入如下 jar 之后,就自动引入了 logback
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
默认配置文件名称如下,在 classpath
目录下存在对应文件名即可,无需额外通过配置指定
- logback-spring.xml (官方推荐,可以添加 spring boot 的
springProfile
配置项) - logback.xml
2. log4j2
请在大流量应用当中使用 log4j2
而不是使用 log4j
,log4j
在并发写文件上有严重的性能问题,而且,她已经退役了。
首先要排除默认日志框架 logback
,如下为样例代码
请不要照抄到项目当中而带来不必要的
spring-boot-starter-web
及其系列 web 功能的引用
你需要掌握的是如何排除一个 jar 和引入另外一个 jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
默认配置文件名称如下,同样在 classpath
目录下存在对应文件名即可,无需额外通过配置指定
- log4j2-spring.xml(官方推荐,可以添加 spring boot 的
springProfile
配置项) - log4j2.xm
二、项目配置文件
1. 配置文件路径
springboot 默认从如下位置加载配置文件,按 优先级
排序为
- 当前 jar 目录下的
config
子目录 - 当前 jar 目录下的
config/*
目录,注意,此时会合并config
的子目录下的所有application
配置文件 - 当前 jar 目录
classpath
下的config
目录classpath
目录
找寻的 默认配置文件
名称,优先级
为:
- application.properties
- application.yml
- application.yaml
相同配置属性下,优先级高的配置会覆盖优先级低的配置,但是不同的配置属性会合并
比如,在 applicaiton.properties
里有:
name=qnlcy
在 application.yml
里有:
name: cnblog
age: 18
这时候,spring 读取到的 name
和 age
值分别为 qnlcy
和 18
2. 环境配置
指定 spring 加载的环境,可以使用如下配置
yml / yaml
spring:
profiles:
active: dev
properties
spring.profiles.active=dev
命令行参数形式
-Dspring.profiles.active=dev
#或
--spring.profiles.active=dev
然后 spring 会在 上述 第1小节 配置文件目录
下,同样 优先
查找对应的如下文件
- application-${profiles}.properties
- application-${profiles}.yml
- application-${profiles}.yaml
对应上述配置,找寻的配置文件即为:application-dev.properties
或 application-dev.yml
或 application.yaml
三、spring默认依赖介绍和项目划分
项目可以粗略划分为
- web 项目
- 非 web 项目
两种类型的项目,如果使用 spring 管理依赖版本,则 pom.xml 需要加上 <parent>
相关配置:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
</parent>
这里默认使用 spring 管理依赖版本,各个项目也应当继承 spring-boot-starter-parent
, 下面不再写各个依赖的版本
1. 非web项目
先说下非 web 项目,非 web 项目使用 springboot,只需要引入如下 jar 即可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
它显式依赖如下包:
spring-boot
springboot项目核心包spring-boot-autoconfigure
springboot项目的自动配置包,包含datasource、http、session等等等自动配置相关代码spring-boot-starter-logging
springboot项目的日志包,默认实现为logback
spring-core
spring核心包
其中,spring-boot-autoconfigure
又自动依赖 spring-boot
spring-boot
依赖 springframework
的系列包,其依赖及其子依赖分别为
spring-context
spring-beans
spring-core
spring-aop
spring-expresion
2. web项目
web项目引用如下 jar 即可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
它显式依赖了如下 jar
spring-boot-starter
spring-boot-starter-json
这里引入 jacksonspring-boot-starter-tomcat
默认内置 tomcat,切换web容器时候需要排除此包spring-webmvc
spring-web
传统的 dubbo
服务提供者项目、RocketMq
消息消费服务、批处理服务,就不要再引用 spring-boot-starter-web
包了,它默认会引入 web 容器,并开放 web 服务和端口,这是危险和不必要的
四、springboot 启动类和其注解
1. 启动类
springboot 的启动类不能直接放在 src/main/java
目录下,即:
springboot 的启动类的
包路径
不能为空
如果这样做的话,会出现意想不到的错误,感兴趣的同学可以试一下。
通常的,我们会把启动类放到诸如 com.xxx.xxx
之类顶级路径下,如:com.xxx.xxx.App.class
然后再在顶级路径下新建如 config
、business
、utils
、constant
、enums
之类的包,结构如下:
com.xxx.xxx
+ config/
+ business/
+ utils/
+ constant/
+ enums/
App.class
spring 默认会自动扫描启动类的同级目录及其子目录
下的 spring bean 定义,并装载到 spring 容器当中
因此,如果要引用外部包内的 spring bean ,则需要通过如下方式告知包扫描路径给 spring
@SpringBootApplication
@ComponentScan({"com.xxx.yyy","com.xxx.zzz"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
其中,com.xxx.zzz
为风控 jar 包的路径,在注解 @ComponentScan
当中添加该路径,即可使用该 jar 包内的 spring bean 组件
2. 启动类注解
通常,springboot 启动类注解只需要一个 @SpringBootApplication
即可,观其定义,可以发现,它自动包含了 @ComponentScan
、@EnableAutoConfiguration
等注解,已经包含 包扫描
、自动配置
功能
//...
@EnableAutoConfiguration
@ComponentScan(
//...
)
public @interface SpringBootApplication {
//...
}
关于 dubbo
如果使用 dubbo 包里的 @Service (注意不是 spring 的 @Service)
、@Reference
来暴露和引用服务,则需手动开启 dubbo 功能,来使 dubbo 扫描到上述注解开启的 dubbo 服务
@SpringBootApplication
@EnableDubbo
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
如果 dubbo 服务不在当前项目路径下,则额外添加
@DubboComponentScan({"com.xxx.xxx"})
注解来指定 dubbo 扫描路径
通常我们使用 xml
配置文件的形式来归纳 dubbo 的相关服务,此时,只要在 spring 启动时候,把 dubbo bean 相关配置引入到 spring 容器中即可。
常用做法如下,定义个 springboot 配置类来导入 dubbo bean
@Configuration
@ImportResource({"classpath:dubbo/config.xml", "classpath:dubbo/consumer.xml", "classpath:dubbo/provider.xml"})
public class DubboConfig {
}
关于事务
如果引用了 spring-jdbc
并且配置了 DataSource
,则 springboot 默认会开启事务支持,不需要再使用 @EnableTransactionManagement
显式开启事务了
五、MVC中的错误处理
在 web 项目当中,springboot 对异常粗略划分,可以有如下处理
- 使用
@ControllerAdvice
+@ExceptionHandler
来处理已到达controller层级的业务异常 - 自定义 error controller 来处理其他系统级异常
感兴趣的同学可以追踪 DispatcherServlet.doDispatch()
方法来查看具体错误处理过程
1. ControllerAdvice 的用法
在项目当中,使用 @ControllerAdvice
注解指定一个 异常处理器
,它自动交给了 spring 管理,其本质是一个 spring bean
@ControllerAdvice
public class ExceptionResolver {
//因为是 spring bean,所以可以注入自己需要的资源
@ExceptionHandler(value = BusinessException.class) //指定拦截的异常
@ResponseBody //指定返回页面为json串
//方法名可以自定义,但参数不能变,可选参数尚有 HttpServletRequest
public String businessExceptionHandler(HandlerMethod handlerMethod, Exception e) throws Exception {
//处理异常
}
//可以定义多个异常处理方法,spring 会自动寻找对应的异常处理器
//切记最后要有一个处理Exception的兜底
@ExceptionHandler(value = Exception.class)
@ResponseBody
public String defaultErrorHandler(HandlerMethod handlerMethod, Exception e) throws Exception {
}
}
2. 自定义 error controller 来处理其他系统级异常
在出现 controller 层级之外的异常之后,springboot加载的内置容器会转发异常到 springboot 指定的 errorPath
路径上,默认为 /error
,可以通过如下配置修改
server.error.path=/err1
转发过来的请求可以这样处理
@Controller
@RequestMapping("/err1") //上述定义的错误路径
public class CustomErrorController{
@RequestMapping(produces = "text/html")
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
//request中含有 状态码 和 错误信息
return new ModelAndView("500.html");
}
}
六、JSON
springboot 支持三种 JSON 框架,默认为 Jackson
1. Jackson
当项目当中引入了 spring-boot-starter-json
包后, springboot 会自动创建一个 ObjectMapper
bean 来使用。可以通过 spring.jackson
的相关配置来 定义该 bean,祥见 org.springframework.boot.autoconfigure.jackson.JacksonProperties
类
2. Gson
当项目当中引入了 Gson
包后,springboot 会自动创建一个 Gson
bean。通过 spring.gson
的相关配置或 GsonBuilderCustomizer
来定义该 bean
3. JSON-B
JSON-b 是一种 json api 规范,当 javax.json-api
jar包存在项目当中,并且其实现类也存在,Springboot就会创建一个 Jsonb
来使用。这种不常见,不再介绍。
七、测试
1. 单元测试
使用单元测试,需引用 spring-boot-starter-test
,并指定其范围为 test
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<!-- 如果是junit5 则排除如下依赖 -->
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
maven 管理的依赖对其 scope 做了划分,有如下几种
编译 | 测试 | 打包 | 运行 | 说明 | |
---|---|---|---|---|---|
compile | √ | √ | √ | √ | 默认的范围 |
test | √(测试代码) | √ | × | × | 如:junit |
runtime | × | √ | √ | √ | 如:jdbc驱动 |
provided | √ | √ | × | × | 运行时使用别的包代替,如:servlet-api,在tomcat容器中已有此包 |
system | √ | √ | × | × | 跟provided相同,只不过从本地文件系统拉包,而不是从maven仓库(本地和远程)拉取 |
创建 test 类,使用 @SpringBootTest
注解来声明该类为测试类
//@@RunWith(SpringRunner.class) 如果使用 junit4 则需要添加此注解,junit5 则不再需要
@SpringBootTest //不用再指定启动类,springboot 会自动找到启动类并启动 spring
public class PaHttpServiceTest {
//可以正常引用其他 spring 资源
//方法必须为public,返回值必须为 void , junit5 可以不写 public
@Test
public void testHttp() {
//
}
}
2. benchmark 基准测试
此类测试为代码性能测试服务,不适用与业务测试。此类测试会分析每个基准测试方法的单位时间吞吐量、执行耗时等,方便我们提升自己代码的性能。
引入相关依赖
<!-- JMH-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<encoding>UTF-8</encoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<jmh.version>1.21</jmh.version>
</properties>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>provided</scope>
</dependency>
创建测试类,main 方法执行,所有被注解 @Benchmark
标注的方法都会进行性能测试
@BenchmarkMode(Mode.Throughput) //基准测试类型。这里选择的是吞吐量
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) //预热,轮数:5轮,每次预热时间:1,单位:秒(默认)
@Measurement(iterations = 10, time = 5, timeUnit = TimeUnit.SECONDS) //进行10轮测试,每次时间:5,单位,秒
@Threads(8) //一般CPU*2 每个fork所用的线程数量
@Fork(2) //JMH分出2个 “进程”进行测试
@OutputTimeUnit(TimeUnit.NANOSECONDS) //基准测试结果里面的时间类型
public class StringAppend {
public static void main(String[] args) throws RunnerException {
Options options = new OptionsBuilder()
.include(StringAppend.class.getSimpleName())
.build();
new Runner(options).run();
}
@Benchmark
public void testStringAdd() {
String a = "";
for (int i = 0; i < 10; i++) {
a += i;
}
}
}
下面测试结果说明,平均每纳秒,能执行4次
方法名 | 测试类型 | 测试次数 | 得分 | 错误 | 单位 |
---|---|---|---|---|---|
StringAppend.testStringAdd | thrpt | 4 | 0.010 | ± 0.001 | ops/ns |