Bootstrap

Spring Boot入门到精通(超详细)

1.Spring Boot简介

我们知道,从 2002 年开始,Spring 一直在飞速的发展,如今已经成为了在Java EE(Java Enterprise Edition)开发中真正意义上的标准,但是随着技术的发展,Java EE使用 Spring 逐渐变得笨重起来,大量的 XML 文件存在于项目之中。繁琐的配置,整合第三方框架的配置问题,导致了开发和部署效率的降低。

我认为 Spring 的 Web 应用体系结构可以大大简化,如果它提供了从上到下利用 Spring 组件和配置模型的工具和参考体系结构。在简单的 main()方法引导的 Spring 容器内嵌入和统一这些常用Web 容器服务的配置。

这一要求促使了 2013 年初开始的 Spring Boot 项目的研发,到今天,Spring Boot 的版本已经到了 2.0.3 RELEASE。Spring Boot 并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。

它集成了大量常用的第三方库配置,Spring Boot应用中这些第三方库几乎可以是零配置的开箱即用(out-of-the-box),大部分的 Spring Boot 应用都只需要非常少量的配置代码(基于 Java 的配置),开发者能够更加专注于业务逻辑。

2.为什么学习Spring Boot

2.1 从Spring官方来看

我们打开 Spring 的官方网站,可以看到下图:
在这里插入图片描述
我们可以看到图中官方对 Spring Boot 的定位:Build Anything, Build任何东西。Spring Boot旨在尽可能快地启动和运行,并且只需最少的 Spring 前期配置。 同时我们也来看一下官方对后面两个的定位:

SpringCloud:Coordinate Anything,协调任何事情
SpringCloud Data Flow:Connect everything,连接任何东西

仔细品味一下,Spring 官网对 Spring Boot、SpringCloud 和 SpringCloud Data Flow三者定位的措辞非常有味道,同时也可以看出,Spring 官方对这三个技术非常重视,是现在以及今后学习的重点。

2.2 Spring Boot的优点

Spring Boot 有哪些优点?主要给我们解决了哪些问题呢?我们以下图来说明:
在这里插入图片描述

2.2.1 良好的基因

Spring Boot 是伴随着 Spring 4.0 诞生的,从字面理解,Boot是引导的意思,因此 Spring Boot 旨在帮助开发者快速搭建 Spring 框架。Spring Boot 继承了原有 Spring 框架的优秀基因,使 Spring 在使用中更加方便快捷。
在这里插入图片描述

2.2.2 简化编码

举个例子,比如我们要创建一个 web 项目,使用 Spring 的朋友都知道,在使用 Spring 的时候,需要在 pom 文件中添加多个依赖,而 Spring Boot 则会帮助开发着快速启动一个 web 容器,在 Spring Boot 中,我们只需要在 pom 文件中添加如下一个 starter-web 依赖即可。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

我们点击starter-web依赖可以看到,Spring Boot 这个 starter-web 已经包含了多个依赖,包括之前在 Spring 工程中需要导入的依赖,如下图所示:
在这里插入图片描述
由此可以看出,Spring Boot 大大简化了我们的编码,我们不用一个个导入依赖,直接一个依赖即可。

2.2.3 简化配置

Spring 虽然使Java EE轻量级框架,但由于其繁琐的配置,一度被人认为是“配置地狱”。各种XML、Annotation配置会让人眼花缭乱,而且配置多的话,如果出错了也很难找出原因。Spring Boot更多的是采用 Java Config 的方式,对 Spring 进行配置。举个例子:

我新建一个类,但是我不用 @Service注解,也就是说,它是个普通的类,那么我们如何使它也成为一个 Bean 让 Spring 去管理呢?只需要@Configuration 和@Bean两个注解即可,如下:
(1)创建TestService类

public class TestService {
   
    public String sayHello(){
   
        return "Hello Spring Boot!";
    }
}

(2)通过@Configuration和@Bean两个注解,将TestService 类,交给Spring容器进行管理

@Configuration
public class JavaBeanConfig {
   
    @Bean
    public TestService getTestService(){
   
        return new TestService();
    }
}

@Configuration表示该类是个配置类,@Bean表示该方法返回一个 Bean。这样就把TestService作为 Bean 让 Spring 去管理了,在其他地方,我们如果需要使用该 Bean,和原来一样,直接使用@Resource注解注入进来即可使用,非常方便。
(3)测试类

@SpringBootTest
public class BeanTest01 {
   

