java前后端分离后怎么部署,SpringBoot,写完老板又让我转回后端

 2023-09-25 阅读 31 评论 0

摘要:SpringBoot1:SpringBoot基础入门1-1:什么是SpringBoot1-2:什么是微服务1-3:官网搭建SpringBoot项目1-4:使用Idea快速创建SpringBoot项目1-5:自定义banner1-6:单元测试@SpringBootTest2:SpringBoot的配置2-1

SpringBoot

  • 1:SpringBoot基础入门
    • 1-1:什么是SpringBoot
    • 1-2:什么是微服务
    • 1-3:官网搭建SpringBoot项目
    • 1-4:使用Idea快速创建SpringBoot项目
    • 1-5:自定义banner
    • 1-6:单元测试@SpringBootTest
  • 2:SpringBoot的配置
    • 2-1: 配置文件格式
    • 2-2:YAML语法
      • 2-2-1:基本语法
      • 2-2-2:值的写法
    • 2-3:从配置文件中获取值
      • 2-3-1:@ConfigurationProperties(prefix="配置")
      • 2-3-2:@ConfigurationProperties(prefix="配置")和@value的区别
      • 2-3-3:@PropertySource()加载.properties文件
      • 2-3-4:@PropertySource()加载.yml或者yaml文件
      • 2-3-5:@ImportResource
      • 2-3-6:@Configuration
      • 2-3-7:配置文件占位符
    • 2-4:多配置环境Profile
      • 2-4-1:多文档快方式
      • 2-4-2:多配置文件方式
      • 2-4-3:激活指定的Profile环境
    • 2-5:配置文件的加载位置与顺序
  • 3:SpingBoot的原理
  • 4:SpingBoot的日志
  • 5:SpingBoot的Web开发
    • 5-1:静态资源的访问
    • 5-2:模板引擎thymeleaf
      • 5-2-1:SpringBoot使用thymeleaf
      • 5-2-2:thymeleaf的语法
    • 5-3:SpringMVC自动配置原理
    • 5-4:扩展或全面接管SpringMVC
      • 5-4-1:扩展SpringMVC
      • 5-4-2:扩展SpringMVC的原理
      • 5-4-3:全面接管SpringMVC
    • 5-5:国际化
    • 5-6:登录拦截
    • 5-7:定制错误页面
      • 5-7-1:如何定制异常的页面
      • 5-7-2:如何定异常的json数据
      • 5-7-3:自动处理是返回页面还是返回json数据
    • 5-8:注册三大组件Servlet、Filter、Listener
  • 6:SpingBoot整合数据访问
    • 6-1:jdbc
    • 6-2:使用Druid数据源
    • 6-3:整合mybatis
      • 6-3-1:注解版
      • 6-3-1:配置版
    • 6-4:整合JPA
  • 7:SpringBoot任务
    • 7-1:异步任务
    • 7-2:邮件任务
    • 7-3:定时任务

1:SpringBoot基础入门

1-1:什么是SpringBoot

什么是Spring

       Spring是一个开源框架,2003年兴起的一个轻量级的Java开发框架,作者:Rod Johnson。


       Spring是为了解决企业级应用开发的复杂性而创建的,简化开发

Spring是如何简化Java开发的

    为了降低Java开发的复杂性,Spring采用了以下4种关键策略:

  • 1、基于Bean的轻量级和最小侵入性编程;
  • 2、通过IOC,依赖注入DI和面向接口实现松耦合;
  • 3、基于切面AOP和惯例进行声明式编程;
  • 4、通过切面和模板减少样式代码;

什么是SpringBoot

       我们从最原始的Servlet结合Tomcat,接着到Struts,后来到SpringMVC,框架从无到有的衍生,这一切都是为何简化开发,提高开发效率,但是慢慢的我们发现,Spring也不满足我们的需求了,因为配置太多,简称配置地狱。而SpringBoot呢,就是一个JavaWeb大的开发框架,和SpringMVC类似,对比其他JavaWeb框架的好处,官方说是简化开发,约定大于配置,you can ‘‘just run’’,能迅速的开发web应用,几行代码开发一个http接口。


       我们慢慢的发现了一个规律,所有的技术框架的发展似乎都遵循了一条主线:从一个复杂应用场景衍生一种规范框架,人们只需要进行各种配置而不需要自己实现它,这时候强大的配置功能成了优点;发展到一定程度之后,人们根据实际生产应用情况,选取其中实际功能和设计精华,重构出一些轻量级的框架;之后为了提高开发效率,嫌弃原先的各种配置过于麻烦,于是开始提倡“约定大于配置”,进而衍生出一些一站式的解决方案。


       这就是Java企业级应用->J2EE->Spring->SpringBoot的过程


       随着Spring不断的发展,涉及的领域越来越多,项目整合开发需要配合各种各样的文件,慢慢变得不那么易用简单,违背了最初的理念,甚至人称配置地域。SpringBoot正是在这样的一个背景下被抽象出来的开发框架,目的为了让大家更容易的使用Spring,更容易的集成各种常用的中间件,开源软件;


       SpringBoot基于Spring开发,SpringBoot本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷的开发新一代基于Spring框架的应用程序。也就是说,它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具。SpringBoot以约定大于配置的核心思想,默认帮我们进行了很多配置,多试SpringBoot应用只需要很少的Spring配置。同时它集成了大量常用的第三方库配置(例如Redis、MongoDB、Jpa、RabbitMQ、Quartz等等),SpringBoot应用中这些第三方库几乎可以零配置的开箱即用。


       简单来说就是SpringBoot其实不是什么 新框架,它默认配置了很多框架的使用方式,就像Maven整合了所有的jar包,SpringBoot整合了所有的框架


       SpringBoot出生名门,从一来是就站在一个比较高的起点,又经过这几年的发展,生态最有完善,SpringBoot已经当之无愧成为Java领域最热门的技术。

SpringBoot的主要特点

  • 快速创建独立运行的Spring项目以及与主流程框架集成
  • 开箱即用,大量的自动配置,提供各种默认配置来简化项目配置,也可以修改默认值
  • 内嵌式容器简化Web项目,使用嵌入式的Servlet容器,应用无需打成war包
  • 没有冗余代码生成和XML配置的要求
  • starters自动依赖与版本控制
  • 准生产环境的运行时应用监控
  • 与运计算的天然集成


    使用SpringBoot到底有多爽,用下面这幅图来表达
    在这里插入图片描述

1-2:什么是微服务

什么是微服务

       微服务是一种架构风格,它要求我们在开发一个应用的时候,这个应用必须构建成一系列小服务的组合;可以通过http的方式进行互通。要说微服务架构,先得说说我们过去的单体应用架构。

单体应用架构

       所谓单体应用架构(all in one)是指,我们将一个应用的中所有应用服务都封装在一个应用中。


       无论是ERP、CRM或是其他什么系统,你都把数据库访问,web访问等等各个功能都放大一个war包内。

    这样做的好处:

  • 易于开发和测试
  • 十分方便部署
  • 需要扩展时,只需要将war复制多份,然后放到多个服务器上,再做个负载均衡就可以了。

    这样做的缺点是:

  • 牵一发而动全身,哪怕修改一个非常小的地方,都要停掉整个服务,重新打包、部署这个应用的war包。
  • 对于一个大型应用,如果把所有的内容都放在一个应用里面,我们如何维护,如何分工合作都是问题。

微服务架构

       所谓微服务架构,就是打破之前all in one的传统架构方式,把每个功能元素单独出来。把独立出来的功能元素进行动态组合,需要的功能元素才拿来组合,需要多一些时可以整合多个功能元素。所以微服务架构是对功能元素进行复制,而没有对整个应用进行复制。

    这样做的好处是:

  • 节省了调用资源
  • 每个 功能元素的服务都是一个可替换换的,可堵路升级的软件代码

    下面的连接详细的阐述了什么是微服务

  • 原文地址:https://martinfowler.com/articles/microservices.html
  • 翻译版本:https://www.cnblogs.com/liuning8023/p/4493156.html

原文里也提供了几种语言的版本
在这里插入图片描述

如何构建微服务

       一个大型系统的微服务架构,就像是一个复杂交织的神经网络,每一个神经元就是一个功能元素,它们各自完成自己的功能,然后通过http相互请求调用。比如一个电商系统,查缓存,连数据库,浏览页面,结账,支付等服务都是一个个独立的功能服务,都被微化了,他们只为一个个微服务共同构建了一个庞大的系统。如果修改其中的一个功能,只需要更新升级其中一个功能服务单元即可。


       但是这种庞大的系统架构给部署和运维带来很大的难度。于是,Spring为我们带来了构建大型分布式微服务的全套、全程产品:

  • 构建一个个功能独立的微服务应用单元,可以使用SpringBoot,可以帮我们快速构建一个应用;
  • 大型分布式网络服务的调用。这部分有SpringCloud来完成,实现分布式;
  • 在分布式中间,进行流式数据计算、批处理,我们有springCloud Data Flow;
  • Spring为我们想清楚了整个从开始构建应用到大型分布式应用全流程方案。

1-3:官网搭建SpringBoot项目

       首先进入官网https://spring.io/projects/spring-boot

       拉到最下面
在这里插入图片描述
       点击进入或者直接进入https://start.spring.io/
在这里插入图片描述
       填写项目信息
在这里插入图片描述
在这里插入图片描述
       我们要创建web项目,选择Spring Web
在这里插入图片描述
       信息填完后创建项目
在这里插入图片描述
       然后会把项目下载下来
在这里插入图片描述
       把包拷贝到你想要的目录下,然后解压
在这里插入图片描述
       用idea打开我们的项目
在这里插入图片描述
       打开项目后先让maven下载依赖包,导入完可以删掉我们不需要的文件
在这里插入图片描述
       删掉文件后的项目
在这里插入图片描述
       然后启动项目
在这里插入图片描述
       访问http://localhost:8080/
在这里插入图片描述
       到这里整个基础项目就完成,接下来就是自己的业务问题,我们这里写个简单的接口
在这里插入图片描述
       重启访问接口
在这里插入图片描述

1-4:使用Idea快速创建SpringBoot项目

       创建项目
在这里插入图片描述
在这里插入图片描述
       填写基本信息
在这里插入图片描述
       提示Artifact含有非法字符集
在这里插入图片描述
       是因为我们的Artifact含有大写字母,改为小写就好
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
       SpringBoot项目创建成功,删掉我们不想要的文件
在这里插入图片描述
在这里插入图片描述
       因为我们创建的时候,没有导入Web依赖,所以这个项目不是Web项目,如果想变为Web项目,可以手动Maven导入依赖

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

在这里插入图片描述
       启动项目
在这里插入图片描述
       访问http://localhost:8080/
在这里插入图片描述
       修改端口号
在这里插入图片描述
在这里插入图片描述
       重启
在这里插入图片描述

       resources文件夹中目录结构

  • static:保存所有的静态资源:(js、css、images)
  • templates:保存所有的模板页面:(SpringBoot默认jar包使用嵌入式的Tomcat,默认不支持JSP页面),可以使用模板引擎(freemarker、thymeleaf)
  • application.properties:SpringBoot应用的配置文件,可以修改一些默认设置

1-5:自定义banner

       所谓banner,就是启动项目的时候的这个东西
在这里插入图片描述

       在项目的resources目录下,新建一个文件叫banner.txt
在这里插入图片描述
       在这个新建的banner.txt文件下定义你们想要输出的内容即可

       更多的banner图,可以来这里找https://www.bootschool.net/ascii-art

       我这里搞了一辆车的

                      _____________________________________________________|                                                     |_______  |                                                     |/ _____ | |                货拉拉---程序员的好归宿                |/ /(__) || |                                                     |________/ / |OO| || |                                                     |
