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 而不是使用 log4jlog4j 在并发写文件上有严重的性能问题,而且,她已经退役了。

首先要排除默认日志框架 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 默认从如下位置加载配置文件,按 优先级 排序为

  1. 当前 jar 目录下的 config 子目录
  2. 当前 jar 目录下的 config/* 目录,注意,此时会合并 config 的子目录下的所有 application 配置文件
  3. 当前 jar 目录
  4. classpath 下的 config 目录
  5. classpath 目录

找寻的 默认配置文件 名称,优先级 为:

  1. application.properties
  2. application.yml
  3. application.yaml

相同配置属性下,优先级高的配置会覆盖优先级低的配置,但是不同的配置属性会合并

比如,在 applicaiton.properties 里有:

name=qnlcy

application.yml 里有:

name: cnblog
age: 18

这时候,spring 读取到的 nameage 值分别为 qnlcy18

2. 环境配置

指定 spring 加载的环境,可以使用如下配置

yml / yaml

spring:
  profiles:
    active: dev

properties

spring.profiles.active=dev

命令行参数形式

-Dspring.profiles.active=dev
#或
--spring.profiles.active=dev

然后 spring 会在 上述 第1小节 配置文件目录 下,同样 优先 查找对应的如下文件

  1. application-${profiles}.properties
  2. application-${profiles}.yml
  3. application-${profiles}.yaml

对应上述配置,找寻的配置文件即为:application-dev.propertiesapplication-dev.ymlapplication.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 这里引入 jackson
  • spring-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

然后再在顶级路径下新建如 configbusinessutilsconstantenums 之类的包,结构如下:

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
posted @ 2022-02-17 17:15  去哪里吃鱼  阅读(958)  评论(0编辑  收藏  举报