    @Resource
    private TestService testService;//通过@Resource,获取testService对象

    @Test
    public void test01(){
   
        String s = testService.sayHello();
        System.out.println(s);
        System.out.println("测试通过!!");
    }
}

另外,部署配置方面,原来 Spring 有多个 xml 和 properties配置,在 Spring Boot 中只需要个 application.yml或application.properties即可。

2.2.4 简化部署

在使用 Spring 时,项目部署时需要我们在服务器上部署 tomcat,然后把项目打成 war 包扔到 tomcat里,在使用 Spring Boot 后,我们不需要在服务器上去部署 tomcat,因为 Spring Boot 内嵌了 tomcat,我们只需要将项目打成 jar 包,使用 java -jar xxx.jar一键式启动项目。

另外,也降低对运行环境的基本要求,环境变量中有JDK即可。

2.2.5 简化监控

我们可以引入 spring-boot-start-actuator (美[ˈæktjuˌeɪtər]译:致动器)依赖,直接使用 REST 方式来获取进程的运行期性能参数,从而达到监控的目的,比较方便。但是 Spring Boot 只是个微框架,没有提供相应的服务发现与注册的配套功能,没有外围监控集成方案,没有外围安全管理方案,所以在微服务架构中,还需要 Spring Cloud 来配合一起使用。

2.3 从未来发展的趋势来看

微服务是未来发展的趋势,项目会从传统架构慢慢转向微服务架构,因为微服务可以使不同的团队专注于更小范围的工作职责、使用独立的技术、更安全更频繁地部署。而 继承了 Spring 的优良特性,与 Spring 一脉相承,而且 支持各种REST API 的实现方式。Spring Boot 也是官方大力推荐的技术,可以看出,Spring Boot 是未来发展的一个大趋势。

3. 本课程可以学到什么???

本课程使用目前 Spring Boot 最新版本2.7.0,课程文章均为作者在实际项目中剥离出来的场景和demo,目标是带领学习者快速上手 Spring Boot,将 Spring Boot 相关技术点快速运用在微服务项目中。全篇分为两部分:基础篇和进阶篇。

基础篇(01—10课)主要介绍 Spring Boot 在项目中最常使用的一些功能点,旨在带领学习者快速掌握 Spring Boot 在开发时需要的知识点,能够把 Spring Boot 相关技术运用到实际项目架构中去。该部分以 Spring Boot 框架为主线,内容包括Json数据封装、日志记录、属性配置、MVC支持、在线接口文档、模板引擎、异常处理、AOP 处理、持久层集成等等。

进阶篇(11—17课)主要是介绍 Spring Boot 在项目中拔高一些的技术点,包括集成的一些组件,旨在带领学习者在项目中遇到具体的场景时能够快速集成,完成对应的功能。该部分以 Spring Boot 框架为主线,内容包括拦截器、监听器、缓存、安全认证、分词插件、消息队列等等。

认真读完该系列文章之后,学习者会快速了解并掌握 Spring Boot 在项目中最常用的技术点,作者课程的最后,会基于课程内容搭建一个 Spring Boot 项目的空架构,该架构也是从实际项目中剥离出来,学习者可以运用该架构于实际项目中,具备使用 Spring Boot 进行实际项目开发的能力。

4. 本课程开发环境和插件

  • 本课程的开发环境:
  • 开发工具:IDEA 2019.3.3
  • JDK版本: JDK 1.8
  • Spring Boot版本:2.7.0
  • Maven版本:3.5.4

涉及到的插件:

  • FastJson
  • Swagger2
  • Thymeleaf
  • MyBatis
  • Redis
  • ActiveMQ
  • Shiro
  • Lucence

第01课:Spring Boot开发环境搭建和项目启动

上一节对 SpringBoot 的特性做了一个介绍,本节主要对 jdk 的配置、Spring Boot工程的构建和项目的启动、Spring Boot 项目工程的结构做一下讲解和分析。

1. jdk 的配置

本课程是使用 IDEA 进行开发,在IDEA 中配置 jdk 的方式很简单,打开File->Project Structure,如下图所:
在这里插入图片描述
通过以上三步骤,即可导入本地安装的 jdk。如果是使用 STS 或者 eclipse 的朋友,可以通过两步骤添加:

  • window->preference->java->Instralled JRES来添加本地 jdk。
  • window–>preference–>java–>Compiler选择 jre,和 jdk 保持一致。

2. Spring Boot 工程的构建