|         |-------|| |                                                     |
(|         |     -.|| |_______________________                             |
|  ____   \       ||_________||____________  |             ____      ____  |
/| / __ \   |______||     / __ \   / __ \   | |            / __ \    / __ \ |\
\|| /  \ |_______________| /  \ |_| /  \ |__| |___________| /  \ |__| /  \|_|/| () |                 | () |   | () |                  | () |    | () |\__/                   \__/     \__/                    \__/      \__/

       启动项目
在这里插入图片描述

1-6:单元测试@SpringBootTest

       使用@SpringBootTest要导入依赖

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

       进入spring-boot-starter-test的依赖看,里面集成了SpringBootTest的依赖,sprin的核心springframework依赖以及junit和各种依赖
在这里插入图片描述
       总体来说比junit强大很多

       然后在单元测试的类上方加上@SpringBootTest,在单元测试的方法上方加上@Test,加了@SpringBootTest,那就可以使用Spring的注入了

package com.lingaolu.springbootdemo;import com.lingaolu.springbootdemo.bean.Student;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
class SpringbootdemoApplicationTests {@AutowiredStudent student;@Testvoid contextLoads() {System.out.println(student);}}

       我这里注入了类Student,Student类注入了name属性

package com.lingaolu.springbootdemo.bean;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;@Component
public class Student {@Value("林某")private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +'}';}
}

       单元测试执行,结果出来
在这里插入图片描述

2:SpringBoot的配置

       我们在第一章SpringBoot基础入门的第四节使用Idea快速创建SpringBoot项目的时候已经初步接触了SpringBoot的配置,那就是修改端口号,下面我们来介绍SpringBoot的配置

2-1: 配置文件格式

       SpringBoot的配置文件,后缀不仅支持.properties,还只会yml和yaml格式,从下图我们可以看出
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2-2:YAML语法

百度百科:


       YAML(/ˈjæməl/,尾音类似camel骆驼)是一个可读性高,用来表达数据序列化的格式。YAML参考了其他多种语言,包括:C语言、Python、Perl,并从XML、电子邮件的数据格式(RFC 2822)中获得灵感。Clark Evans在2001年首次发表了这种语言,另外Ingy döt Net与Oren Ben-Kiki也是这语言的共同设计者。当前已经有数种编程语言或脚本语言支持(或者说解析)这种语言。


       YAML是"YAML Ain’t a Markup Language"(YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言),但为了强调这种语言以数据做为中心,而不是以标记语言为重点,而用反向缩略语重命名。

YAML以数据为中心,比json、xml等更适合做位置文件

YAML配置例子

server:port: 666

XML配置例子

<server><port>666</port>
</server>

       可见yml太简洁方便,我们验证一下yaml修改端口号
在这里插入图片描述
说明我们使用yaml修改端口号成功

2-2-1:基本语法

  • k:(空格)v:表示一对键值对,k和v之间的空格必须有,并且冒号:是英文的冒号:
  • 空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的
  • 属性和值也是大小写敏感

2-2-2:值的写法

字面量:普通的值(数字,字符串,布尔)


     k: v(字面直接来写)

  • 字符串默认不用加上单引号或者双引号
  • “”:双引号,不会转义字符串里面的特殊字符,特殊字符会作为本身想表达的意思
    如:name: “lin\nmou”,输出:lin换行mou
  • ‘’:单引号,会转义特殊字符,特殊字符最终只是一个普通的字符串数据
    如:name: ‘lin\nmo’,输出:lin\nmou

对象、map(属性和值)(键值对)

girlFriend:name: 未知age: 29

  行内写法

girlFriend: {name: 未知,age: 29}

数组(List、Set)


  用-值表示数组中的第一个元素

games:- LOL- CS- WOW

  行内写法

games: [LOL,CS,WOW]

2-3:从配置文件中获取值

       SpringBoot默认的配置文件全名为application.properties,默认被识别,也就是application开头.(properties/yaml/yml)的配置文件属于SpringBoot的全局配置文件,可以 被默认加载

2-3-1:@ConfigurationProperties(prefix=“配置”)

       @ConfigurationProperties可以从配置文件中获取整个对象的配置值,将配置文件中配置的每一个属性的值,通过反射的set方法,映射到这个组件中,我们还是直接举例子

       我们有2个实体bean对象

  • Girl:含有name和age属性,书写set和get方法
  • Programmer:含有name和age以及girl属性,使用lombok

       Girl的代码

package com.lingaolu.springbootdemo.bean;import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;// 只有这个组件是容器的组件,才能使用容器提供的>@ConfigurationProperties功能
@Component
// 将配置文件中配置的每一个属性的值,映射到这个组件中
// @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配合文件中相关的配置进行绑定
// prefix="girl",配置文件中哪个页面的所有属性进行--映射
@ConfigurationProperties(prefix="girl")
public class Girl {private String name;private Integer age;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "Girl{" +"name='" + name + '\'' +", age=" + age +'}';}
}

       Programmer的代码

package com.lingaolu.springbootdemo.bean;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Component
// 使用lombok
@Data
@ConfigurationProperties(prefix="pro")
public class Programmer {private String name;private Integer age;private Girl girl;
}

       Girl的配置,采用properties文件

girl.name=go
girl.age=25

       Programmer的配置,采用yml文件

pro:name: 林某age: 25girl:name: 狗不理age: 23

       单元测试

package com.lingaolu.springbootdemo;import com.lingaolu.springbootdemo.bean.Girl;
import com.lingaolu.springbootdemo.bean.Programmer;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
class SpringbootdemoApplicationTests {@AutowiredGirl girl;@AutowiredProgrammer programmer;@Testvoid contextLoads() {System.out.println(girl);System.out.println("------------------");System.out.println(programmer);}}

       运行结果
在这里插入图片描述

       从结果可以看出,无论是properties文件还是yml文件,都能成功的把配置文件里的属性值映射到我们的实体

       properties文件中文乱码问题,从上面的例子看出,我们的yml配置文件使用中文,可能成功的映射,那么我们的properties文件也使用中文,结果乱码了
在这里插入图片描述

       解决properties文件中文乱码问题,因为我们idea里的properties文件默认的是utf-8编码,而运行时的properties文件是ascii编码,所以我们需设置一下
在这里插入图片描述

       Apply,OK,接着修改属性的值,我们修改为一场梦幻的爱情,再测试一下,中文乱码解决了
在这里插入图片描述

       发现没有,无论是Girl类还是Programmer类,上方都会有一个红色的提示,这个不影响我们的代码,就是看起来不舒服
在这里插入图片描述

       解决上面的问题,我们可以导入配置文件处理器

<!-- 导入配置文件处理器,配置文件进行绑定就会有提示  -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional>
</dependency>

       刷新maven,重启一下,那么无论是哪种配置文件,我们配置的时候也会有提示了
在这里插入图片描述
在这里插入图片描述

2-3-2:@ConfigurationProperties(prefix=“配置”)和@value的区别

       我们知道spring原生的注解@value也可以给属性注入值,如我们下列的例子

       Programmer类的name属性使用@value,其他的使用@ConfigurationProperties

package com.lingaolu.springbootdemo.bean;import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Component
@Data
@ConfigurationProperties(prefix="programmer")
public class Programmer {@Value("${name}")private String name;private Integer age;private String no;
}

       配置文件

programmer:age: 25no: 110
name: 码农

       结果,name属性也注入值了
在这里插入图片描述

       虽然@value也可以注入,但是和@ConfigurationProperties还是有区别的,如下表

比较项@ConfigurationProperties@value
决定性非SpEL注入,以@ConfigurationProperties为最终结果以SpEL注入,以@value为最终结果
功能批量注入配置文件中的属性一个一个指定
SpEL不支持支持
松散绑定支持不支持
JSR303数据校验支持不支持
复杂类型封装支持不支持

下面我们举例说明

决定性
在这里插入图片描述

SpEL
在这里插入图片描述

松散绑定
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

JSR303数据校验

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

       要使用数据校验,得maven引入上面的包,然后使用注解@Validated作用在要校验的类上,接着在对应属性的相关类型校验进行注解,比如我们这里进行邮箱校验
在这里插入图片描述
在这里插入图片描述

复杂类型封装
在这里插入图片描述
在这里插入图片描述

2-3-3:@PropertySource()加载.properties文件

       我们先看一个例子
在这里插入图片描述

       我们前面说过,SpringBoot默认的配置文件名前缀是application,并且这种前缀的配置文件属于全局配置文件,可以被SpringBoot全局找到,被默认加载,但是我们的配置文件是pro.properties,命名的格式不符合全局配置文件的方式,所以找不到,要想找到,就使用@PropertySource(),加载指定的配置文件

package com.lingaolu.springbootdemo.bean;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;@Data
@Component
@PropertySource(value = {"classpath:pro.properties"})
@ConfigurationProperties(prefix="pro")
public class Programmer {private String name;private Integer age;}

       加载了指定的配置文件后,就可以被找到了
在这里插入图片描述

2-3-4:@PropertySource()加载.yml或者yaml文件

       上一节我们说到了@PropertySource()加载.properties文件,这节我们将说@PropertySource()加载.yml或者yaml文件,那么你可能回想,为何要分开说,既然分开说,那就明他们是有区别的,我们先用.properties文件成功的例子,演示一下.yml文件

       发现即使使用了@PropertySource()加载指定的文件,还是没有成功
在这里插入图片描述

       这是为什么呢?这是因为@PropertySource只对properties文件可以进行加载,但对于yml或者yaml不能支持。

       想要解决这个问题,我们需要写一个工厂类,继承DefaultPropertySourceFactory,用来处理yml或者yaml文件,因为@PropertySource有一个工厂属性factory。默认就是DefaultPropertySourceFactory
在这里插入图片描述

       所以我们写了一个工厂类
在这里插入图片描述

package com.lingaolu.springbootdemo.factory;import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.core.io.support.EncodedResource;
import java.io.IOException;
import java.util.List;
import java.util.Properties;// 继承DefaultPropertySourceFactory
public class YamlAndPropertySourceFactory extends DefaultPropertySourceFactory {@Overridepublic PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {if (resource == null) {return super.createPropertySource(name, resource);}Resource resourceResource = resource.getResource();if (!resourceResource.exists()) {return new PropertiesPropertySource(null, new Properties());} else if (resourceResource.getFilename().endsWith(".yml") || resourceResource.getFilename().endsWith(".yaml")) {List<PropertySource<?>> sources = new YamlPropertySourceLoader().load(resourceResource.getFilename(), >resourceResource);return sources.get(0);}return super.createPropertySource(name, resource);}
}

       然后我们的@PropertySource,除了要加载指定的配置文件外,还要指定我们所写的工厂类
在这里插入图片描述

package com.lingaolu.springbootdemo.bean;import com.lingaolu.springbootdemo.factory.YamlAndPropertySourceFactory;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;@Data
@Component
@PropertySource(value = {"classpath:pro.yml"}, factory = YamlAndPropertySourceFactory.class)
@ConfigurationProperties(prefix="pro")
public class Programmer {private String name;private Integer age;}

       结果能成功注入
在这里插入图片描述

2-3-5:@ImportResource

       @ImportResource的作用就是导入Spring的配置文件,让配置文件里面的内容生效

       我们先看一个例子,SpringBoot里面没有Spring的配置文件,我们自己编写的配置文件,也不能自动识别

我们自己写了一个spring的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring->>beans.xsd"><bean id="programmer1" class="com.lingaolu.springbootdemo.bean.Programmer"><property name="name" value="我们缺少的是勇气"/></bean></beans>

单元测试

package com.lingaolu.springbootdemo;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;@SpringBootTest
class SpringbootdemoApplicationTests {@AutowiredApplicationContext ioc;@Testvoid contextLoads() {boolean b1 = ioc.containsBean("programmer");System.out.println(b1);boolean b2 = ioc.containsBean("programmer1");System.out.println(b2);}}

