JavaWeb学习 | 后端部分
Maven
概述
介绍:Maven是apache旗下的一个开源项目,是一款用于管理和构建java项目的工具,标准跨平台(Linux、Windows、MacOS)的自动化项目构建方式。
- 仓库:用于存储资源,管理各种jar包。
- 本地仓库:自己计算机上的一个目录。
- 中央仓库:由Maven团队维护的全球唯一的。 仓库地址:https://repo1.maven.org/maven2/
- 远程仓库(私服):一般由公司团队搭建的私有仓库。
- 仓库:用于存储资源,管理各种jar包。
作用:
- 依赖管理:方便快捷的管理项目依赖的资源(jar包),避免版本冲突问题
- 统一项目结构:提供标准、统一的项目结构
- 项目构建:标准跨平台(Linux、Windows、MacOS)的自动化项目构建方式
安装
解压 apache-maven-3.6.1-bin.zip。
配置本地仓库:修改 conf/settings.xml 中的
为一个指定目录。 1
<localRepository>E:\develop\apache-maven-3.6.1\mvn_repo</localRepository>
配置阿里云私服:修改 conf/settings.xml 中的
标签,为其添加如下子标签: 1
2
3
4
5
6<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>配置环境变量: MAVEN_HOME 为maven的解压目录,并将其bin目录加入PATH环境变量。
测试:cmd中输入以下代码查看是否安装成功
1
mvn -v
IDE集成Maven
配置Maven环境
选择 IDEA中 File –> Settings –> Build,Execution,Deployment –> Build Tools –> Maven
设置 IDEA 使用本地安装的 Maven,并修改配置文件及本地仓库路径
创建Maven项目
创建模块,选择Maven,点击Next
填写模块名称,坐标信息,点击finish,创建完成
编写 HelloWorld,并运行
Maven坐标
什么是坐标?
Maven 中的坐标是资源的唯一标识,通过该坐标可以唯一定位资源位置。
使用坐标来定义项目或引入项目中需要的依赖。
Maven 坐标主要组成
groupId:定义当前Maven项目隶属组织名称(通常是域名反写,例如:com.itheima)
artifactId:定义当前Maven项目名称(通常是模块名称,例如 order-service、goods-service)
version:定义当前项目版本号
1
2
3
4
5<dependency>
<groupId>com.itheima</groupId>
<artifactId>maven-project01</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
导入Maven项目
略略略
依赖管理
依赖配置
依赖:指当前项目运行所需要的jar包,一个项目中可以引入多个依赖。
配置:
在 pom.xml 中编写
<dependencies>
标签在
<dependencies>
标签中 使用<dependency>
引入坐标定义坐标的 groupId,artifactId,version
点击刷新按钮,引入最新加入的坐标
1
2
3
4
5
6
7<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
注意:
如果引入的依赖,在本地仓库不存在,将会连接远程仓库/中央仓库,然后下载依赖。(这个过程会比较耗时,耐心等待)
如果不知道依赖的坐标信息,可以到https://mvnrepository.com/中搜索。
依赖传递
依赖具有传递性
- 直接依赖:在当前项目中通过依赖配置建立的依赖关系
- 间接依赖:被依赖的资源如果依赖其他资源,当前项目间接依赖其他资源
排除依赖:排除依赖指主动断开依赖的资源,被排除的资源无需指定版本。
1
2
3
4
5
6
7
8
9
10
11<dependency>
<groupId>com.itheima</groupId>
<artifactId>maven-projectB</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
依赖范围
依赖的jar包,默认情况下,可以在任何地方使用。可以通过 <scope> … </ scope >
设置其作用范围。
作用范围:
主程序范围有效。(main文件夹范围内)
测试程序范围有效。(test文件夹范围内)
是否参与打包运行。(package指令范围内)
1 | <dependency> |
scope值 | 主程序 | 测试程序 | 打包(运行) | 范例 |
---|---|---|---|---|
compile(默认) | Y | Y | Y | log4j |
test | - | Y | - | junit |
provided | Y | Y | - | servlet-api |
runtime | - | Y | Y | jdbc驱动 |
生命周期
Maven的生命周期就是为了对所有的maven项目构建过程进行抽象和统一。
Maven中有3套相互独立的生命周期:每套生命周期包含一些阶段(phase),阶段是有顺序的,后面的阶段依赖于前面的阶段。
clean:清理工作。
- pre-clean
- clean: 移除上一次构建生成的文件
- post-clean
default:核心工作,如:编译、测试、打包、安装、部署等。
- validate
- initialize
- generate-sources
- process-sources
- generate-resources
- process-resources
- compile: 编译项目源代码
- process-classes
- generate-test-sources
- process-test-sources
- generate-test-resources
- process-test-resources
- test-compile
- process-test-classes
- test: 使用合适的单元测试框架运行测试(junit)
- prepare-package
- package: 将编译后的文件打包,如:jar、war等
- verify
- install: 安装项目到本地仓库
- deploy
site:生成报告、发布站点等。
- pre-site
- site
- post-site
- site-deploy
注意:在同一套生命周期中,当运行后面的阶段时,前面的阶段都会运行。
执行指定生命周期的两种方式:
在idea中,右侧的maven工具栏,选中对应的生命周期,双击执行。
在命令行中,通过命令执行。
Maven高级
分模块设计与开发
为什么? 将项目按照功能拆分成若干个子模块,方便项目的管理维护、扩展,也方便模块间的相互调用,资源共享。
步骤:
- 创建maven模块 tlias-pojo,存放实体类。
- 创建maven模块 tlias-utils,存放相关工具类。
注意事项:分模块开发需要先针对模块功能进行设计,再进行编码。不会先将工程开发完毕,然后进行拆分。
继承与聚合
继承
继承关系
- 概念:继承描述的是两个工程间的关系,与java中的继承相似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承。
- 作用:简化依赖配置、统一管理依赖
- 实现:<parent> … </parent>
步骤:
- 创建maven模块 tlias-parent ,该工程为父工程,设置打包方式pom(默认jar)。
- 在子工程的pom.xml文件中,配置继承关系。
注意事项:
- 在子工程中,配置了继承关系之后,坐标中的groupId是可以省略的,因为会自动继承父工程的 。
- relativePath指定父工程的pom文件的相对位置(如果不指定,将从本地仓库/远程仓库查找该工程)。
- 在父工程中配置各个工程共有的依赖(子工程会自动继承父工程的依赖)。
注意事项:若父子工程都配置了同一个依赖的不同版本,以子工程的为准。
jar:普通模块打包,springboot项目基本都是jar包(内嵌tomcat运行)
war:普通web程序打包,需要部署在外部的tomcat服务器中运行
pom:父工程或聚合工程,该模块不写代码,仅进行依赖管理
版本锁定
- 在maven中,可以在父工程的pom文件中通过 <dependencyManagement> 来统一管理依赖版本。
注意事项:子工程引入依赖时,无需指定 <version> 版本号,父工程统一管理。变更依赖版本,只需在父工程中统一变更。
- 自定义属性/引用属性
<dependencyManagement> 与 <dependencies>的区别是什么?
- <dependencies> 是直接依赖,在父工程配置了依赖,子工程会直接继承下来。
- <dependencyManagement> 是统一管理依赖版本,不会直接依赖,还需要在子工程中引入所需依赖(无需指定版本)
聚合
聚合:将多个模块组织成一个整体,同时进行项目的构建。
聚合工程:一个不具有业务功能的“空”工程(有且仅有一个pom文件)
作用:快速构建项目(无需根据依赖关系手动构建,直接在聚合工程上构建即
maven中可以通过 <modules> 设置当前聚合工程所包含的子模块名称
注意事项:聚合工程中所包含的模块,在构建时,会自动根据模块间的依赖关系设置构建顺序,与聚合工程中模块的配置书写位置无关。
继承与聚合
- 作用
- 聚合用于快速构建项目
- 继承用于简化依赖配置、统一管理依赖
- 相同点:
- 聚合与继承的pom.xml文件打包方式均为pom,可以将两种关系制作到同一个pom文件中
- 聚合与继承均属于设计型模块,并无实际的模块内容
- 不同点:
- 聚合是在聚合工程中配置关系,聚合可以感知到参与聚合的模块有哪些
- 继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己
私服
私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,用来代理位于外部的中央仓库,用于解决团队内部的资源共享与资源同步问题。
依赖查找顺序:
- 本地仓库
- 私服
- 中央仓库
资源上传与下载
项目版本:
RELEASE(发行版本):功能趋于稳定、当前更新停止,可以用于发行的版本,存储在私服中的RELEASE仓库中。
SNAPSHOT(快照版本):功能不稳定、尚处于开发中的版本,即快照版本,存储在私服的SNAPSHOT仓库中。
- 设置私服的访问用户名/密码(settings.xml中的servers中配置)
1 | <server> |
- IDEA的maven工程的pom文件中配置上传(发布)地址
1 | <distributionManagement> |
- 设置私服依赖下载的仓库组地址(settings.xml中的mirrors、profiles中配置)
1 | <mirror> |
1 | <profile> |
SpringBoot Web 基础篇
- 官网:spring.io
- Spring发展到今天已经形成了一种开发生态圈,Spring提供了若干个子项目,每个项目用于完成特定的功能。
- Spring Boot 可以帮助我们非常快速的构建应用程序、简化开发、提高效率 。
SpringBootWeb入门
- 需求:使用 SpringBoot 开发一个web应用,浏览器发起请求 /hello后,给浏览器返回字符串 “Hello World ~”。
步骤
创建springboot工程,填写模块信息,并勾选web开发相关依赖。
定义HelloController类,添加方法 hello,并添加注解。
运行启动类,打开浏览器测试。
HTTP协议
概述
- 概念:Hyper Text Transfer Protocol,超文本传输协议,规定了浏览器和服务器之间数据传输的规则。
- 特点:
- 基于TCP协议:面向连接,安全
- 基于请求-响应模型的:一次请求对应一次响应
- HTTP协议是无状态的协议:对于事务处理没有记忆能力。每次请求-响应都是独立的。
- 缺点:多次请求间不能共享数据。
- 优点:速度快
请求协议
Host | 请求的主机名 |
---|---|
User-Agent | 浏览器版本,例如Chrome浏览器的标识类似Mozilla/5.0 … Chrome/79,IE浏览器的标识类似Mozilla/5.0 (Windows NT …) like Gecko |
Accept | 表示浏览器能接收的资源类型,如text/*,image/或者/*表示所有; |
Accept-Language | 表示浏览器偏好的语言,服务器可以据此返回不同语言的网页; |
Accept-Encoding | 表示浏览器可以支持的压缩类型,例如gzip, deflate等。 |
Content-Type | 请求主体的数据类型。 |
Content-Length | 请求主体的大小(单位:字节)。 |
注意:
请求方式-GET:请求参数在请求行中,没有请求体,如:**/brand/findAll?name=OPPO&status=1**。GET请求大小是有限制的。
请求方式-POST:请求参数在请求体中,POST请求大小是没有限制的。
响应协议
状态码分类 | 说明 |
---|---|
1xx | 响应中-临时状态码,表示请求已经接收,告诉客户端应该继续请求或者如果它已经完成则忽略它。 |
2xx | 成功-表示请求已经被成功接收,处理已完成。 |
3xx | 重定向-重定向到其他地方;让客户端再发起一次请求以完成整个处理。 |
4xx | 客户端错误-处理发生错误,责任在客户端。如: 请求了不存在的资源、客户端未被授权、禁止访问等。 |
5xx | 服务器错误-处理发生错误,责任在服务端。如:程序抛出异常等。 |
响应头 | 说明 |
---|---|
Content-Type | 表示该响应内容的类型,例如text/html,application/json。 |
Content-Length | 表示该响应内容的长度(字节数)。 |
Content-Encoding | 表示该响应压缩算法,例如gzip。 |
Cache-Control | 指示客户端应如何缓存,例如max-age=300表示可以最多缓存300秒。 |
Set-Cookie | 告诉浏览器为当前页面所在的域设置cookie。 |
状态码 | 解释 |
---|---|
200 | 客户端请求成功。 |
404 | 请求资源不存在,般是URL输入有误,或者网站资源被删除了。 |
500 | 服务器发生不可预期的错误。 |
状态码大全:https://cloud.tencent.com/developer/chapter/13553
- 协议解析
Web服务器-Tomcat
- Web服务器是一个软件程序,对HTTP协议的操作进行封装,使得程序员不必直接对协议进行操作,让Web开发更加便捷。主要功能是 “提供网上信息浏览服务” 。
Tomcat简介
- 概念: Tomcat是Apache 软件基金会一个核心项目,是一个开源免费的轻量级Web服务器,支持Servlet/JSP少量JavaEE规范。
- JavaEE:Java Enterprise Edition,Java企业版。指 Java 企业级开发的技术规范总和。包含13项技术规范:JDBC、JNDI、EJB、RMI、JSP、Servlet、XML、JMS、Java IDL、JTS、JTA、JavaMail、JAF
- Tomcat 也被称为 Web容器、Servlet容器。Servlet程序需要依赖于 Tomcat 才能运行
基本使用
下载:官网下载,地址 https://tomcat.apache.org/download-90.cgi
安装:绿色版,直接解压即可
卸载:直接删除目录即可
启动:双击:bin\startup.bat
控制台中文乱码:修改conf/ logging.properties
关闭:
- 直接×掉运行窗口:强制关闭
- bin\shutdown.bat:正常关闭
- Ctrl+C:正常关闭
常见问题:
启动窗口一闪而过:检查JAVA_HOME环境变量是否正确配置
端口号冲突:找到对应程序,将其关闭掉
- 配置Tomcat端口号(conf/server.xml)
注意:HTTP协议默认端口号为80,如果将Tomcat端口号改为80,则将来访问Tomcat时,将不用输入端口号 。
- Tomcat 部署项目:将项目放置到 webapps 目录下, 即部署完成
入门程序解析
起步依赖:
- spring-boot-starter-web:包含了web应用开发所需要的常见依赖。
- spring-boot-starter-test:包含了单元测试所需要的常见依赖。
- 官方提供的starter:https://docs.spring.io/spring-boot/docs/2.7.4/reference/htmlsingle/#using.build-systems.starters
请求响应
- 请求(HttpServletRequest):获取请求数据
- 响应(HttpServletResponse):设置响应数据
- BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。(维护方便 体验一般)
- CS架构:Client/Server,客户端/服务器架构模式。(开发、维护麻烦 体验不错)
请求
Postman
Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。
作用:常用于进行接口测试
简单参数
原始方式:在原始的web程序中,获取请求参数,需要通过HttpServletRequest 对象手动获取。
SpringBoot方式:参数名与形参变量名相同,定义形参即可接收参数。如果方法形参名称与请求参数名称不匹配,可以使用 @RequestParam 完成映射。
注意:@RequestParam中的required属性默认为true,代表该请求参数必须传递,如果不传递将报错。 如果该参数是可选的,可以将required属性设置为false。
- post传递
实体参数
简单实体对象:请求参数名与形参对象属性名相同,定义POJO接收即可。
复杂实体对象:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数。
数组参数
请求参数名与形参数组名称相同且请求参数为多个,定义数组类型形参即可接收参数
集合参数
请求参数名与形参集合名称相同且请求参数为多个,@RequestParam 绑定参数关系
日期参数
使用 @DateTimeFormat 注解完成日期参数格式转换
JSON参数
JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接收参数,需要使用 @RequestBody 标识
路径参数
通过请求URL直接传递参数,使用{…}来标识该路径参数,需要使用 @PathVariable 获取路径参数
响应
响应数据:字符串、对象、集合
ResponseBody
- 类型:方法注解、类注解
- 位置:Controller方法上/类上
- 作用:将方法返回值直接响应,如果返回值类型是 实体对象/集合 ,将会转换为JSON格式响应
- 说明:@RestController = @Controller + @ResponseBody ;
统一响应结果:Result(code, msg, data)
分层解耦
三层架构
- controller:控制层,接收前端发送的请求,对请求进行处理,并响应数据。
- service:业务逻辑层,处理具体的业务逻辑。
- dao:数据访问层(Data Access Object)(持久层),负责数据访问操作,包括数据的增、删、改、查。
分层解耦
内聚:软件中各个功能模块内部的功能联系。
耦合:衡量软件中各个层/模块之间的依赖、关联的程度。
软件设计原则:高内聚低耦合。
分层思想
控制反转: Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。
依赖注入: Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。
Bean对象:IOC容器中创建、管理的对象,称之为bean。
IOC & DI入门
步骤:
Service层 及 Dao层的实现类,交给IOC容器管理。(加上
@Component
)为Controller及Service注入运行时,依赖的对象。(加上
@Autowired
)运行测试。
IOC详解
Bean的声明
要把某个对象交给IOC容器管理,需要在对应的类上加上如下注解之一:
注解 | 说明 | 位置 |
---|---|---|
@Component | 声明bean的基础注解 | 不属于以下三类时,用此注解 |
@Controller | @Component的衍生注解 | 标注在控制器类上 |
@Service | @Component的衍生注解 | 标注在业务类上 |
@Repository | @Component的衍生注解 | 标注在数据访问类上(由于与mybatis整合,用的少) |
注意:
- 声明bean的时候,可以通过value属性指定bean的名字,如果没有指定,默认为类名首字母小写。
- 使用以上四个注解都可以声明bean,但是在springboot集成web开发中,声明控制器bean只能用@Controller。
Bean组件扫描
前面声明bean的四大注解,要想生效,还需要被组件扫描注解@ComponentScan扫描。
@ComponentScan注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解 @SpringBootApplication 中,默认扫描的范围是启动类所在包及其子包。
DI详解
Bean注入
- @Autowired注解,默认是按照类型进行,如果存在多个相同类型的bean,将会报出如下错误:
- 通过以下几种方案来解决:
- @Primary
- @Qualifier
- @Resource
@Resource 与 @Autowired区别
@Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解。
@Autowired 默认是按照类型注入,而@Resource默认是按照名称注入
Mybatis
MyBatis是一款优秀的持久层框架,用于简化JDBC的开发
MyBatis本是 Apache的-个开源项目iBatis,2010年这项目apache迁移到了oole cde,并且改名为MvBatis2013年11月迁移到Github年11月迁移到Github
Mybatis入门
快速入门
需求:使用Mybatis查询所有用户数据
步骤:
- 准备工作(创建springboot工程、数据库表user、实体类User)
- 引入Mybatis的相关依赖,配置Mybatis(数据库连接信息)
- 编写SQL语句(注解/XML)
- 单元测试
JDBC介绍
JDBC: ( Java DataBase Connectivity ),就是使用Java语言操作关系型数据库的一套API。
数据库连接池
介绍:
- 数据库连接池是个容器,负责分配、管理数据库连接(Connection)
- 它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
- 释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏
优势:
- 资源重用
- 提升系统响应速度
- 避免数据库连接遗漏
标准接口:DataSource
- 官方(sun)提供的数据库连接池接口,由第三方组织实现此接口。
- 功能:获取连接
常见产品:
Druid(德鲁伊)
- Druid连接池是阿里巴巴开源的数据库连接池项目
- 功能强大,性能优秀,是 Java 语言最好的数据库连接池之一
切换Druid数据库连接池
官方地址:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
详见:JDBC学习 | Threewood (threewood1.top) 中的使用优化部分
lombok
Lombok是一个实用的Java类库,能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,并可以自动化生成日志变量,简化java开发、提高效率。
依赖
1
2
3
4<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
注解 | 作用 |
---|---|
@Getter/@Setter | 为所有的属性提供get/set方法 |
@ToString | 会给类自动生成易阅读的 toString 方法 |
@EqualsAndHashCode | 根据类所拥有的非静态字段自动重写 equals 方法和 hashCode 方法 |
@Data | 提供了更综合的生成代码功能(@Getter + @Setter + @ToString + @EqualsAndHashCode) |
@NoArgsConstructor | 为实体类生成无参的构造器方法 |
@AllArgsConstructor | 为实体类生成除了static修饰的字段之外带有各参数的构造器方法。 |
注意:Lombok会在编译时,自动生成对应的 java 代码。我们使用 lombok 时,还需要安装一个 lombok 的插件(idea自带)。
Mybatis基础操作
准备
- 准备数据库表 emp
- 创建一个新的springboot工程,选择引入对应的起步依赖(mybatis、mysql驱动、lombok)
- application.properties中引入数据库连接信息
- 创建对应的实体类 Emp(实体类属性采用驼峰命名)
- 准备Mapper接口 EmpMapper
删除
接口方法:
1
2
public void delete(Integer id);
注意:如果mapper接口方法形参只有一个普通类型的参数,#{…} 里面的属性名可以随便写,如:#{id}、#{value}。
日志输出:可以在application.properties中,打开mybatis的日志,并指定输出到控制台。
1
2// 指定mybatis输出日志的位置,输出控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImplSQL注入:是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。
参数占位符:
- #{…}: 执行SQL时,会将#{…}替换为?,生成预编译SQL,会自动设置参数值。使用时机:参数传递,都使用#{…}
- ${…}: 拼接SQL,直接将参数拼接在SQL语句中,存在SQL注入问题。使用时机:如果对表名、列表进行动态设置时使用。
插入
接口方法:
1
2
3
public void insert(Emp emp);描述:在数据添加成功后,需要获取插入数据库数据的主键。如:添加套餐数据时,还需要维护套餐菜品关系表数据。
实现:
1
2
3
4//会自动将生成的主键值,赋值给emp对象的id属性
public void insert(Emp emp);
更新
接口方法:
1
2
public void update(Emp emp);
查询(根据ID查询)
接口方法:
1
2
public Emp getById(Integer id);
数据封装
实体类属性名和数据库表查询返回的字段名一致,mybatis会自动封装。
如果实体类属性名 和 数据库表查询返回的字段名不一致,不能自动封装。
起别名:在SQL语句中,对不一样的列名起别名,别名和实体类属性名一样。
1
2
public Emp getById(Integer id);手动结果映射:通过 @Results 及 @Result 进行手动结果映射。
1
2
3
4
5
6
public Emp getById(Integer id);开启驼峰命名:如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射。
1
2// 开启驼峰命名自动映射,即从数据库字段名 a_column 映射到Java 属性名 aColumn。
mybatis.configuration.map-underscore-to-camel-case=true
查询(条件查询)
接口方法
1
2
public List<Emp> list(String name, Shortgender , LocalDate begin , LocalDate end);
XML映射文件
使用Mybatis的注解,主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能,建议使用XML来配置映射语句。
规范:
- XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)。
- XML映射文件的namespace属性为Mapper接口全限定名一致。
- XML映射文件中sql语句的id与Mapper 接口中的方法名一致,并保持返回类型一致。
映射文件:MybatisX 是一款基于 IDEA 的快速开发Mybatis的插件,为效率而生。
Mybatis动态SQL
- 动态SQL:随着用户的输入或外部条件的变化而变化的SQL语句,我们称为 动态SQL。
- <if>: 用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL。形式:<if test=”name != null”>…</if>
- <where>: where 元素只会在子元素有内容的情况下才插入where子句。而且会自动去除子句的开头的AND 或 OR。
- <set>: 动态地在行首插入 SET 关键字,并会删掉额外的逗号。(用在update语句中)
- <foreach>: 遍历操作。属性如下:
- collection:集合名称
- item:集合遍历出来的元素/项
- separator:每一次遍历使用的分隔符
- open:遍历开始前拼接的片段
- close:遍历结束后拼接的片段
- <sql>: 定义可重用的 SQL 片段。
- <include>: 通过属性refid,指定包含的sql片段。
SpringBoot Web开发篇
SpringBootWeb案例
略略略略略。详见https://www.bilibili.com/video/BV1m84y1w7Tb?p=135
SpringBootWeb登录校验
问题分析:在未登录情况下,我们也可以直接访问部门管理、员工管理等功能。
登录校验
- 登录标记:用户登录成功之后,每一次请求中,都可以获取到该标记。
- 统一拦截:①过滤器Filter、②拦截器Interceptor
会话技术
- 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。
- 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
- 会话跟踪方案:
- 客户端会话跟踪技术:Cookie
- 服务端会话跟踪技术:Session
- 令牌技术
- 会话跟踪方案对比
JWT令牌
简介:
全称:JSON Web Token (https://jwt.io/)
定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。
组成:
- 第一部分:Header(头), 记录令牌类型、签名算法等。 例如:{“alg”:”HS256”,”type”:”JWT”}
- 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。 例如:{“id”:”1”,”username”:”Tom”}
- 第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。
Base64: 是一种基于64个可打印字符(A-Z a-z 0-9 + /)来表示二进制数据的编码方式。
场景:登陆认证
① 登录成功后,生成令牌
② 后续每个请求,都要携带JWT令牌,系统在每次请求处理之前,先校验令牌,通过后,再处理
JWT-生成
依赖:
1
2
3
4
5<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>生成 JWT:
1
2
3
4
5
6
7
8
9
10
11
12
public void genJwt(){
Map<String,Object> claims = new HashMap<>();
claims.put(“id”,1);
claims.put(“username”,“Tom”);
String jwt = Jwts.builder()
.setClaims(claims) //自定义内容(载荷)
.signWith(SignatureAlgorithm.HS256, “atxiong”) //签名算法
.setExpiration(new Date(System.currentTimeMillis() + 12*3600*1000)) //有效期
.compact();
System.out.println(jwt);
}JWT-校验
1
2
3
4
5
6
7
8
public void parseJwt(){
Claims claims = Jwts.parser()
.setSigningKey(“atxiong”) //指定签名秘钥
.parseClaimsJws(“eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjU5OTk1NTE3LCJ1c2VybmFtZSI6IlRvbSJ9.EUTfeqPkGslekdKBezcWCe7a7xbcIIwB1MXlIccTMwo”) //解析令牌
.getBody();
System.out.println(claims);
}
注意:
JWT校验时使用的签名秘钥,必须和生成JWT令牌时使用的秘钥是配套的。
如果JWT令牌解析校验时报错,则说明 JWT令牌被篡改 或 失效了,令牌非法
实现
- 令牌生成:登录成功后,生成JWT令牌,并返回给前端。
- 引入JWT令牌操作工具类。
- 登录完成后,调用工具类生成JWT令牌,并返回。
- 令牌校验:在请求到达服务端后,对令牌进行统一拦截、校验**(Filter、Interceptor)**。
- 令牌生成:登录成功后,生成JWT令牌,并返回给前端。
过滤器Filter
概念:Filter 过滤器,是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。
过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能。
过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理、敏感字符处理等。
快速入门
- 定义Filter:定义一个类,实现 Filter 接口,并重写其所有方法。
- 配置Filter:Filter类上加 @WebFilter 注解,配置拦截资源的路径。引导类上加 @ServletComponentScan 开启Servlet组件支持
详解
- 执行流程
- 拦截路径:Filter 可以根据需求,配置不同的拦截资源路径
拦截路径 urlPatterns值 含义 拦截具体路径 /login 只有访问 /login 路径时,才会被拦截 目录拦截 /emps/* 访问/emps下的所有资源,都会被拦截 拦截所有 /* 访问所有资源,都会被拦截 - 过滤器链:
- 介绍:一个web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链。
- 顺序:注解配置的Filter,优先级是按照过滤器类名(字符串)的自然排序。
登录校验Filter-流程
- 获取请求url。
- 判断请求url中是否包含login,如果包含,说明是登录操作,放行。
- 获取请求头中的令牌(token)。
- 判断令牌是否存在,如果不存在,返回错误结果(未登录)。
- 解析token,如果解析失败,返回错误结果(未登录)。
- 放行。
拦截器Interceptor
- 概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,用来动态拦截控制器方法的执行。
- 作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。
快速入门
- 定义拦截器,实现HandlerInterceptor接口,并重写其所有方法。
- 注册拦截器
详解
- 拦截路径
拦截路径 含义 举例 /* 一级路径 能匹配/depts,/emps,/login,不能匹配 /depts/1 /** 任意级路径 能匹配/depts,/depts/1,/depts/1/2 /depts/* /depts下的一级路径 能匹配/depts/1,不能匹配/depts/1/2,/depts /depts/** /depts下的任意级路径 能匹配/depts,/depts/1,/depts/1/2,不能匹配/emps/1 - 执行流程
Filter 与 Interceptor的区别
- 接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口。
- 拦截范围不同:过滤器Filter会拦截所有的资源,而Interceptor只会拦截Spring环境中的资源。
登录校验Interceptor-流程
- 获取请求url。
- 判断请求url中是否包含login,如果包含,说明是登录操作,放行。
- 获取请求头中的令牌(token)。
- 判断令牌是否存在,如果不存在,返回错误结果(未登录)。
- 解析token,如果解析失败,返回错误结果(未登录)。
- 放行。
异常处理
程序开发过程中不可避免的会遇到异常现象。出现异常,该如何处理?
- 方案一:在Controller的方法中进行try…catch处理 代码臃肿、不推荐
- 方案二:全局异常处理器 简单、优雅、推荐
全局异常处理器
1 |
|
@RestControllerAdvice = @ControllerAdvice + @ResponseBody
Spring事务管理
事务回顾
概念:事务 是一组操作的集合,它是一个不可分割的工作单位,这些操作 要么同时成功,要么同时失败。
操作:
- 开启事务(一组操作开始前,开启事务):
start transaction / begin
; - 提交事务(这组操作全部成功后,提交事务):
commit
; - 回滚事务(中间任何一个操作出现异常,回滚事务):
rollback
;
spring事务管理
注解:@Transactional
位置:业务(service)层的方法上、类上、接口上
作用:将当前方法交给spring进行事务管理,方法执行前,开启事务;成功执行完毕,提交事务;出现异常,回滚事务
1 | #开启事务管理日志 |
事务属性-回滚
rollbackFor
默认情况下,只有出现 RuntimeException 才回滚异常。rollbackFor属性用于控制出现何种异常类型,回滚事务。
事务属性-传播行为
propagation
事务传播行为:指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。
属性值 | 含义 |
---|---|
REQUIRED | 【默认值】需要事务,有则加入,无则创建新事务 |
REQUIRES_NEW | 需要新事务,无论有无,总是创建新事务 |
SUPPORTS | 支持事务,有则加入,无则在无事务状态中运行 |
NOT_SUPPORTED | 不支持事务,在无事务状态下运行,如果当前存在已有事务,则挂起当前事务 |
MANDATORY | 必须有事务,否则抛异常 |
NEVER | 必须没事务,否则抛异常 |
… | … |
- 场景
- REQUIRED :大部分情况下都是用该传播行为即可。
- REQUIRES_NEW :当我们不希望事务之间相互影响时,可以使用该传播行为。比如:下订单前需要记录日志,不论订单保存成功与否,都需要保证日志记录能够记录成功。
AOP
AOP基础
AOP概述
- AOP:Aspect Oriented Programming(面向切面编程、面向方面编程),其实就是面向特定方法编程。
- 场景:案例部分功能运行较慢,定位执行耗时较长的业务方法,此时需要统计每一个业务方法的执行耗时。
- 优势
- 实现:动态代理是面向切面编程最主流的实现。而SpringAOP是Spring框架的高级技术,旨在管理bean对象的过程中,主要通过底层的动态代理机制,对特定的方法进行编程。
AOP快速入门
导入依赖:在pom.xml中导入AOP的依赖
1
2
3
4<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>编写AOP程序:针对于特定方法根据业务需要进行编程
1
2
3
4
5
6
7
8
9
10
11
12
public class TimeAspect {
public Object recordTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long begin = System.currentTimeMillis();
Object object = proceedingJoinPoint.proceed(); //调用原始方法运行
long end = System.currentTimeMillis();
log.info(proceedingJoinPoint.getSignature()+"执行耗时: {}ms", end - begin);
return object;
}
}
AOP核心概念
- 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)
- 通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)
- 切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用
- 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)
- 目标对象:Target,通知所应用的对象
- AOP执行流程
AOP进阶
通知类型
- @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行
- @Before:前置通知,此注解标注的通知方法在目标方法前被执行
- @After :后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
- @AfterReturning : 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
- @AfterThrowing : 异常后通知,此注解标注的通知方法发生异常后执行
注意事项:
- @Around环绕通知需要自己调用 ProceedingJoinPoint.proceed() 来让原始方法执行,其他通知不需要考虑目标方法执行
- @Around环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值。
- PointCut:该注解的作用是将公共的切点表达式抽取出来,需要用到时引用该切点表达式即可。
通知顺序
当有多个切面的切入点都匹配到了目标方法,目标方法运行时,多个通知方法都会被执行。
执行顺序:
- 不同切面类中,默认按照切面类的类名字母排序:
- 目标方法前的通知方法:字母排名靠前的先执行
- 目标方法后的通知方法:字母排名靠前的后执行
- 用 @Order(数字) 加在切面类上来控制顺序
- 目标方法前的通知方法:数字小的先执行
- 目标方法后的通知方法:数字小的后执行
切入点表达式
切入点表达式:描述切入点方法的一种表达式
作用:主要用来决定项目中的哪些方法需要加入通知
常见形式:
- execution(……):主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配,语法为:
1
execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
其中带 ? 的表示可以省略的部分
- 访问修饰符:可省略(比如: public、protected)
- 包名.类名: 可省略
- throws 异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)
可以使用通配符描述切入点
- * :单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分
1
execution(* com.*.service.*.update*(*))
- .. :多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数
1
execution(* com.itheima..DeptService.*(..))
注意事项:根据业务需要,可以使用 且(&&)、或(||)、非(!) 来组合比较复杂的切入点表达式。
- 书写建议
- 所有业务方法名在命名时尽量规范,方便切入点表达式快速匹配。如:查询类方法都是 find 开头,更新类方法都是 update开头。
- 描述切入点方法通常基于接口描述,而不是直接描述实现类,增强拓展性。
- 在满足业务需要的前提下,尽量缩小切入点的匹配范围。如:包名匹配尽量不使用 ..,使用 * 匹配单个包。
- @annotation(……) :用于匹配标识有特定注解的方法。
1
连接点
- 在Spring中用 JoinPoint 抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等。
- 对于 @Around 通知,获取连接点信息只能使用 ProceedingJoinPoint
- 对于其他四种通知,获取连接点信息只能使用 JoinPoint ,它是 ProceedingJoinPoint 的父类型
AOP案例
略略略略略。详见https://www.bilibili.com/video/BV1m84y1w7Tb?p=182
SpringBoot Web进阶篇
配置优先级
- SpringBoot 中支持三种格式的配置文件的优先级为:
application.properties
>application.yml
>application.yaml
注意:虽然springboot支持多种格式配置文件,但是在项目开发时,推荐统一使用一种格式的配置 (yml是主流)。
- SpringBoot 除了支持配置文件属性配置,还支持 Java系统属性 和 命令行参数 的方式进行属性配置。
①. 执行maven打包指令package
②. 执行java指令,运行jar包
1 | java -Dserver.port=9000 -jar tlias-web-management-0.0.1-SNAPSHOT.jar --server.port=10010 |
注意:Springboot项目进行打包时,需要引入插件 spring-boot-maven-plugin (基于官网骨架创建项目,会自动添加该插件)
- 优先级(低 —> 高)
- application.yaml(忽略)
- application.yml
- application.properties
- java系统属性(-Dxxx=xxx)
- 命令行参数(–xxx=xxx)
Bean管理
获取bean
默认情况下,Spring项目启动时,会把bean都创建好放在IOC容器中,如果想要主动获取这些bean,可以通过如下方式:
- 根据name获取bean:
1
Object getBean(String name)
- 根据类型获取bean:
1
<T> T getBean(Class<T> requiredType)
- 根据name获取bean(带类型转换):
1
<T> T getBean(String name, Class<T> requiredType)
注意事项:上述所说的 【Spring项目启动时,会把其中的bean都创建好】还会受到作用域及延迟初始化影响,这里主要针对于 默认的单例非延迟加载的bean而言。
bean作用域
- Spring支持五种作用域,后三种在web环境才生效:
作用域 | 说明 |
---|---|
singleton | 容器内同 名称 的 bean 只有一个实例(单例)(默认) |
prototype | 每次使用该 bean 时会创建新的实例(非单例) |
request | 每个请求范围内会创建新的实例(web环境中,了解) |
session | 每个会话范围内会创建新的实例(web环境中,了解) |
application | 每个应用范围内会创建新的实例(web环境中,了解) |
- 可以通过 @Scope 注解来进行配置作用域:
1 |
|
注意事项:
- 默认singleton的bean,在容器启动时被创建,可以使用@Lazy注解来延迟初始化(延迟到第一次使用时)。
- prototype的bean,每一次使用该bean的时候都会创建一个新的实例。
- 实际开发当中,绝大部分的Bean是单例的,也就是说绝大部分Bean不需要配置scope属性。
第三方bean
- @Bean:如果要管理的 bean 对象来自于第三方(不是自定义的),是无法用
@Component
及衍生注解声明 bean 的,就需要用到@Bean
注解。 - 若要管理的第三方bean对象,建议对这些bean进行集中分类配置,可以通过
@Configuration
注解声明一个配置类。
注意事项:
- 通过@Bean注解的name或value属性可以声明bean的名称,如果不指定,默认bean的名称就是方法名。
- 如果第三方bean需要依赖其它bean对象,直接在bean定义方法中设置形参即可,容器会根据类型自动装配。
- @Component 及衍生注解 与 @Bean注解使用场景?
- 项目中自定义的,使用@Component及其衍生注解
- 项目中引入第三方的,使用@Bean注解
SpringBoot原理
起步依赖
Spring Boot Reference Documentation
自动配置
SpringBoot的自动配置就是当spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。
自动配置原理
- 方案一(不推荐):
@ComponentScan
组件扫描
1
2
3
4
public class SpringbootWebConfig2Application {
}- 方案二:
@Import
导入。使用@Import
导入的类会被 Spring 加载到 IOC 容器中,导入形式主要有以下几种:- 导入 普通类
- 导入 配置类
- 导入
ImportSelector
接口实现类 - @EnableXxxx注解,封装@Import注解 (SpringBoot 使用的)
- 方案一(不推荐):
源码跟踪
@SpringBootApplication
该注解标识在SpringBoot工程引导类上,是SpringBoot中最最最重要的注解。该注解由三个部分组成:
- @SpringBootConfiguration:该注解与 @Configuration 注解作用相同,用来声明当前也是一个配置类。
- @ComponentScan:组件扫描,默认扫描当前引导类所在包及其子包。
- @EnableAutoConfiguration:SpringBoot实现自动化配置的核心注解。
SpringBoot 会根据 @Conditional
注解条件装配!
Conditional
- 作用:按照一定的条件进行判断,在满足给定条件后才会注册对应的 bean 对象到 Spring IOC 容器中。
- 位置:方法、类
@Conditional
本身是一个父注解,派生出大量的子注解:@ConditionalOnClass
:判断环境中是否有对应字节码文件,才注册bean到IOC容器。@ConditionalOnMissingBean
:判断环境中没有对应的 bean(类型 或 名称) ,才注册bean到IOC@ConditionalOnProperty
:判断配置文件中有对应属性和值,才注册bean到IOC容器。
自动配置-案例