2.1 IDEA 快速构建

IDEA 中可以通过File->New->Project来快速构建 Spring Boot 工程。如下,选择 Spring Initializr,在 Project SDK 中选择刚刚我们导入的 jdk,点击 Next,到了项目的配置信息。

  • Group:填企业域名
  • Artifact:填项目名称
  • Dependencies:可以添加我们项目中所需要的依赖信息,根据实际情况来添加,本课程只需要选择 Web 即可。

2.2 官方构建

  • 访问 http://start.spring.io/
  • 在页面上输入相应的 Spring Boot 版本、Group 和 Artifact 信息以及项目依赖,然后创建项目。
    在这里插入图片描述
  • 解压后,使用 IDEA 导入该 maven 工程:File->New->Model from Existing Source,然后选择解压后的项目文件夹即可。如果是使用 eclipse 的朋友,可以通过Import->Existing Maven Projects->Next,然后选择解压后的项目文件夹即可。

2.3 maven配置

创建了 Spring Boot 项目之后,需要进行 maven 配置。打开File->settings,搜索 maven,配置一下本地的 maven 信息。如下:
在这里插入图片描述
在 Maven home directory 中选择本地 Maven 的安装路径;在 User settings file 中选择本地 Maven 的配置文件所在路径。在配置文件中,我们配置一下国内阿里的镜像,这样在下载 maven 依赖时,速度很快(不配置,我们导入依赖,下载会很慢)。

	  <!-- 2.阿里云仓库的配置 -->
<mirror>
	<id>nexus-aliyun</id>
	<mirrorOf>*</mirrorOf>
    <name>Nexus aliyun</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>

如果是使用 eclipse 的朋友,可以通过window–>preference–>Maven–>User Settings来配置,配置方式和上面一致。

2.4 编码配置

同样地,新建项目后,我们一般都需要配置编码,这点非常重要,很多初学者都会忘记这一步,所以要养成良好的习惯。

IDEA 中,仍然是打开File->settings,搜索 encoding,配置一下本地的编码信息。如下:

在这里插入图片描述
如果是使用 eclipse 的朋友,有两个地方需要设置一下编码:

  • window–> perferences–>General–>Workspace,将Text file encoding改成utf-8
  • window–>perferences–>General–>content types,选中Text,将Default encoding填入utf-8

OK,编码设置完成即可启动项目工程了。

3. Spring Boot 项目工程结构

在这里插入图片描述

  • src/main/java路径:主要编写业务程序
  • src/main/resources路径:存放静态文件和配置文件
  • src/test/java路径:主要编写测试程序

默认情况下,如上图所示会创建一个启动类Springboot66Application,该类上面有个@SpringBootApplication注解,该启动类中有个 main 方法,没错,Spring Boot 启动只要运行该 main 方法即可,非常方便。因为,Spring Boot 内部集成了 tomcat(引入starter-web时,就自动引入了Tomcat,如下图),不需要我们人为手动去配置 tomcat,开发者只需要关注具体的业务逻辑即可。
在这里插入图片描述
到此为止,Spring Boot 就启动成功了,为了比较清楚的看到效果,我们写一个 Controller 来测试一下,如下:

@RestController
@RequestMapping("/test")
public class TestController {
   

    @RequestMapping("/springboot")
    public String testSpringBoot(){
   
        return "Welcome to the world of spring boot!";
    }
}

重新运行 main 方法启动项目,在浏览器中输入 localhost:8080/test/springboot,如果看到 “Welcome to the world of Spring Boot!”,那么恭喜你项目启动成功!Spring Boot 就是这么简单方便!端口号默认是8080,如果想要修改,可以在 application.properties 文件中使用 server.port 来人为指定端口,如8081端口:

server.port=8081

4. 总结

本节我们快速学习了如何在 IDEA 中导入 jdk,以及使用 IDEA 如何配置 maven 和编码,如何快速的创建和启动 Spring Boot 工程。IDEA 对 Spring Boot 的支持非常友好,建议大家使用 IDEA 进行 Spring Boot 的开发,从下一课开始,我们真正进入 Spring Boot 的学习中。

第02课:Spring Boot返回Json数据及数据封装

在项目开发中,接口与接口之间,前后端之间数据的传输都使用 Json 格式,在 Spring Boot 中,接口返回 Json 格式的数据很简单,在 Controller 中使用@RestController注解即可返回 Json 格式的数据,@RestController也是 Spring Boot 新增的一个注解,我们点进去看一下该注解都包含了哪些东西。