结果发现IOC容器竟然没有我们注入的bean,因为我们自己写的spring.xml文件没有生效
在这里插入图片描述

       想要我们自己写的spring.xml生效,这就需要@ImportResource登场了,在我们的SpringBoot的主配置类入口上标注上@ImportResource(locations = {“classpath:spring.xml”})
在这里插入图片描述
       我们再运行测试一下,成功生效
在这里插入图片描述

2-3-6:@Configuration

       我们在Spring中知道,xml配置文件可以使用配置类来代替,只需要在配置类中使用注解@Configuration就可以声明这个类是配置类,然后使用@Bean注解说明这个方法相当于配置文件的<bean>标签,SpringBoot提倡使用配置类的方式,代替2-3-5:@ImportResource的配置文件方式

       我们定义一个配置类MyConfig
在这里插入图片描述

package com.lingaolu.springbootdemo.config;import com.lingaolu.springbootdemo.bean.Programmer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;// @Configuration声明这个类是配置类
@Configuration
public class MyConfig {// @Bean相当于配置文件里的<bean>标签,注入到IOC容器中,key为方法名@Beanpublic Programmer programmer2(){return new Programmer();}}

       结果
在这里插入图片描述

2-3-7:配置文件占位符

       配置文件占位符使用的是${表达式},其中可以使用随机数等等
在这里插入图片描述

       我们的Programmer类代码

package com.lingaolu.springbootdemo.bean;import com.lingaolu.springbootdemo.factory.YamlAndPropertySourceFactory;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Data
@Component
@ConfigurationProperties(prefix="pro")
public class Programmer {private Integer age;private String name1;private String name2;private String name3;private String name4;}

       配置文件代码

pro:age: 24name1: ${random.uuid}name2: ${pro.name1}name3: ${pro.name}name4: ${pro.name:}

结果说明
在这里插入图片描述

2-4:多配置环境Profile

       我们在协作开发的过程中,通常需要多配置环境,比如开发是一套环境,上线又是一套环境,这就需要多环境配置,需要用到什么环境就切换到相应的配置环境即可

2-4-1:多文档快方式

       在yml文件中,支持多文档快方式,使用—进行文档快的分割,如下
在这里插入图片描述

       结果

默认环境
在这里插入图片描述

使用激活dev环境
在这里插入图片描述

使用激活prod环境
在这里插入图片描述

2-4-2:多配置文件方式

       上面介绍了多文档快方式,但是如果配置很多的时候,使用多文档快方式容易搞错,所以还是需要多配置文件方式比较清楚,但是使用多配置文件方式,其文件名有要求

  • 文件名:application-环境名.yml

在这里插入图片描述

       结果

默认环境
在这里插入图片描述

使用激活dev环境
在这里插入图片描述

使用激活prod环境
在这里插入图片描述

2-4-3:激活指定的Profile环境

       激活指定的Profile环境的 方式有很多种,上面已经演示过一种,在配置文件中指定,下面再介绍其他的

在配置文件中指定

  • .properties文件
spring.profiles.active=prod
  • .yml文件
spring:profiles:active: prod

虚拟机参数-Dspring.profiles.active=prod
在这里插入图片描述
虽然我配置文件中指定了dev环境,但是运行的时候虚拟机参数指定了prod环境,所以实际的配置是prod环境,所以端口号是8082
在这里插入图片描述

命令行参数指定,–spring.profiles.active=prod
在这里插入图片描述
虽然我配置文件中指定了dev环境,但是运行的时候命令行参数指定了prod环境,所以实际的配置是prod环境,所以端口号是8082
在这里插入图片描述
关于命令行方式还可以这样操作,因为我们的项目最终打包成1个jar包
在这里插入图片描述
最终也是使用了我们指定的配置环境和端口号
在这里插入图片描述

       注意,当虚拟机参数和命令行参数同时存在时,最终已命令行参数为最终的配置

2-5:配置文件的加载位置与顺序

       SpringBoot启动会扫描以下位置的application.properties文件或者application.yml文件以及application.yaml文件作为SpringBoot的默认配置文件

  • file:./config/
  • file:./
  • classpath:/config/
  • classpath:/

       加载顺序为

  • 这些位置的加载顺序为由低到高,所以优先级为由高到低,高优先级的配置会覆盖掉低优先级的配置

  • 同一位置的加载顺序为:application.properties文件,application.yml文件,到application.yaml文件,所以优先级从高到低为application.yaml文件—application.yml文件—application.properties文件,优先级高的配置会覆盖掉低优先级的配置

  • 所有的配置文件都可以共同起作用进行互补配置

       同一位置,三种文件同时存在时示例

       示例如下

同一位置,三种文件同时存在时,最高优先级为.yaml文件
在这里插入图片描述

.yaml文件不存在相关的配置,那么.yml文件第二优先级
在这里插入图片描述

.yaml文件和.yml文件都不存在相关的配置,轮到第三优先级的.properties文件
在这里插入图片描述

       不同位置配置文件示例

       示例如下

4个位置都有配置文件时,最高优先级为项目路径下的config/配置文件,即file:./config/
在这里插入图片描述

项目路径下的config/配置文件没有相关配置时,轮到第2优先级的项目路径下的配置文件,即file:./
在这里插入图片描述

前2个配置文件没有相关配置时,轮到第3优先级的类路径下的config/的配置文件,即 classpath:/config/
在这里插入图片描述

前3个配置文件没有相关配置时,轮到第4优先级的类路径下的配置文件,即 classpath:/
在这里插入图片描述

       我们还可以通过–spring.config.location来改变默认的配置文件位置,项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认加载的这些配置文件共同起作用形成互补配置

       我们启动jar包,端口号是8089
在这里插入图片描述
       现在我们外部有一个配置文件,端口号是8090
在这里插入图片描述
       我们还是启动之前的jar包,只不过是通过–spring.config.location来改变默认的配置文件位置,这样端口号也变为了8090
在这里插入图片描述

3:SpingBoot的原理

       我们都知道,SpringBoot比Spring方便,是因为它帮我们默认配置了很多东西,那么SpringBoot到底能配置什么属性,我们可以从官网里面找SpringBoot可配置属性
在这里插入图片描述
       但是这里有那么多的属性,要记起来那就是头疼了,所以要方便了解SpringBoot可配置什么,去了解SpringBoot自动装配的原理是很有必要的。

       万事开头难,那么我们就从项目的源头开始入手,项目启动很简单,就只有一个注解@SpringBootApplication,看来这个注解帮我们处理很很多事,我们就从这个注解入手
在这里插入图片描述
       点进来发现了一个启用自动配置的注解,SpringBoot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration
在这里插入图片描述
       跟着开启自动配置的注解进去,发现了@Import(AutoConfigurationImportSelector.class)
在这里插入图片描述
       我们知道@Import是导入组件的意思,而AutoConfigurationImportSelector.class字面意思就是自动配置导入选择器,,也就是说,这里就选择了导入了自动配置的组件,那么具体导入什么组件呢,我们跟着AutoConfigurationImportSelector类进去看
在这里插入图片描述
       往下翻这个类,找到selectImports方法,方法里面有一个获取自动配置的方法getAutoConfigurationEntry
在这里插入图片描述
       我们跟着getAutoConfigurationEntry方法进去
在这里插入图片描述
       找到一个方法getCandidateConfigurations,获取候选的配置,返回的是配置列表,我们接着跟进去
在这里插入图片描述
       紧接着又是套了一个方法loadFactoryNames,这些娃一个套着一个,没办法,毕竟框架是别人写的,我们只能跟着进去
在这里插入图片描述
       又是套了一层loadSpringFactories,接着套,我们接着跟就是了
在这里插入图片描述
       这里获取了classLoader下的所有资源,那么这个classLoader到底是什么,返回上步看一下
在这里插入图片描述
       factoryType是传进来的,还得往上上一步找
在这里插入图片描述
       好了,我们知道classloader是EnableAutoConfiguration,回到classloader获取资源的地方
在这里插入图片描述
       跟到这里,对我们重要来说,的信息就是FACTORIES_RESOURCE_LOCATION了,看一下FACTORIES_RESOURCE_LOCATION
在这里插入图片描述
       好了,我们找一下META-INF/spring.factories
在这里插入图片描述
       跟着spring.factories
在这里插入图片描述
       找到了classloader(EnableAutoConfiguration)下的众多配置类,也就是SpringBoot启动的时候,会加载这些自动配置类,默认的帮我们的配置了很多东西,这也是SpringBoot比Spring方便的原因,现在,我们知道每一个这样的xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中,用它们来做自动配置,每一个自动配置类进行自动配置功能,但是我们通过配置文件修改默认的配置是如何办到的,别急,我们接着慢慢往下看

       我们在SpringBoot众多的自动配置类中随便找一个,进去看看,我这里随便找一个,就找了HttpEncodingAutoConfiguration
在这里插入图片描述
       我们看一下HttpEncodingAutoConfiguration
在这里插入图片描述

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ServerProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
  • @Configuration(proxyBeanMethods = false):表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件
  • @EnableConfigurationProperties(ServerProperties.class):启动指定类ServerProperties的ConfigurationProperties功能,将配置文件中对应的值和ServerProperties绑定起来
  • @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET):Spring底层@Conditional注解,根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效;@ConditionalOnWebApplication判断当前应用是否是web应用,如果是,则当前配置类生效
  • @ConditionalOnClass(CharacterEncodingFilter.class):判断当前项目有没有这个类CharacterEncodingFilter,此类是SpringMVC中进行乱码解决的过滤器
  • @ConditionalOnProperty(prefix = “server.servlet.encoding”, value = “enabled”, matchIfMissing = true):判断配置文件中是否存在某个配置server.servlet.encoding.enabled;matchIfMissing = true表示如果不存在,判断也是成立的,即使我们配置文件中不配置server.servlet.encoding.enabled=true,也是默认生效的

       以上就是根据不同的条件判断,决定这个配置类是否生效

在这里插入图片描述
       从我们的上图指示中,我们可以知道

  • 配置类的配置和属性Encoding properties相关
  • 属性private final Encoding properties是通过构造方法HttpEncodingAutoConfiguration中注入的ServerProperties获取,这个会自动从IOC容器中获取,因为ServerProperties已经注入到了IOC容器中

       那么我们的下一步重点就是ServerProperties
在这里插入图片描述
       打开ServerProperties进去看
在这里插入图片描述
       @ConfigurationProperties熟悉吗,那可太熟悉了,我们在讲从配置文件中获取值的时候,就说到了它,于是乎,我们明白,比如private Integer port,我们在配置端口号的时候server.port=端口号,通过@ConfigurationProperties获取配置文件的值,注入到了相关类ServerProperties,然后再通过@EnableConfigurationProperties(ServerProperties.class)注入到IOC容器中,接着自动配置类通过构造构造方法获取其属性值进行替换掉默认值,这就是SpringBoot自动配置的本质,也就是说,我们可在配置文件中配置的属性,其实都是spring.factories下的所有xxxAutoConfiguration里的注解@EnableConfigurationProperties类里面的属性

       比如我们上面举例的charset就是
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
       这里按理来说应该是server.servlet.encoding.charset.name=UTF-8,但是这里实际上可以server.servlet.encoding.charset=UTF-8
在这里插入图片描述

       在上面我们介绍了SpringBoot的自动配置原理,同时也接触了一些条件判断的注解,比如@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)等等,这些注解都是@Conditional的派生注解(Spring注解),其作用就是必须满足@Conditional指定的条件,才给容器中添加组件,配置里面的所有内容才生效,SpringBoot在@Conditional的基础上,扩展了很多注解