@Target({
   ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
   
    @AliasFor(
        annotation = Controller.class
    )
    String value() default "";
}

可以看出, @RestController 注解包含了原来的 @Controller 和 @ResponseBody 注解,使用过 Spring 的朋友对 @Controller 注解已经非常了解了,这里不再赘述, @ResponseBody 注解是将返回的数据结构转换为 Json 格式。所以在默认情况下,使用了 @RestController 注解即可将返回的数据结构转换成 Json 格式,Spring Boot 中默认使用的 Json 解析技术框架是 jackson。我们点开 pom.xml 中的 spring-boot-starter-web 依赖,可以看到一个 spring-boot-starter-json 依赖:
在这里插入图片描述
Spring Boot 中对依赖都做了很好的封装,可以看到很多 spring-boot-starter-xxx 系列的依赖,这是 Spring Boot 的特点之一,不需要人为去引入很多相关的依赖了,starter-xxx 系列直接都包含了所必要的依赖,所以我们再次点进去上面这个 spring-boot-starter-json 依赖,可以看到:
在这里插入图片描述

到此为止,我们知道了 Spring Boot 中默认使用的 json 解析框架是 jackson 。下面我们看一下默认的 jackson 框架对常用数据类型的转 Json 处理。

1. Spring Boot 默认对Json的处理

在实际项目中,常用的数据结构无非有类对象、List对象、Map对象,我们看一下默认的 jackson 框架对这三个常用的数据结构转成 json 后的格式如何。

1.1 创建 User 实体类

为了测试,我们需要创建一个实体类,这里我们就用 User 来演示。

public class User {
   
    private Long id;
    private String username;
    private String password;
    /* get、set和带参构造方法此处省略 */
}

1.2 创建Controller类

然后我们创建一个 Controller,分别返回 User对象、List 和 Map。

@RestController
@RequestMapping("/json")
public class JsonController {
   

    @RequestMapping("/user")
    public User getUser() {
   
        return new User(1, "小明", "123456");
    }

    @RequestMapping("/list")
    public List<User> getUserList() {
   
        List<User> userList = new ArrayList<>();
        User user1 = new User(1, "小明", "123456");
        User user2 = new User(2, "小红", "123456");
        userList.add(user1);
        userList.add(user2);
        return userList;
    }

    @RequestMapping("/map")
    public Map<String, Object> getMap() {
   
        Map<String, Object> map = new HashMap<>(3);
        User user = new User(1, "小明", "123456");
        map.put("作者信息", user);
        map.put("博客地址", "http://blog.itcodai.com");
        map.put("CSDN地址", "http://blog.csdn.net/");
        map.put("粉丝数量", 360);
        return map;
    }
}

1.3 测试不同数据类型返回的json

OK,写好了接口,分别返回了一个 User 对象、一个 List 集合和一个 Map 集合,其中 Map 集合中的 value 存的是不同的数据类型。接下来我们依次来测试一下效果。

在浏览器中输入:http://localhost:8081/json/user返回 json 如下:

{
   "id":1,"username":"小明","password":"123456"}

在浏览器中输入:http://localhost:8081/json/list返回 json 如下:

[{
   "id":1,"username":"小明","password":"123456"},{
   "id":2,"username":"小红","password":"123456"}]

在浏览器中输入:http://localhost:8081/json/map 返回 json 如下:

{
   "作者信息":{
   "id":1,"username":"倪升武","password":"123456"},"CSDN地址":"http://blog.csdn.net/","粉丝数量":360,"博客地址":"http://blog.itcodai.com"}

可以看出,map 中不管是什么数据类型,都可以转成相应的 json 格式,这样就非常方便。

1.4 jackson 中对null的处理

在实际项目中,我们难免会遇到一些 null 值出现,我们转 json 时,是不希望有这些 null 出现的,比如我们期望所有的 null 在转 json 时都变成 “” 这种空字符串,那怎么做呢?在 Spring Boot 中,我们做一下配置即可,新建一个 jackson 的配置类(实际应用中,可直接复制该配置类):

@Configuration
public class JacksonConfig {
   
    @Bean
    @Primary
    @ConditionalOnMissingBean(ObjectMapper.class)
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder){
   
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
   
            @Override
            public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
   
                jsonGenerator.writeString("");
            }
        });
        return objectMapper;
    }
}

然后我们修改一下上面返回 map 的接口,将几个值改成 null 测试一下:

@RequestMapping("/map2")
    public Map<String, Object> getMap1() {
   
        Map<String, Object> map = new HashMap<>(3);
        User user = new User(null, "小明", null);
        map.put("作者信息", user);
        map.put("博客地址", "http://blog.itcodai.com");
        map.put("CSDN地址", null);
        map.put("粉丝数量", 666);
        return map;
    }

重启项目,再次输入:localhost:8081/json/map2,可以看到 jackson 已经将所有 null 字段转成了空字符串了。

{
   "作者信息":{
   "id":"","username":"小明","password":""},"CSDN地址":"","粉丝数量":4153,"博客地址":"http://blog.itcodai.com"}

2. 使用阿里巴巴FastJson的设置

2.1 jackson 和 fastJson 的对比

有很多朋友习惯于使用阿里巴巴的 fastJson 来做项目中 json 转换的相关工作,目前我们项目中使用的就是阿里的 fastJson,那么 jackson 和 fastJson 有哪些区别呢?根据网上公开的资料比较得到下表。

选项 fastJson jackson
上手难易程度 容易 中等
高级特性支持 中等 丰富
官方文档、Example支持 中文 英文
处理json速度 略快
关于 fastJson 和 jackson 的对比,网上有很多资料可以查看,主要是根据自己实际项目情况来选择合适的框架。从扩展上来看,fastJson 没有 jackson 灵活,从速度或者上手难度来看,fastJson 可以考虑,我们项目中目前使用的是阿里的 fastJson,挺方便的。

2.2 fastJson依赖导入

使用 fastJson 需要导入依赖,本课程使用 1.2.35 版本,依赖如下:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.78</version>
</dependency>

2.2 使用 fastJson 处理 null

使用 fastJson 时,对 null 的处理和 jackson 有些不同,需要继承 WebMvcConfigurationSupport 类,然后覆盖 configureMessageConverters 方法,在方法中,我们可以选择对要实现 null 转换的场景,配置好即可。如下:

@Configuration
public class fastJsonConfig extends WebMvcConfigurationSupport {
   
    /**
     * 使用阿里 FastJson 作为JSON MessageConverter
     * @param converters
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
   
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
        FastJsonConfig config = new FastJsonConfig();
        config.setSerializerFeatures(
                // 保留map空的字段
                SerializerFeature.WriteMapNullValue,
                // 将String类型的null转成""
                SerializerFeature.WriteNullStringAsEmpty,
                // 将Number类型的null转成0
                SerializerFeature.WriteNullNumberAsZero,
                // 将List类型的null转成[]
                SerializerFeature.WriteNullListAsEmpty,
                // 将Boolean类型的null转成false
                SerializerFeature.WriteNullBooleanAsFalse,
                // 避免循环引用
                SerializerFeature.DisableCircularReferenceDetect);

        converter.setFastJsonConfig(config);
        converter.setDefaultCharset(Charset.forName("UTF-8"));
        List<MediaType> mediaTypeList = new ArrayList<>();
        // 解决中文乱码问题,相当于在Controller上的@RequestMapping中加了个属性produces = "application/json"
        mediaTypeList.add(MediaType.APPLICATION_JSON);
        converter.setSupportedMediaTypes(mediaTypeList);
        converters.add(converter);
    }
}

3. 封装统一返回的数据结构

以上是 Spring Boot 返回 json 的几个代表的例子,但是在实际项目中,除了要封装数据之外,我们往往需要在返回的 json 中添加一些其他信息,比如返回一些状态码 code ,返回一些 msg 给调用者,这样调用者可以根据 code 或者 msg 做一些逻辑判断。所以在实际项目中,我们需要封装一个统一的 json 返回结构存储返回信息。

3.1 定义统一的 json 结构

由于封装的 json 数据的类型不确定,所以在定义统一的 json 结构时,我们需要用到泛型。统一的 json 结构中属性包括数据、状态码、提示信息即可,构造方法可以根据实际业务需求做相应的添加即可,一般来说,应该有默认的返回结构,也应该有用户指定的返回结构。如下:

public class JsonResult<T> {
   
    private T data;
    private String code;
    private String msg;
 
    /**
     * 若没有数据返回,默认状态码为0,提示信息为:操作成功!
     */
    public JsonResult() {
   
        this.code = "0";
        this.msg = "操作成功!";
    }
 