@Conditional扩展注解作用(判断是否满足当前指定条件)
@ConditionalOnJava系统的Java版本是否符合要求
@ConditionalOnBean容器中存在指定的Bean
@ConditionalOnMissingBean容器中不存在指定的Bean
@ConditionalOnExpression满足SpEL表达式指定
@ConditionalOnClass系统中有指定的类
@ConditionalOnMissingClass系统中没有指定的类
@ConditionalOnSingleCandidate容器中只有一个指定的Bean,或者这个Bean是首选的Bean
@ConditionalOnProperty系统中指定的属性是否有指定的值
@ConditionalOnResource类路径下是否存在指定资源文件

       在SpringBoot中,自动配置类必须在一定的条件下才能生效,我们要想知道哪些自动配置类生效,可以通过一个一个去看自动配置类的生效条件,但是这样太麻烦了,我们可以通过配置debug=true属性,来让控制台打印出自动配置报告,这样我们就可以方便的知道哪些自动配置类生效
在这里插入图片描述
       运行看一下。可以看到哪些自动配置生效,哪些没生效,没生效的原因是什么
在这里插入图片描述
在这里插入图片描述

4:SpingBoot的日志

       市面上常见的日志框架有:JUL , JCL , Jboss-logging , logback , log4j , log4j2 , slf4j等等,他们的分类如下:

日志门面(日志的抽象层)日志实现
JCL(Jakarta Commons Logging) , SLF4J(Simple Logging Facade for Java), Jboss-loggingLog4j , JUL(java.util.logging ) , Log4j2, Logback

       JCL是Apache公司开发的一个框架,Jakarta小组开发的,Spring Framework在使用,但是2014年已经停止更新了。SLF4J , Log4j , Logback是同一个人写的,这个人想优化Log4j,但是认为重新写比较麻烦,于是写了SLF4J这个抽象层日志框架,又写了Logback这个实现类。Log4j2也是Apache公司的,好多框架都没适配。Hibernate底层使用Jboss-logging实现。

       所以综合来说,我们需要在左边选一个门面(抽象层),右边选一个实现。以后开发的时候,我们调用日志门面抽象层里面的方法,而不直接调用日志的实现类,我们就选择了slf4j+logback,而SpringBoot默认的日志实现是正是使用slf4j+logback

       Slf4j的使用,我们先看slf4j官网:https://www.slf4j.org/
在这里插入图片描述
在这里插入图片描述
       使用前给项目导入slf4j的jar包和logback的实现jar

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class HelloWorld {public static void main(String[] args) {Logger logger = >LoggerFactory.getLogger(HelloWorld.class);logger.info("Hello World");}
}

       但是我们默认使用的是logback实现,我们要是想用其他实现也可以,毕竟slf4j只是一个抽象层,这里slf4j的官网已经给我们解释
在这里插入图片描述

  • 可以看到,只引入slf4j是不能够实现日志记录的,在引入slf4j-api.jar的基础上;
  • 如果要实现logback日志框架,需要引入logback-core.jar和logback-classic.jar;
  • 如果要实现log4j日志框架,需要引入slf4j-log412.jar(log4j适配slf4j的jar),log4j.jar。slf4j-log412.jar起到了适配slf4j-api.jar和log4j.jar其他情况类似。
  • 每一个日志实现框架都有自己的配置文件,使用slf4j以后,配置文件还是做成日志实现框架自己本身的配置文件。比如实现框架选用logback,那么配置文件就写logback.xml这种,实现框架选log4j,那么配置问价就选log4j.xml这种。

       我们知道SpringBoot日志采用的是slf4j+logback,而SpringBoot的底层是Spring,Spring底层使用的是Commons-Logging,那么这样系统就存在了2种日志门面,于是SpringBoot做了处理,统一日志记录,即使是别的框架也和我一起统一使用slf4j进行输出,SpringBoot做了包装,统一使用logback进行配置。具体做法如下图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
       如上图,比如Spring框架,为了达到上述目标,我们需要排除掉Spring底层的commons-logging包,(不引入Spring会报包找不到的错误)然后引入包装层jcl-over-slf4j,这个包装层jar的包路径,类名等都采用commons-logging的形式进行配置,但是底层实现是slf4j,最后添加logback的jar包。

       统一日志记录的思路(SpringBoot实现统一日志记录为slf4j的做法):

  • 先排除掉其他日志框架
  • 然后用中间包来替换排除掉的日志框架
  • 最后再添加目标日志框架

       所以当我们在SpringBoot引入新框架的时候,如果此框架使用了JUL,JCL,或者log4j,这个时候我们应该排除掉原来的日志框架,避免因为现在SpringBoot中存在相同的日志实现而造成jar包冲突冲突。

       注意:在SpringBoot2.x版本的时候,上述的实现方式发生了一些改变,中间引入了"桥接"的概念,没有直接通过模拟类名实现,比如类:SLF4JBridgeHandler,但是其底层的实现方法都是类似的,都是通过排除原有依赖实现,比如spring-boot-starter-logging的依赖
       JUL的桥接模式的实现是继承JUL的Handler抽象类,按源码上的注释所说,是实现一种redirected重定向
在这里插入图片描述

       注意实现由log4j-over-slf4j变成了log4j-to-slf4j

       说了那么多,该来说说SpringBoot中日志的使用了

package com.lingaolu.springbootdemo;import com.lingaolu.springbootdemo.bean.Programmer;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;@SpringBootTest
class SpringbootdemoApplicationTests {@AutowiredProgrammer programmer2;private Logger logger = LoggerFactory.getLogger(this.getClass());@Testvoid contextLoads() {System.out.println(programmer2);logger.trace("trace跟踪轨迹");logger.debug("debug调试");logger.info("info信息");logger.warn("warn警告");logger.error("error错误");}}

       结果
在这里插入图片描述

       上面我们的某些日志没有输出出来这就和日志的配置有关

       SpringBoot默认是info级别,如果没有指定级别就使用 info 级别,这个默认级别也叫 root级别,想要调整某个包的日志级别可以通过application.properties进行配置,例:

logging.level.com.lingaolu.springbootdemo=debug

       com.lingaolu.springbootdemo是我自己的包路径,设置完后,就可以看到debug级别以及这级别以上的日志了,日志的级别从低到高分别是trace,debug,info ,warn,error
在这里插入图片描述

       有时候想查看Mybatis执行的具体sql语句可以将mapper对应的包路径日志级别设置为debug级别,除了日志级别的配置,还有很多配置,比如

logging.level.com.lingaolu.springbootdemo=debug
# 配置在指定目录,不指定路径的时候就是配置在项目路径下
logging.file.path=C:/Users/Administrator/Desktop/文章/log
# 配置指定的日志名,不指定的话默认为spring.log
logging.file.name=C:/Users/Administrator/Desktop/文章/log/my.log
# 在控制台输出的日志的格式
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
# 在文件中日志输出的格式
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n

       %d-时间格式、%thread-线程、%-5level-日志级别从左5字符宽度、%logger{50}-日志50个字符、%msg-信息、%n-换行

       注意,logging.file.path和logging.file.name会有冲突两者都指定,以logging.file.path为主,如下表

logging.file.namelogging.file.pathExampleDescription
未配置未配置只在控制台输出
指定文件名未配置my.log输出日志到my.log文件
未配置指定目录C:/Users/Administrator/Desktop/文章/log输出到指定目录的spring.log文件中
指定文件名指定目录C:/Users/Administrator/Desktop/文章/log/my.log
C:/Users/Administrator/Desktop/文章/log
输出到指定目录的spring.log文件中

       除此之外,还可以配置日志滚动条件:比如配置日志文件满10M就记录到下一个文件,文件名以i递增,或者按天进行滚动。日志配置文件的名称:logback-spring.xml或者logback.xml,推荐使用logback-spring.xml,因为配置成logback-spring.xml是由SpringBoot自己加载,不是由日志框架直接加载,可以使用高级特性:,通过这个特性,可以配置不同环境配置不同的日志级别,比如开发环境使用debug,生产环境使用info。

       比如现在我建立了一个logback-spring.xml文件
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false"><!--设置存储路径变量--><property name="LOG_HOME" value="./log"/><!--控制台输出appender--><appender name="console" class="ch.qos.logback.core.ConsoleAppender"><!--设置输出格式--><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--><pattern>日志%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern><!--设置编码--><charset>UTF-8</charset></encoder></appender><!--文件输出,时间窗口滚动--><appender name="timeFileOutput" class="ch.qos.logback.core.rolling.RollingFileAppender"><!--日志名,指定最新的文件名,其他文件名使用FileNamePattern --><File>${LOG_HOME}/timeFile/out.log</File><!--文件滚动模式--><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--日志文件输出的文件名,可设置文件类型为gz,开启文件压缩--><FileNamePattern>${LOG_HOME}/timeFile/info.%d{yyyy-MM-dd}.%i.log.gz</FileNamePattern><!--日志文件保留天数--><MaxHistory>30</MaxHistory><!--按大小分割同一天的--><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>10MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy></rollingPolicy><!--输出格式--><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--><pattern>测试日志%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern><!--设置编码--><charset>UTF-8</charset></encoder></appender><!--指定基础的日志输出级别--><!--<root level="INFO">--><!--&lt;!&ndash;appender将会添加到这个loger&ndash;&gt;--><!--<appender-ref ref="console"/>--><!--<appender-ref ref="timeFileOutput"/>--><!--</root>--><!-- 测试环境+开发环境. 多个使用逗号隔开. --><springProfile name="test,dev"><!-- com.lingaolu.springbootdemo是我的包路径 --><!-- 不激活状态additivity="false"--><logger name="com.lingaolu.springbootdemo" level="DEBUG" additivity="false"><appender-ref ref="console"/></logger></springProfile><!-- 生产环境. --><springProfile name="prod"><!-- com.lingaolu.springbootdemo是我的包路径 --><!-- 不激活状态additivity="false"--><logger name="com.lingaolu.springbootdemo" level="INFO" additivity="false"><appender-ref ref="timeFileOutput"/></logger></springProfile>
</configuration>

       然后我们的SpringBoot的配置环境开启了开发环境的配置
在这里插入图片描述

spring.profiles.active=dev

       运行结果,相应的开发环境配置起作用
在这里插入图片描述
       切换到生产环境prod也一样,相应的prod环境配置起作用,日志写入文件,没有打印在控台
在这里插入图片描述

5:SpingBoot的Web开发

       新建一个干净的SpringBoot项目,新建一个接口,启动
在这里插入图片描述
       访问
在这里插入图片描述

       我们上面所示都是基于前后端分离的,因为我们写的接口返回的就是数据字符串,但是如果我们想要返回到自己的页面,也就是SpringBoot的非前后端分离项目,怎么办,看ssm项目都是有一个webapp的目录,而我们的SpringBoot的项目目录下没有这个目录,那么SpringBoot是如何实现页面的跳转访问等,这就需要我们往下学习

5-1:静态资源的访问

       在第三章SpringBoot的原理中,我们了解到SpringBoot的自动配置,那么关于SpringBoot的Web配置中,在WebMvcAutoConfiguration帮我们自动配置了
在这里插入图片描述

       WebMvcAutoConfiguration的源码走起,涉及的主要方法在这里,我就不仔细带看源码了,自己看

  • addResourceHandlers
  • WelcomePageHandlerMapping

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

       静态资源的访问

       1:/webjars/映射到 classpath:/META-INF/resources/webjars/

webjars的官网https://www.webjars.org/
在这里插入图片描述
POM依赖

<dependency><groupId>org.webjars</groupId><artifactId>jquery</artifactId><version>3.5.1</version>
</dependency>

访问
在这里插入图片描述

       2:默认静态资源映射路径

  • classpath:/META-INF/resources/
  • classpath:/resources/
  • classpath:/static/
  • classpath:/public/

在这里插入图片描述
也就是1-4的html页面都能访问到
在这里插入图片描述

       3:首页路径