    /**
     * 若没有数据返回,可以人为指定状态码和提示信息
     * @param code
     * @param msg
     */
    public JsonResult(String code, String msg) {
   
        this.code = code;
        this.msg = msg;
    }
 
    /**
     * 有数据返回时,状态码为0,默认提示信息为:操作成功!
     * @param data
     */
    public JsonResult(T data) {
   
        this.data = data;
        this.code = "0";
        this.msg = "操作成功!";
    }
 
    /**
     * 有数据返回,状态码为0,人为指定提示信息
     * @param data
     * @param msg
     */
    public JsonResult(T data, String msg) {
   
        this.data = data;
        this.code = "0";
        this.msg = msg;
    }
    // 省略get和set方法
}

3.2 修改 Controller 中的返回值类型及测试

由于 JsonResult 使用了泛型,所以所有的返回值类型都可以使用该统一结构,在具体的场景将泛型替换成具体的数据类型即可,非常方便,也便于维护。在实际项目中,还可以继续封装,比如状态码和提示信息可以定义一个枚举类型,以后我们只需要维护这个枚举类型中的数据即可(在本课程中就不展开了)。根据以上的 JsonResult,我们改写一下 Controller,如下:

@RestController
@RequestMapping("/jsonResult")
public class JsonResultController {
   

    @RequestMapping("/user")
    public JsonResult<User> getUser() {
   
        User user = new User(1, "小张", "123456");
        return new JsonResult<>(user);
    }

    @RequestMapping("/list")
    public JsonResult<List> getUserList() {
   
        List<User> userList = new ArrayList<>();
        User user1 = new User(1, "小张", "123456");
        User user2 = new User(2, "李四", "123456");
        userList.add(user1);
        userList.add(user2);
        return new JsonResult<>(userList, "获取用户列表成功");
    }

    @RequestMapping("/map")
    public JsonResult<Map> getMap() {
   
        Map<String, Object> map = new HashMap<>(3);
        User user = new User(1, "小张", null);
        map.put("作者信息", user);
        map.put("博客地址", "http://blog.itcodai.com");
        map.put("CSDN地址", null);
        map.put("粉丝数量", 3568);
        return new JsonResult<>(map);
    }
}

我们重新在浏览器中输入:http://localhost:8081/jsonResult/user 返回 json 如下:

{
   "code":"0","data":{
   "id":1,"password":"123456","username":"小张"},"msg":"操作成功!"}

输入:http://localhost:8081/jsonResult/list 返回 json 如下:

{
   "code":"0","data":[{
   "id":1,"password":"123456","username":"小张"},{
   "id":2,"password":"123456","username":"李四"}],"msg":"获取用户列表成功"}

输入:http://localhost:8081/jsonResult/map 返回 json 如下:

{
   "code":"0","data":[{
   "id":1,"password":"123456","username":"小张"},{
   "id":2,"password":"123456","username":"李四"}],"msg":"获取用户列表成功"}

通过封装,我们不但将数据通过 json 传给前端或者其他接口,还带上了状态码和提示信息,这在实际项目场景中应用非常广泛。

4. 总结

本节主要对 Spring Boot 中 json 数据的返回做了详细的分析,从 Spring Boot 默认的 jackson 框架到阿里巴巴的 fastJson 框架,分别对它们的配置做了相应的讲解。另外,结合实际项目情况,总结了实际项目中使用的 json 封装结构体,加入了状态码和提示信息,使得返回的 json 数据信息更加完整。

第03课:Spring Boot使用slf4j进行日志记录

在开发中,我们经常使用 System.out.println() 来打印一些信息,但是这样不好,因为大量的使用 System.out 会增加资源的消耗。我们实际项目中使用的是== slf4j 的 logback 来输出日志,效率挺高的==,Spring Boot 提供了一套日志系统,logback 是最优的选择

springboot的starter-web依赖集成了slf4j和logback,使用logback输出日志的时候,不用单独导入logback依赖

1. slf4j 介绍

引用百度百科里的一段话:

SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统。按照官方的说法,SLF4J是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用其所希望的日志系统。

这段的大概意思是:你只需要按统一的方式写记录日志的代码,而无需关心日志是通过哪个日志系统,以什么风格输出的。因为它们取决于部署项目时绑定的日志系统。例如,在项目中使用了 slf4j 记录日志,并且绑定了 log4j(即导入相应的依赖),则日志会以 log4j 的风格输出;后期需要改为以 logback 的风格输出日志,只需要将 log4j 替换成 logback 即可,不用修改项目中的代码。这对于第三方组件的引入的不同日志系统来说几乎零学习成本,况且它的优点不仅仅这一个而已,还有简洁的占位符的使用和日志级别的判断。

正因为 sfl4j 有如此多的优点,阿里巴巴已经将 slf4j 作为他们的日志框架了。在《阿里巴巴Java开发手册(正式版)》中,日志规约一项第一条就强制要求使用 slf4j:

1.【强制】应用中不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架SLF4J中的API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。

“强制”两个字体现出了 slf4j 的优势,所以建议在实际项目中,使用 slf4j 作为自己的日志框架。使用 slf4j 记录日志非常简单,直接使用 LoggerFactory 创建即可。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Test {
   
    private static final Logger logger = LoggerFactory.getLogger(Test.class);

    //、、、、、、、、、、、
}

2. application.yml 中对日志的配置

Spring Boot 对 slf4j 支持的很好,内部已经集成了 slf4j,一般我们在使用的时候,会对slf4j 做一下配置。application.yml 文件是 Spring Boot 中唯一一个需要配置的文件,一开始创建工程的时候是 application.properties 文件,个人比较细化用 yml 文件,因为 yml 文件的层次感特别好,看起来更直观,但是 yml 文件对格式要求比较高,比如英文冒号后面必须要有个空格,否则项目估计无法启动,而且也不报错。用 properties 还是 yml 视个人习惯而定,都可以。本课程使用 yml。

我们看一下 application.yml 文件中对日志的配置:

logging:
  config:
    classpath: logback.xml  //记得加classpath
  level:
    com.zhengzhou.springboot_6_6.entity: trace

server:
  port: 8081

logging.config 是用来指定项目启动的时候,读取哪个配置文件,这里指定的是日志配置文件是根路径下的 logback.xml 文件(即resource文件夹下边),关于日志的相关配置信息,都放在 logback.xml 文件中了。logging.level 是用来指定具体的 mapper 中日志的输出级别,上面的配com.zhengzhou.springboot_6_6.dao包下的所有 mapper 日志输出级别为 trace,会将操作数据库的 sql 打印出来,开发时设置成 trace 方便定位问题,在生产环境上,将这个日志级别再设置成 error 级别即可(本节课不讨论 mapper 层,在后面 Spring Boot 集成 MyBatis 时再详细讨论)。

常用的日志级别按照从高到低依次为:ERROR、WARN、INFO、DEBUG。

3. logback.xml 配置文件解析

在上面 application.yml 文件中,我们指定了日志配置文件 logback.xml,logback.xml 文件中主要用来做日志的相关配置。在 logback.xml 中,我们可以定义日志输出的格式、路径、控制台输出格式、文件大小、保存时长等等。下面来分析一下:

3.1 定义日志输出格式和存储路径

<configuration>
    <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
    <property name="FILE_PATH" value="D:/logs/springboot/demo.%d{yyyy-MM-dd}.%i.log" />
</configuration>

我们来看一下这个定义的含义:首先定义一个格式,命名为 “LOG_PATTERN”,该格式中 %date 表示日期,%thread 表示线程名,%-5level 表示级别从左显示5个字符宽度,%logger{36} 表示 logger 名字最长36个字符,%msg 表示日志消息,%n 是换行符。

然后再定义一下名为 “FILE_PATH” 文件路径,日志都会存储在该路径下。%i 表示第 i 个文件,当日志文件达到指定大小时,会将日志生成到新的文件里,这里的 i 就是文件索引,日志文件允许的大小可以设置,下面会讲解。这里需要注意的是,不管是 windows 系统还是 Linux 系统,日志存储的路径必须要是绝对路径。

3.2 定义控制台输出

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <!-- 按照上面配置的LOG_PATTERN来打印日志 -->
        <pattern>${LOG_PATTERN}</pattern>
    </encoder>
</appender>

使用 节点设置个控制台输出(class=“ch.qos.logback.core.ConsoleAppender”)的配置,定义为 “CONSOLE”。使用上面定义好的输出格式(LOG_PATTERN)来输出,使用 ${} 引用进来即可。

3.3 定义日志文件的相关参数

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!-- 按照上面配置的FILE_PATH路径来保存日志 -->
        <fileNamePattern>${FILE_PATH}</fileNamePattern>
        <!-- 日志保存15天 -->
        <maxHistory>15</maxHistory>
        <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <!-- 单个日志文件的最大,超过则新建日志文件存储 -->
            <maxFileSize>10MB</maxFileSize>
        </timeBasedFileNamingAndTriggeringPolicy>
    </rollingPolicy>
    <encoder>
        <!-- 按照上面配置的LOG_PATTERN来打印日志 -->
        <pattern>${LOG_PATTERN}</pattern>
    </encoder>