首页的路径就是在第2的任意目录下添加index.html即可
在这里插入图片描述
在这里插入图片描述

       4:修改默认icon

正常情况下,每个网站都会有一个对应的网站图标(Favicon),在浏览器访问网站时,对应的浏览器标签上会出现对应的图标。如下图百度的图标:
在这里插入图片描述
对此Spring Boot项目也提供了支持,但不同版本有所区别,在最新版本中的使用,网络上大多数文章已经失效,本篇文章带大家看一下Spring Boot 2.x版本中的使用情况。在早些版本中Spring Boot对Favicon进行了默认支持,并且通过如下配置进行关闭操作:(不同的版本不一样,具体配置可看自动配置的属性文件)

spring.mvc.favicon.enabled=false ## 关闭

然后在第2的任意目录下添加favicon.ico即可,默认显示效果如下
在这里插入图片描述
我的ico为
在这里插入图片描述
效果为
在这里插入图片描述
但在Spring Boot项目的issues中提出,如果提供默认的Favicon可能会导致网站信息泄露。如果用户不进行自定义的Favicon的设置,而Spring Boot项目会提供默认的上图图标,那么势必会导致泄露网站的开发框架。因此,在Spring Boot2.2.x中,将默认的favicon.ico移除,同时也不再提供上述application.properties中的属性配置。更多详细信息可查看对应的issues:https://github.com/spring-pr

       以上路径我们都可以进行属性文件上配置修改,知道SpringBoot的自动配置原理,那就可以相应的配置其对应的属性来代替其默认的配置,比如如下配置

spring.web.resources.static-locations=classpath:/hehe/

       就可以把静态资源的路径变为类路径下的hehe文件夹下

5-2:模板引擎thymeleaf

5-2-1:SpringBoot使用thymeleaf

       POM添加依赖

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

       如果要切换版本的话,可以去git查看最新的版本https://github.com/thymeleaf/thymeleaf/releases,然后在Maven配置文件中添加版本配置

<properties><thymeleaf.version>3.0.12.RELEASE</thymeleaf.version><!-- 布局功能的支持程序,thymeleaf3主程序,要layout2以上版本 --><thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version>
</properties>

       我们先去看一下springBoot对thymeleaf的自动配置ThymeleafAutoConfiguration
在这里插入图片描述
在这里插入图片描述
       从这里我们可以看出,默认的前缀是"classpath:/templates/",后缀是".html",那么我们默认的页面就要放在类路径下的templates文件夹下,我们写一个测试页面
在这里插入图片描述
       基于默认配置的前缀和后缀,我们的接口就可以这样写实现页面跳转

package com.lingaolu.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
public class HelloWord {@RequestMapping("/test")public  String test(){return "myTest/index";}
}

       启动测试
在这里插入图片描述

5-2-2:thymeleaf的语法

       开发期间模板引擎页面修改后,要实时生效

  • 禁用模板引擎的缓存
spring.thymeleaf.cache=false
  • 页面修改完成以后ctrl+f9,重新编译

       学语法最好的方式就是看官方文档,https://www.thymeleaf.org/documentation.html
在这里插入图片描述
       把PDF文件下下来,就是我文档了,这里不做多讲解,举个例子就可以了,剩下的慢慢去看吧

       接口

package com.lingaolu.controller;import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;import java.util.ArrayList;
import java.util.List;
import java.util.Map;@Controller
public class HelloWord {class Student{private String name;public Student(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}}@RequestMapping("/test")public  String test(Model model){model.addAttribute("hello","<h1>hello</h1>");List<Student> studentList = new ArrayList<>();studentList.add(new Student("张三"));studentList.add(new Student("李四"));model.addAttribute("students",studentList);return "myTest/index";}
}

       index.html页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title th:value="${hello}">test</title></head>
<body><!-- 标签不起作用 --><div th:text="${hello}">这是div1</div><!-- 保留标签的作用 --><div th:utext="${hello}">这是div2</div><!-- 遍历 --><h1 th:text="${student.name}" th:each="student:${students}"></h1>
</body>
</html>

       结果
在这里插入图片描述

       html页面添加以下头文件是为了写代码的时候,有提示

xmlns:th="http://www.thymeleaf.org"

5-3:SpringMVC自动配置原理

       Spring官方文档https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-mvc-auto-configuration
在这里插入图片描述

1- Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
2- Support for serving static resources, including support for WebJars (covered later in this document)).
3- Automatic registration of Converter, GenericConverter, and Formatter beans.
4- Support for HttpMessageConverters (covered later in this document).
5- Automatic registration of MessageCodesResolver (covered later in this document).
6- Static index.html support.
7- Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).

       其对应解释

  • 1-自动配置了视图解析器
  • 2-静态资源文件夹路径
  • 3-自动注册了转换器和格式化器
  • 4-消息转换,SpringMVC用来转换Http请求和响应的
  • 5-定制错误代码生成规则
  • 6-首页
  • 7-初始化wb数据绑定器

       那么多功能,都是默认自动配置的,我们如何扩展或者替代,文档上也有说明,比如
在这里插入图片描述
       其他的明白了其自动配置的原理,都可以自己处理,比如,我们就用第一个做例子,自动配置了视图解析器,找到MVC自动配置类WebMvcAutoConfiguration的viewResolver方法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
       基于上述源码,我们知道想增强视图解析器,我们只要往容器丢进去ViewResolver就可以了,所以我们自己增强的视图解析器,实现ViewResolver接口,然后丢进去容器,那么项目启动的就会从容器找到我们的解析器

       我们打个断点,先看一下默认的视图解析器
在这里插入图片描述
       然后我们写自己的视图解析器

package com.lingaolu.viewResolver;import org.springframework.stereotype.Component;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import java.util.Locale;@Component
public class MyViewResolver implements ViewResolver {@Overridepublic View resolveViewName(String viewName, Locale locale) throws Exception {// ....实现功能return null;}
}

       重启看一下
在这里插入图片描述
       至于其他的功能看文档或者源码,也就清楚该如何处理

5-4:扩展或全面接管SpringMVC

5-4-1:扩展SpringMVC

       官方文档永远都是最好的东西https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-mvc-message-converters
在这里插入图片描述
       If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.其解释为如果要保留这些Spring Boot MVC定制并进行更多的MVC定制(拦截器,格式化程序,视图控制器和其他功能),则可以添加自己的类型为WebMvcConfigurer的@Configuration类,但不添加@EnableWebMvc。

       直接上例子,我们写自己的配置类来扩展SprngMVC大的功能,老的做法是继承WebMvcConfigurerAdapter类,因为WebMvcConfigurerAdapter类实现了WebMvcConfigurer接口,所以继承WebMvcConfigurerAdapter类也就相当于是WebMvcConfigurer类型,并且继承的话可以不用重写所有的方法
在这里插入图片描述
       有横线说明这种方式已经过时了,这个类已经标上了@Deprecated注解
在这里插入图片描述

       所以新的做法是直接实现WebMvcConfigurer接口,因为JDK8之后接口有了新功能,WebMvcConfigurer接口也使用了这个新功能,方法不再是抽象的,而是改为默认的,这样实现了WebMvcConfigurer接口也不需要实现所有的方法,具体新功能介绍在我的一篇博客有介绍https://lingaolu.blog.csdn.net/article/details/106498820,所以新的WebMvcConfigurer接口其方法都是默认方法
在这里插入图片描述
       我们写个例子来扩展视图处理器,访问/aa跳到/myTest/index
在这里插入图片描述

package com.lingaolu.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class MyMvcConfig implements WebMvcConfigurer {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/aa").setViewName("/myTest/index");}
}

       启动项目访问
在这里插入图片描述

5-4-2:扩展SpringMVC的原理

       既然是MVC,那肯定是离不开WebMvcAutoConfiguration
在这里插入图片描述
在这里插入图片描述
       其父类有方法,自动注入,把容器里的所有WebMvcConfigurer加入到WebMvcConfigurerComposite类的delegates列表
在这里插入图片描述
在这里插入图片描述
       到这里WebMvcConfigurerComposite类的delegates列表已经存放了容器中的所有WebMvcConfigurer类型数据
在这里插入图片描述
在这里插入图片描述
       而delegates列表存放的就是所有WebMvcConfigurer类型数据,所以当我们写自己的配置实现了WebMvcConfigurer接口,并且注入到容器,那么当程序运行的时候,就会获取容器中所有WebMvcConfigurer类型数据存放到delegates列表,就会遍历所有的数据执行相应的方法,也就会调用到我们的配置类,执行相应的重写的方法

5-4-3:全面接管SpringMVC

       见官网文档
在这里插入图片描述
       只需要在我们自己的配置类中加上注解**@EnableWebMvc**,这就是为什么我们上面扩展SpringMVC的时候,说到不要加上@EnableWebMvc注解的原因,因为加上就是全面接管了,那么SpringMVC的所有默认配置都失效,只有我们的配置起作用,一般来说不建议这么做,因为默认配置帮我们处理了很多的功能,除非你很强大,自己写的比默认的好

       那么其原理是什么,为什么加上注解**@EnableWebMvc就全面接管了,我们看一下这个注解
在这里插入图片描述
在这里插入图片描述
       也就是说,注解
@EnableWebMvc相当于导入了WebMvcConfigurationSupport类,而我们的SpringMVC的自动配置的条件为
在这里插入图片描述
       所以我们自己的配置类使用了注解
@EnableWebMvc后,相当于导入了WebMvcConfigurationSupport**类,那就使得了SpringMVC的默认配置不生效,也就是全面接管了SpringMVC的配置

5-5:国际化

       使用国际化步骤

  • 修改Idea配置,毕竟涉及中文和properties文件
  • 编写国际化配置文件,书写默认的properties文件和各个语言相对应的properties文件
  • 设置国际化资源基础名
  • 页面获取国际化资源属性
  • 书写我们自己的区域信息解析器配置类,替换掉默认SpringBoot默认的解析器

修改Idea配置

在这里插入图片描述

编写国际化配置文件

       新建目录,书写默认的配置文件。比如这里取名为aa.properties,定的基础名为aa
在这里插入图片描述
       再新建一个中文的配置文件,其命名格式为aa_语言_区域.properties,比如我们这里中文是aa_zh_CN.properties
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
       这里我们再添加英文和日文
在这里插入图片描述
       编写配置
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

       我们添加了name属性,用一个来举例即可,默认保存,我们的国家化属性文件就出来;
在这里插入图片描述

设置国际化资源基础名

       先看国际化的默认自动配置MessageSourceAutoConfiguration
在这里插入图片描述
在这里插入图片描述
       默认的基础名是messages,默认目录为类资源路径下,而我们定的基础名是aa,在i18n的文件夹下,所以要配置修改

spring.messages.basename=i18n.aa

页面获取国际化资源属性

       先来一个页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>首页</title>
</head>
<body><h1 th:text="#{name}"></h1>
</body>
</html>

在这里插入图片描述
       测试结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

书写区域信息解析器配置类

       上一步已经得到了我们的结果,但是需要用户自己调浏览器使用的语言,这个对于用户来说肯定是不可行的,同时我们也知道变动的结果只需要改动Content-Language:参数即可,所以我们可以在页面上提供语言选项给用户选择,按钮访问经过我们的区域信息解析器配置类,如下页面代码

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>首页</title>
</head>
<body><h1 th:text="#{name}"></h1><a th:href="@{/(contentLanguage='zh_CN')}">中文</a><a th:href="@{/(contentLanguage='en_US')}">English</a><a th:href="@{/(contentLanguage='ja_JA')}">日本語</a>
</body>
</html>

在这里插入图片描述
       接着就是我们的区域信息解析器

package com.lingaolu.config;import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;public class MyLocalResolver implements LocaleResolver {// 重写resolveLocale方法@Overridepublic Locale resolveLocale(HttpServletRequest request) {String contentLanguage = request.getParameter("contentLanguage");// 默认的语言区域Locale locale = Locale.getDefault();if (StringUtils.hasLength(contentLanguage)) {// 参数存在,就使用参数的语言区域String[] split = contentLanguage.split("_");locale = new Locale(split[0],split[1]);}return locale;}@Overridepublic void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {}
}