</appender>

使用 定义一个名为 “FILE” 的文件配置,主要是配置日志文件保存的时间、单个日志文件存储的大小、以及文件保存的路径和日志的输出格式。

3.4 定义日志输出级别


<configuration>
	<logger name="com.zhengzhou.springboot_6_6" level="INFO" />
	<root level="INFO">
		<appender-ref ref="CONSOLE" />
		<appender-ref ref="FILE" />
	</root>
</configuration>

有了上面那些定义后,最后我们使用 来定义一下项目中默认的日志输出级别,这里定义级别为 INFO,然后针对 INFO 级别的日志,使用 引用上面定义好的控制台日志输出和日志文件的参数。这样 logback.xml 文件中的配置就设置完了。

4. 使用Logger在项目中打印日志

在代码中,我们一般使用 Logger 对象来打印出一些 log 信息,可以指定打印出的日志级别,也支持占位符,很方便。

@RestController
@RequestMapping("/test")
public class TestController {
   

    private final static Logger logger = LoggerFactory.getLogger(TestController.class);
    
    @RequestMapping("/log")
    public String testLog() {
   
        logger.debug("===========测试日志debug级别打印===========");
        logger.info("============测试日志info级别打印============");
        logger.error("===========测试日志error级别打印===========");
        logger.warn("============测试日志warn级别打印============");

        // 可以使用占位符打印出一些参数信息
        String str1 = "blog.my.com";
        String str2 = "blog.csdn.net/person01";
        logger.info("======小明的个人博客:{};小明的CSDN博客:{}", str1, str2);

        return "success";
    }
}

启动该项目,在浏览器中输入http://localhost:8081/test/log 后可以看到控制台的日志记录:

11:05:45.259 [http-nio-8081-exec-1] INFO  o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
11:05:45.259 [http-nio-8081-exec-1] INFO  o.s.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
11:05:45.259 [http-nio-8081-exec-1] INFO  o.s.web.servlet.DispatcherServlet - Completed initialization in 0 ms
11:05:45.279 [http-nio-8081-exec-1] INFO  c.z.s.controller.TestController - ============测试日志info级别打印============
11:05:45.279 [http-nio-8081-exec-1] ERROR c.z.s.controller.TestController - ===========测试日志error级别打印===========
11:05:45.279 [http-nio-8081-exec-1] WARN  c.z.s.controller.TestController - ============测试日志warn级别打印============
11:05:45.279 [http-nio-8081-exec-1] INFO  c.z.s.controller.TestController - ======小明的个人博客:blog.my.com;小明的CSDN博客:blog.csdn.net/person01

因为 INFO 级别比 DEBUG 级别高,所以 debug 这条没有打印出来,如果将 logback.xml 中的日志级别设置成 DEBUG,那么四条语句都会打印出来,这个大家自己去测试了。同时可以打开 D:\logs\springboot\ 目录,里面有刚刚项目启动,以后后面生成的所有日志记录。在项目部署后,我们大部分都是通过查看日志文件来定位问题

5. 总结

本节课主要对 slf4j 做了一个简单的介绍,并且对 Spring Boot 中如何使用 slf4j 输出日志做了详细的说明,着重分析了 logback.xml 文件中对日志相关信息的配置,包括日志的不同级别。最后针对这些配置,在代码中使用 Logger 打印出一些进行测试。在实际项目中,这些日志都是排查问题的过程中非常重要的资料。

附加:logback.xml(完整)

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
    <property name="FILE_PATH" value="D:/logs/springboot/demo01.%d{yyyy-MM-dd}.%i.log" />

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- 按照上面配置的LOG_PATTERN来打印日志 -->
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
    </appender>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 按照上面配置的FILE_PATH路径来保存日志 -->
            <fileNamePattern>${FILE_PATH}</fileNamePattern>
            <!-- 日志保存15天 -->
            <maxHistory>15</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- 单个日志文件的最大,超过则新建日志文件存储 -->
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <!-- 按照上面配置的LOG_PATTERN来打印日志 -->
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
    </appender>
    <logger name="com.zhengzhou.springboot_6_6" level="INFO" />
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    </root>
</configuration>

;