       接着就是扩展SpringMVC,把我们的区域信息的配置类注入到容器中

package com.lingaolu.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class MyMvcConfig implements WebMvcConfigurer {@Beanpublic LocaleResolver localeResolver(){return new MyLocalResolver();}
}

       启动访问
在这里插入图片描述
       切换语言按钮即可切换为相应的语言

5-6:登录拦截

       先来一个登录接口

package com.lingaolu.controller;import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpSession;
import java.util.Map;@Controller
public class LoginController {@PostMapping(value = "/user/login")public String login(@RequestParam("userName") String userName,@RequestParam("passWord") String passWord,Map<String, Object> map,HttpSession session) {// 判断账号密码是否正确if(StringUtils.hasLength(userName) && "admin".equals(userName)){// 登录成功写入session,用于拦截器判断是否放行session.setAttribute("loginUser",userName);// 登录成功,防止表单重复提交,重定向到main页面return "redirect:/main";}// 失败重新登录map.put("msg","用户名密码错误");return "index";}
}

       再来一个登录页,index.html,我把首页当成登录页

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>登录页</title>
</head>
<body><!--如果msg不为空,就生成p标签,显示错误信息msg--><p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p><form action="/user/login" method="post">用户名:<input name="userName" />密码:<input name="passWord" /><input type="submit" value="登录"></form>
</body>
</html>

       主页

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>主页</title>
</head>
<body>这是主页
</body>
</html>

       来个拦截器

package com.lingaolu.interceptor;import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class LoginHandlerInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {Object user = request.getSession().getAttribute("loginUser");if(user == null){// 未登录,返回登录页面request.setAttribute("msg","没有权限请先登录");// 请求转发会登录页request.getRequestDispatcher("/").forward(request,response);return false;}return true;}
}

       注册拦截器,也就是扩展SpringMVC,把我们的拦截器注入到容器中

package com.lingaolu.config;import com.lingaolu.interceptor.LoginHandlerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class MyMvcConfig implements WebMvcConfigurer {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {// 简单路径映射,不需要那么麻烦写Controller层registry.addViewController("/").setViewName("index");registry.addViewController("/index").setViewName("index");registry.addViewController("/main").setViewName("main");}// 注册拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {// *.css,*.js等静态资源,SpringBoot已经处理了映射registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/user/login","/");}// 注册自定义的国际化区域语言解析/* @Beanpublic LocaleResolver localeResolver(){return new MyLocalResolver();}*/
}

       到这里登录拦截做完了

5-7:定制错误页面

       SpringBoot的错误自动配置主要分为2种情况

  • 浏览器访问,默认返回一个错误页面
    在这里插入图片描述
  • 其他客户端,默认响应json数据
    在这里插入图片描述

       SpringBoot的错误自动配置类为ErrorMvcAutoConfiguration,主要是给容器中添加了一下组件

  • DefaultErrorAttributes
  • BasicErrorController
  • ErrorPageCustomizer
  • DefaultErrorViewResolver

       其主要的原理不说了,你们自己看源码,主要就是这2个方法区分是浏览器还是其他客户端访问的
在这里插入图片描述
       这是通过请求头决定区分的,比如浏览器的请求头
在这里插入图片描述
       其他客户端请求头,比如postman
在这里插入图片描述

       能获取到的错误信息有

  • timestamp:时间戳
  • status:状态码
  • error:错误提示
  • message:错误消息
  • exception:异常对象
  • errors:JSR303数据校验的错误都在这里

5-7-1:如何定制异常的页面

  • 有模板引擎的情况下:error/状态码,将错误页面命名为错误状态码.html放在模板引擎文件夹里面的error文件夹下,发生此状态码的错误就会来到对应的页面,我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html)
  • 没有模板引擎(模板引擎找不到这个错误页面),静态资源文件夹下找
  • 以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面

       我们针对第一种情况举例,我来个自己的错误页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>错误页</title>
</head>
<body>时间戳:<span th:text="${timestamp}"></span><br/>状态码:<span th:text="${status}"></span><br/>错误提示:<span th:text="${error}"></span><br/>异常对象:<span th:text="${exception}"></span><br/>JSR303数据校验的错误:<span th:text="${errors}"></span></body>
</html>

       访问404结果
在这里插入图片描述
       又如搞一个自定义异常

package com.lingaolu.exception;public class MyException extends RuntimeException {public MyException(String message) {super(message);}
}

       来一个500页面5xx.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>异常页</title>
</head>
<body>时间戳:<span th:text="${timestamp}"></span><br/>状态码:<span th:text="${status}"></span><br/>错误提示:<span th:text="${error}"></span><br/>错误提示:<span th:text="${message}"></span><br/>异常对象:<span th:text="${exception}"></span><br/>JSR303数据校验的错误:<span th:text="${errors}"></span>
</body>
</html>

       要想显示自定义异常,SpringBoot2.x版本还要配置

server.error.include-exception=true
server.error.include-message=always

       来一个接口,直接抛出我们的异常

package com.lingaolu.controller;import com.lingaolu.exception.MyException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
public class HelloWord {@RequestMapping("/test")public  String test(){throw new MyException("自定义异常");}
}

       启动访问接口

在这里插入图片描述

5-7-2:如何定异常的json数据

       我们要弄就弄好一点,我们使用lombok,简化一下代码

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>

       先来一个错误的消息封装类ErrResponseBodyVo

package com.lingaolu.exception;import lombok.Data;@Data
public class ErrResponseBodyVo<T> {private int code;private String msg;private String sys;private String url;private T data;
}

       来一个枚举ExceptionStatusCode,定义异常code和消息的枚举,当然也可以搞一个父类出来继承,这样更方便封装

package com.lingaolu.enums;public enum ExceptionStatusCode {/*** 异常枚举*/A(100000,"A异常!"),B(100001,"B异常!");ExceptionStatusCode(int key, String value) {this.key = key;this.value = value;}// 成员变量private int key;private String value;public int getKey() {return key;}public void setKey(int key) {this.key = key;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}
}

       定义自己的异常MyException,构造方法可以自己决定,这里就简单定一个2个,其中一个接收上面的枚举

package com.lingaolu.exception;import com.lingaolu.enums.ExceptionStatusCode;
import lombok.Data;@Data
public class MyException extends RuntimeException {private ExceptionStatusCode exceptionStatusCode;private String data;public MyException(String message) {super(message);}public MyException(ExceptionStatusCode exceptionStatusCode, String data){this(exceptionStatusCode.getValue());this.exceptionStatusCode=exceptionStatusCode;this.data = data;}
}

       来一个测试接口,抛出自己的异常

package com.lingaolu.controller;import com.lingaolu.enums.ExceptionStatusCode;
import com.lingaolu.exception.MyException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
public class HelloWord {@RequestMapping("/test")public  String test(){// int a = 1/0;throw new MyException(ExceptionStatusCode.A,"这就是A异常-测试");}
}

       第一种做法,处理5xx异常

       来一个异常处理器MyExceptionHandler

package com.lingaolu.exception;import com.lingaolu.enums.ExceptionStatusCode;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;// 标注为异常处理器
@ControllerAdvice
public class MyExceptionHandler {// 返回的是json数据@ResponseBody// 处理的异常@ExceptionHandler(Exception.class)public ErrResponseBodyVo handleException(Exception e,HttpServletRequest req){ErrResponseBodyVo errResponseBody=new ErrResponseBodyVo();errResponseBody.setSys("某某服务");errResponseBody.setUrl(req.getRequestURL().toString());// 如果是自己抛出的异常if( e instanceof MyException ){MyException myException = ((MyException)e);ExceptionStatusCode exceptionStatusCode = myException.getExceptionStatusCode();errResponseBody.setMsg(myException.getMessage());errResponseBody.setData(myException.getData());if (null != exceptionStatusCode) {errResponseBody.setCode(exceptionStatusCode.getKey());}} else {    // 系统自带的异常,比如除以0这种// 写死500.因为我们获取不到系统自带的异常状态码errResponseBody.setCode(500);errResponseBody.setMsg(e.getMessage());}return errResponseBody;}
}

       访问接口结果,postman
在这里插入图片描述
       访问接口结果,浏览器
在这里插入图片描述
       但是这种方式只能处理5xx异常,一但是404异常就捕获不到,以为根本就没跑进这个处理器里边,404还是走默认的异常处理方式
在这里插入图片描述

       第二种做法

       上面第一种做法我们获取不到系统自带的异常状态码,所以我们写死了500,要想获取状态码,并且处理4.4异常,我们可以使用第二种方式

       先看源码,我们上面已经知道,返回页面还是json数据的方法在BasicErrorController类的方法中
在这里插入图片描述
       接着再看自动配置的源码
在这里插入图片描述
       BasicErrorController的父类也是实现了ErrorController,所以我们明白,只要我们自定义ErrorController对象,那就完全可以替代BasicErrorController,所以我们自定义MyBasicErrorController实现ErrorController,这样就可以代替了自动配置的BasicErrorController,再写返回json数据的方法error,系统最终就会找到我们自定义的BasicErrorController类来找到相应的方法error

package com.lingaolu.controller;import com.lingaolu.enums.ExceptionStatusCode;
import com.lingaolu.exception.ErrResponseBodyVo;
import com.lingaolu.exception.MyException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/**
* 错误异常处理控制类
*/
@RestController
@RequestMapping("/error")
public class MyBaseErrorController implements ErrorController {private static final String PATH = "/error";@Value("${spring.application.name}")private String applicationName;@Overridepublic String getErrorPath() {return PATH;}@RequestMappingpublic ErrResponseBodyVo error(HttpServletRequest request, HttpServletResponse response) {ErrResponseBodyVo errResponseBody=new ErrResponseBodyVo();errResponseBody.setSys(applicationName);errResponseBody.setUrl(request.getRequestURL().toString());// 获取状态码和异常Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");Exception exception=(Exception) request.getAttribute("javax.servlet.error.exception");// 如果是自己抛出的异常if( exception!=null){if(exception.getCause() instanceof MyException) {MyException myException=((MyException) exception.getCause());ExceptionStatusCode exceptionStatusCode = myException.getExceptionStatusCode();errResponseBody.setMsg(myException.getMessage());errResponseBody.setData(myException.getData());if (null != exceptionStatusCode) {errResponseBody.setCode(exceptionStatusCode.getKey());}} else {// 系统自带的异常errResponseBody.setCode(statusCode);errResponseBody.setMsg(exception.getMessage());}} else {// 系统自带的异常errResponseBody.setCode(statusCode);errResponseBody.setMsg(HttpStatus.valueOf(statusCode).getReasonPhrase());}return errResponseBody;}}

       属性文件配置上application.name

spring.application.name =某某服务

       启动访问
在这里插入图片描述

在这里插入图片描述

       第三种做法,两者结合

       发现这种方式有一个缺点,就是获取不到访问路径,因为都变为了/error,所以如果想弥补这个缺点,可以两者结合,使用第一种异常处理器处理抛出的异常,使用第二种错误异常处理控制类处理404

package com.lingaolu.exception;import com.lingaolu.enums.ExceptionStatusCode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;// 标注为异常处理器
@ControllerAdvice
public class MyExceptionHandler {@Value("${spring.application.name}")private String applicationName;// 返回的是json数据@ResponseBody// 处理的异常@ExceptionHandler(Exception.class)public ErrResponseBodyVo handleException(Exception e,HttpServletRequest req){ErrResponseBodyVo errResponseBody=new ErrResponseBodyVo();errResponseBody.setSys(applicationName);errResponseBody.setUrl(req.getRequestURL().toString());// 如果是自己抛出的异常if( e instanceof MyException ){MyException myException = ((MyException)e);ExceptionStatusCode exceptionStatusCode = myException.getExceptionStatusCode();errResponseBody.setMsg(myException.getMessage());errResponseBody.setData(myException.getData());if (null != exceptionStatusCode) {errResponseBody.setCode(exceptionStatusCode.getKey());}} else {    // 系统自带的异常,比如除以0这种errResponseBody.setCode(500);errResponseBody.setMsg(e.getMessage());}return errResponseBody;}
}
package com.lingaolu.controller;import com.lingaolu.exception.ErrResponseBodyVo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/**
* 错误异常处理控制类
*/
@RestController
@RequestMapping("/error")
public class MyBaseErrorController implements ErrorController {private static final String PATH = "/error";@Value("${spring.application.name}")private String applicationName;@Overridepublic String getErrorPath() {return PATH;}@RequestMappingpublic ErrResponseBodyVo error(HttpServletRequest request, HttpServletResponse response) {ErrResponseBodyVo errResponseBody=new ErrResponseBodyVo();errResponseBody.setSys(applicationName);errResponseBody.setUrl(request.getRequestURL().toString());// 获取状态码和异常Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");errResponseBody.setCode(statusCode);errResponseBody.setMsg(HttpStatus.valueOf(statusCode).getReasonPhrase());return errResponseBody;}}

5-7-3:自动处理是返回页面还是返回json数据

       在我们的5-7-2定制json数据中,返回的都是json数据,对于前后端分离的项目来说,这个足够了,但是对于非前后端分离的项目,我想浏览器访问就到页面,其他客户端访问就是json数据

       还是源码
在这里插入图片描述
       基于5-7-2,我们知道新建一个ErrorController代替自动配置的BasicErrorController,写了error方法即可实现json数据的定制,那么同理,写了errorHtml方法那么也可实现页面的定制,这2个都写就可以实现自动分配页面还是json数据

       我们的异常处理器不抛出json了,而是重定向到/error

package com.lingaolu.exception;import com.lingaolu.enums.ExceptionStatusCode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.servlet.http.HttpServletRequest;// 标注为异常处理器
@ControllerAdvice
public class MyExceptionHandler {@Value("${spring.application.name}")private String applicationName;// 处理的异常@ExceptionHandler(Exception.class)public String handleException(Exception e,HttpServletRequest req){ErrResponseBodyVo errResponseBody=new ErrResponseBodyVo();errResponseBody.setSys(applicationName);errResponseBody.setUrl(req.getRequestURL().toString());// 如果是自己抛出的异常if( e instanceof MyException ){MyException myException = ((MyException)e);ExceptionStatusCode exceptionStatusCode = myException.getExceptionStatusCode();errResponseBody.setMsg(myException.getMessage());errResponseBody.setData(myException.getData());if (null != exceptionStatusCode) {errResponseBody.setCode(exceptionStatusCode.getKey());}} else {    // 系统自带的异常,比如除以0这种errResponseBody.setMsg(e.getMessage());}req.setAttribute("errResponseBody",errResponseBody);return "forward:/error";}
}

       重定向到/error之后,确实是会自动判断是返回页面还是json数据,但是我们是要定制返回内容,所以我们要自己的ErrorController,所以我们就写自己的ErrorController,重写error和errorHtml方法即可

package com.lingaolu.controller;import com.lingaolu.exception.ErrResponseBodyVo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/**
* 错误异常处理控制类
*/
@RestController
@RequestMapping("/error")
public class MyBaseErrorController implements ErrorController {private static final String PATH = "/error";@Value("${spring.application.name}")private String applicationName;@Overridepublic String getErrorPath() {return PATH;}@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {ErrResponseBodyVo errResponseBody = (ErrResponseBodyVo)request.getAttribute("errResponseBody");ModelAndView  mv =new ModelAndView();if(null != errResponseBody){mv.setViewName("error/5xx");}else {errResponseBody = new ErrResponseBodyVo();errResponseBody.setSys(applicationName);errResponseBody.setUrl(request.getRequestURL().toString());// 获取状态码和异常Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");errResponseBody.setCode(statusCode);errResponseBody.setMsg(HttpStatus.valueOf(statusCode).getReasonPhrase());mv.setViewName("error/404");}mv.addObject("errResponseBody",errResponseBody);return mv;}@RequestMappingpublic ErrResponseBodyVo error(HttpServletRequest request, HttpServletResponse response) {ErrResponseBodyVo errResponseBody = (ErrResponseBodyVo)request.getAttribute("errResponseBody");if(null != errResponseBody){return errResponseBody;}errResponseBody=new ErrResponseBodyVo();errResponseBody.setSys(applicationName);errResponseBody.setUrl(request.getRequestURL().toString());// 获取状态码和异常Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");errResponseBody.setCode(statusCode);errResponseBody.setMsg(HttpStatus.valueOf(statusCode).getReasonPhrase());return errResponseBody;}}

       接着就是我们的页面了,404.html和5xx.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>404错误页</title>
</head>
<body><h1>系统:[[${errResponseBody.sys}]]</h1><h1>url:[[${errResponseBody.url}]]</h1><h1>code:[[${errResponseBody.code}]]</h1><h1>消息:[[${errResponseBody.msg}]]</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>5xx异常页</title>
</head>
<body><h1>系统:[[${errResponseBody.sys}]]</h1><h1>url:[[${errResponseBody.url}]]</h1><h1>code:[[${errResponseBody.code}]]</h1><h1>消息:[[${errResponseBody.msg}]]</h1><h1>数据:[[${errResponseBody.data}]]</h1>
</body>
</html>

       启动访问结果,浏览器
在这里插入图片描述
在这里插入图片描述
       启动访问结果,postman
在这里插入图片描述
在这里插入图片描述

5-8:注册三大组件Servlet、Filter、Listener

       SpringBoot提供了3个类来分别注册Servlet、Filter、Listener

  • ServletRegistrationBean
  • FilterRegistrationBean
  • ServletListenerRegistrationBean

       别废话,直接写,我们自定义的Servlet

package com.lingaolu.servlet;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class MyServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws >ServletException, IOException {this.doPost(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws >ServletException, IOException {resp.getWriter().write("MyServlet");}
}

       我们自定义的Filter

package com.lingaolu.fiiter;import javax.servlet.*;
import java.io.IOException;public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse >servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("MyFilter......");filterChain.doFilter(servletRequest,servletResponse);}@Overridepublic void destroy() {}
}

       我们自定义的Listener

package com.lingaolu.listener;import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;public class MyListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {System.out.println("初始...");}@Overridepublic void contextDestroyed(ServletContextEvent sce) {System.out.println("销毁...");}
}

       注册三大组件

package com.lingaolu.config;import com.lingaolu.fiiter.MyFilter;
import com.lingaolu.listener.MyListener;
import com.lingaolu.servlet.MyServlet;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;@Configuration
public class MyServerConfig {// 注册servlet@Beanpublic ServletRegistrationBean myServlet(){ServletRegistrationBean<MyServlet> registrationBean = new >ServletRegistrationBean<>();registrationBean.setServlet(new MyServlet());// 设置映射的urlregistrationBean.addUrlMappings("/myServlet");return registrationBean;}// 注册filter@Beanpublic FilterRegistrationBean myFilter(){FilterRegistrationBean<MyFilter> registrationBean =new FilterRegistrationBean<>();registrationBean.setFilter(new MyFilter());// 设置拦截的请求registrationBean.setUrlPatterns(Arrays.asList("/myServlet","/hello"));return registrationBean;}// 注册listener@Beanpublic ServletListenerRegistrationBean myListener(){ServletListenerRegistrationBean<MyListener> registrationBean = new >ServletListenerRegistrationBean<>();registrationBean.setListener(new MyListener());return  registrationBean;}
}

6:SpingBoot整合数据访问

       我们的数据最终要保存在数据库上,所以要整合数据库访问,很简单,mavan导包


<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency>

       添加配置

spring:datasource:username: rootpassword: passwordurl: jdbc:mysql://localhost:3306/study?userSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghaidriver-class-name: com.mysql.cj.jdbc.Driver

6-1:jdbc

       jdbc简单,直接使用容器中的模板JdbcTemplate

package com.lingaolu.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;@RestController
public class JdbcController {// 注入模板@AutowiredJdbcTemplate jdbcTemplate;// 查询@GetMapping(value = "/account/accounts")public List<Map<String,Object>> accountList(){String sql = "select * from account";return jdbcTemplate.queryForList(sql);}// 更新@PatchMapping(value = "/account/accounts/{id}")public long apdateAccount(@PathVariable("id") Long id ){String sql = "update account set name=?,age=? where id="+id;Object[] objects = new Object[2];objects[0] = "张六";objects[1] = 27;return jdbcTemplate.update(sql,objects);}}

6-2:使用Druid数据源

       SpringBoot2.x默认使用的数据源是HikariDataSource
在这里插入图片描述
       我们可以切换数据源,使用Druid数据源。因为它可以监测很多东西,通过以下配置来指定。

spring.datasource.type

       切换Druid数据源需要maven导包

<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.13</version>
</dependency>

       然后配置文件制定数据源

spring:datasource:username: rootpassword: passwordurl: jdbc:mysql://127.0.0.1:3306/study?userSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghaidriver-class-name: com.mysql.cj.jdbc.Driver# 指定数据源type: com.alibaba.druid.pool.DruidDataSource

       数据源切换为Druid了
在这里插入图片描述

       Druid还有自己的配置

spring:datasource:username: rootpassword: passwordurl: jdbc:mysql://127.0.0.1:3306/study?userSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghaidriver-class-name: com.mysql.cj.jdbc.Driver# 指定数据源type: com.alibaba.druid.pool.DruidDataSource#SpringBoot默认是不注入这些的,需要自己绑定,开始#druid数据源专有配置initialSize: 5minIdle: 5maxActive: 20maxWait: 60000timeBetweenEvictionRunsMillis: 60000minEvictableIdleTimeMillis: 300000validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsepoolPreparedStatements: true#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入#如果允许报错,java.lang.ClassNotFoundException: org.apache.Log4j.Properity#则导入log4j 依赖就行filters: stat,wall,log4jmaxPoolPreparedStatementPerConnectionSize: 20useGlobalDataSourceStat: trueconnectionoProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500#SpringBoot默认是不注入这些的,需要自己绑定,结束

       注释说到这些配置是druid数据源专有配置,SpringBoot默认是不注入这些的,所以我们还得对这些配置注入到druid,配置里用到log4j。所以我们要先maven导入log4j

<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency>

       自定义配置类配置druid

package com.lingaolu.config;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;@Configuration
public class DruidConfig {// 配置文件注入Druid数据源DruidDataSource@ConfigurationProperties(prefix = "spring.datasource")@Beanpublic DataSource druidDataSource(){return new DruidDataSource();}// 后台监控@Beanpublic ServletRegistrationBean statViewServlet(){ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>();registrationBean.setServlet(new StatViewServlet());// 设置请求,这就是我们要访问druid后台的请求registrationBean.addUrlMappings("/druid/*");// 后台需要有人登陆,账号密码设置Map<String,String> initParams = new HashMap<>();// 增加配置initParams.put("loginUsername","admin");initParams.put("loginPassword","admin");// 允许谁可以访问//initParams.put("allow","localhost");// 默认所有人都可以访问initParams.put("allow","");// 拒绝谁访问initParams.put("deny","192.168.2.25");// 初始化设置参数registrationBean.setInitParameters(initParams);return registrationBean;}@Beanpublic FilterRegistrationBean webStatFilter(){FilterRegistrationBean<WebStatFilter> registrationBean = new FilterRegistrationBean<>();registrationBean.setFilter(new WebStatFilter());Map<String,String> initParams = new HashMap<>();// 这些请求不拦截,也就是不统计这些initParams.put("exclusions","*.js,*.css,/druid/*");registrationBean.setInitParameters(initParams);// 拦截所有请求来统计registrationBean.setUrlPatterns(Arrays.asList("/*"));return registrationBean;}
}

       参数的初始化比如账号密码等中,map的key可以参照本类StatViewServletWebStatFilter或者其父类
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

       上面我们配置了StatViewServlet的路径为/druid/*,所谓我们启动项目,访问http://localhost:8080/druid,访问druid数据源的监控后台
在这里插入图片描述
       用我们初始化参数的账号admin和密码admin登录
在这里插入图片描述
       可见Druid数据源有多强大

6-3:整合mybatis

       要整合mybatis,需要maven导入mybatis和SpringBoot的整合包

<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.1</version>
</dependency>

       为何方便,我还导入了lombok

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>

       来一个我们的实体类Job

package com.lingaolu.bean;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
public class Job {private long id;private String jobName;private String description;
}

6-3-1:注解版

       直接写mapper文件,使用注解sql

package com.lingaolu.dao;import com.lingaolu.bean.Job;
import org.apache.ibatis.annotations.*;
import java.util.List;// 此注解表示这是一个mapper
@Mapper
public interface JobMapper {@Select("select * from job")List<Job> jobList();@Delete("delete from job where id=#{id}")Long deleteJobById(Long id);// @Options(useGeneratedKeys = true,keyProperty = "id")表示插入后id返回给实体类@Options(useGeneratedKeys = true,keyProperty = "id")@Insert("insert into job(job_name,description) values(#{jobName},#{description})")Long insertJob(Job job);@Update("update job set job_name=#{jobName} where id=#{id}")Long updateJob(Job job);}

       每个mapper文件加上注解@Mapper表示这是一个Mapper,也可以在启动类的主配置上加上扫描Mapper文件的注解,这样就不需要在每个mapper文件加上@Mapper注解了

@MapperScan(value = "mapper的包路径")

在这里插入图片描述

       接口控制器层JdbcController

package com.lingaolu.controller;import com.lingaolu.bean.Job;
import com.lingaolu.dao.JobMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;@RestController
public class JobController {@AutowiredJobMapper jobMapper;@GetMapping(value = "/job/jobs")public List<Job> jobList(){return jobMapper.jobList();}@DeleteMapping(value = "/job/jobs/{id}")Long deleteJobById(@PathVariable("id") Long id){return jobMapper.deleteJobById(id);}@PostMapping(value = "/job/jobs")Job insertJob(Job job){jobMapper.insertJob(job);return job;}@PatchMapping(value = "/job/jobs/{id}")Long updateJob(Job job){return jobMapper.updateJob(job);}
}

       启动访问测试
在这里插入图片描述

       因为我们的数据库的字段是job_name,而我们的属性是jobName,所以这需要开启驼峰规则

package com.lingaolu.config;import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MyBatisConfig {@Beanpublic ConfigurationCustomizer configurationCustomizer(){/* return new ConfigurationCustomizer(){@Overridepublic void customize(Configuration configuration){// 开启驼峰规则configuration.setMapUnderscoreToCamelCase(true);}};*/// 使用lamda表达式return configuration-> configuration.setMapUnderscoreToCamelCase(true);}
}

       再次启动访问
在这里插入图片描述

6-3-1:配置版

       在资源路径下添加我们的mybatis的配置文件和mapper文件,并且在全局配置文件中配置mybatis的相关路径配置
在这里插入图片描述
       mybatis-config.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 其余关于mybatis的配置都可以在这里配置 --><settings><!-- 设置驼峰 --><setting name="mapUnderscoreToCamelCase" value="true"/></settings><!-- 别名配置,包下 --><typeAliases><package name="com.lingaolu.bean"/></typeAliases>
</configuration>

       JobMapper.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lingaolu.dao.JobMapper"><sql id="Base_Column_List" >id, job_name, description</sql><sql id="QueryCondition"><where><if test="jobName != null and jobName!=''" >and LOCATE(#{jobName},job_name) > 0</if></where></sql><select id="jobList" parameterType="map" resultType="Job">SELECT<include refid="Base_Column_List" />FROMjob<include refid="QueryCondition" /></select><insert id="insertJob" parameterType="Job" useGeneratedKeys="true"keyColumn="id" keyProperty="id">insert into job<trim prefix="(" suffix=")" suffixOverrides="," ><if test="id != null" >id,</if><if test="jobName != null" >job_name,</if><if test="description != null" >description,</if></trim><trim prefix="values (" suffix=")" suffixOverrides="," ><if test="id != null" >#{id},</if><if test="jobName != null" >#{jobName},</if><if test="description != null" >#{description},</if></trim></insert><update id="updateJob" parameterType="Job">update job<set ><if test="jobName != null" >job_name = #{jobName},</if><if test="description != null" >description = #{description},</if></set>whereid = #{id}</update><delete id="deleteJobById" parameterType="long">delete from job where id = #{id}</delete>
</mapper>

       接口文件

package com.lingaolu.dao;import com.lingaolu.bean.Job;
import java.util.List;// 已经在入口添加扫描@MapperScan(value = "com.lingaolu.dao"),所以这里不再需要@Mapper
public interface JobMapper {List<Job> jobList();Long deleteJobById(Long id);Long insertJob(Job job);Long updateJob(Job job);}

       接口控制层

package com.lingaolu.controller;import com.lingaolu.bean.Job;
import com.lingaolu.dao.JobMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;@RestController
public class JobController {@AutowiredJobMapper jobMapper;@GetMapping(value = "/job/jobs")public List<Job> jobList(){return jobMapper.jobList();}@DeleteMapping(value = "/job/jobs/{id}")Long deleteJobById(@PathVariable("id") Long id){return jobMapper.deleteJobById(id);}@PostMapping(value = "/job/jobs")Job insertJob(Job job){jobMapper.insertJob(job);return job;}@PatchMapping(value = "/job/jobs/{id}")Long updateJob(Job job){return jobMapper.updateJob(job);}
}

6-4:整合JPA

       整合JPA很简单,只需4步

       第一步导入依赖

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

       如果需要还可以导lombok的包

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>

       第二步配置JPA

spring:# JPA配置jpa:# 更新或者创建数据表结构hibernate:ddl-auto: update# 控制台显示SQLshow-sql: true# 数据库配置datasource:username: rootpassword: passwordurl: jdbc:mysql://127.0.0.1:3306/study?>userSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghaidriver-class-name: com.mysql.cj.jdbc.Driver
``

       第三步书写javaBean,通过配置与数据库表建立关联关系

package com.lingaolu.bean;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;@Data
@AllArgsConstructor
@NoArgsConstructor
// 使用JPA注解配置映射关系
@Entity //告诉JPA这是一个实体类(和数据库表映射的类)
@Table(name = "dept")   // 用@Table来指定和哪个数据库表对应,如果省略默认表明小>写,就是deptment
public class Deptment {@Id // 这是一个主键@GeneratedValue(strategy = GenerationType.IDENTITY) // 自增主键private Long id;@Column(name = "name",length = 50)  // 这是和数据库表对应的一列private String name;@Column()  // 默认列名就是属性名private String loc;@Column(name = "status")  // 这是和数据库表对应的一列private Integer status;
}

       第四步写接口继承JpaRepository,只需要继承即可

package com.lingaolu.repository;import com.lingaolu.bean.Deptment;
import org.springframework.data.jpa.repository.JpaRepository;// 写一个揭接口继承JpaRepository,泛型里第一个参数是与数据表映射的实体类,第二>个参数是实体类主键的类型
public interface DeptRepository extends JpaRepository<Deptment,Long> {}

       这样JPA就整合完了,接着就是自己的控制层接口

package com.lingaolu.controller;import com.lingaolu.bean.Deptment;
import com.lingaolu.repository.DeptRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Optional;@RestController
public class DeptController {@AutowiredDeptRepository deptRepository;@GetMapping(value = "dept/depts")List<Deptment> deptList(){return deptRepository.findAll();}@GetMapping(value = "dept/{id}")Optional deptById(@PathVariable("id") Long id){Optional<Deptment> optional=  deptRepository.findById(id);return optional;}
}

7:SpringBoot任务

7-1:异步任务

       先看一个同步例子

       来一个方法线程睡4秒

package com.lingaolu.service;import org.springframework.stereotype.Service;@Service
public class AsyncService {public void test(){try {Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}}
}

       来一个控制层接口,调用上面的方法

package com.lingaolu.controller;import com.lingaolu.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class HelloController {@AutowiredAsyncService asyncService;@GetMapping("/hello")public String hello(){long startTime = System.currentTimeMillis();asyncService.test();return "耗时"+(System.currentTimeMillis()-startTime);}}

       启动访问接口,可见耗时算上了睡眠的4秒
在这里插入图片描述

       我们不想等待那么长时间,而是先返回结果,这需要异步

       可以使用@Async注解在方法上,说明这个是一个异步方法

package com.lingaolu.service;import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;@Service
public class AsyncService {@Asyncpublic void test(){try {Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();}}
}

       然后在启动类加上注解@EnableAsync,表示开启异步功能
在这里插入图片描述
       启动访问,可见注解了异步的方法进行了异步任务
在这里插入图片描述

7-2:邮件任务

       maven导包

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

       yml添加配置

spring:mail:# QQ账号username: 782519197@qq.compassword: 这里写你的授权码,而不是QQ密码host: smtp.qq.com# QQ邮箱需要配这个properties:main:smtl:ssl:enable: true

       授权码可以从这里获取
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

       控制层代码

package com.lingaolu.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;@RestController
public class HelloController {@AutowiredJavaMailSenderImpl mailSender;@GetMapping("/send")public String send(){// 简单邮件SimpleMailMessage mailMessage = new SimpleMailMessage();// 标题mailMessage.setSubject("python爬虫到入狱");// 正文mailMessage.setText("欢迎来到我的python爬虫系列课程");// 寄给谁mailMessage.setTo("782519197@qq.com");// 谁寄mailMessage.setFrom("782519197@qq.com");mailSender.send(mailMessage);return "发送成功";}@GetMapping("/send2")public String send2() throws MessagingException {// 复杂邮件MimeMessage mimeMessage = mailSender.createMimeMessage();// 组装MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);// 正文helper.setSubject("一个努力的博主");helper.setText("这是一个很优秀努力的博主,欢迎来到他的主页https://lingaolu.blog.csdn.net/");// 附件helper.addAttachment("b.jpg",new File("C:\\Users\\Administrator\\Desktop\\a.jpg"));helper.addAttachment("国富论.txt",new File("C:\\Users\\Administrator\\Desktop\\《国富论》全本.txt"));// 寄给谁helper.setTo("782519197@qq.com");// 谁寄helper.setFrom("782519197@qq.com");mailSender.send(mimeMessage);return "发送成功";}}

7-3:定时任务

       定时任务就2个东西

  • TaskScheduler:任务调度者
  • TaskExecutor:任务执行者

       实现方式

  • @EnableScheduling:开启定时功能的注解,在启动类加上
  • @Scheduled():什么时候执行,在要执行的方法上加

       执行方法例子

package com.lingaolu.service;import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;@Service
public class ScheduledService {、、秒@Scheduled(cron="0/2 * * * * ?")    // cron表达式public void hello(){System.out.println("你被执行了!");}
}

在这里插入图片描述
       主要就是cron表达式,可以参考这个https://www.bejson.com/othertools/cronvalidate/

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/100643.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息