Maven

是一款用于管理和构建Java项目的工具。

作用:

  • 依赖管理:方便地管理项目的依赖资源(jar包),避免版本冲突问题
  • 同一项目结构:提供标准、统一的项目结构
  • 项目构建:标准跨平台(Linux、Windows、MacOS)的自动化项目的构建方式

配置步骤

image-20231012213407052

image-20231012215208634

image-20231012220617455

Maven坐标

  • Maven中的坐标是资源的唯一标识,通过该坐标可以唯一定位资源位置。
  • 使用坐标来定义项目或引入项目中需要的依赖。

image-20231012221532819

生命周期

为了对所有的maven项目构建过程进行抽象和统一。

maven中有3套相互独立的生命周期:

  • clean:清理工作
  • default:核心工作,如:编译、测试、打包、安装、部署等
  • site:生成报告、发布站点等

需要关注的阶段:

  • clean:移除上一次构建生成的文件
  • compile:编译项目源代码
  • test:使用合适的单元测试框架运行测试(junit)
  • package:将编译后的文件打包,如:jar、war等
  • install:安装项目到本地仓库

注意:在同一套生命周期中,当运行后面的阶段时,前面的阶段都会运行。

执行指定生命周期的两种方式:

  • 在idea中,右侧的maven工具栏,选中对应的生命周期,双击执行。
  • 在命令行中,通过命令执行。

Web相关基础知识回顾

HTTP(Hyper Text Transfer Protocol)

超文本传输协议,规定了浏览器和服务器之间数据传输的规则。

特点:

  1. 基于TCP协议:面向连接,安全
  2. 基于请求-响应模型的:一次请求对应一次响应
  3. HTTP协议是无状态的协议:对于事务处理没有记忆能力。每次请求-响应都是独立的。

缺点:多次请求间不能共享数据
优点:速度快

HTTP请求数据格式

请求行:请求数据第一行(请求方式、资源路径、协议)
请求头:第二行开始,格式key: value
请求体:POST请求,存放请求参数

img

两种请求方式的区别:
GET:请求参数在请求行中,没有请求体,如:/brand/findAll?name=OPPO&status=1。GET请求大小是有限制的。
POST:请求参数在请求体中,POST请求大小是没有限制的。

HTTP响应数据格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
HTTP/1.1 200 OK 						-- 响应行

Content-Type:text/html,charset=UTF-8

Content-length:101

Date:Wed,06 Jun 2018 07:08:42 GMT -- 响应头

-- 空行
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

helloword
</body>
</html> -- 响应体

响应行:响应数据第一行(协议、状态码、描述)
响应头:第二行开始,格式key: value
响应体:最后一部分,存放响应参数

状态码:
image-20231019161313961

响应头:
image-20231019161453280

比较

image-20231226032006042

Tomcat

web服务器:

  • 对HTTP协议操作进行封装,简化web程序开发。
  • 部署web项目,对外提供网上信息浏览服务。

Tomcat:

  • 一个轻量级的web服务器,支持servlet、jsp等少量javaEE规范。
  • 也被称为web容器、servlet容器。

SpringBootWeb

起步依赖

  • spring-boot-starter-web:包含了web应用开发所需要的常见依赖。
  • spring-boot-starter-test:包含了单元测试所需要的常见依赖。

内嵌Tomcat服务器

基于Springboot开发的web应用程序,内置了tomcat服务器,当启动类运行时,会自动启动内嵌的tomcat服务器。

前端控制器 DispatcherServlet

image-20231019184401199

image-20231019184617183

postman

postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。
作用:常用于进行接口测试

image-20231019204635308

请求响应

简单参数の请求方式

用原始方式获取请求参数(了解即可)

  • Controller方法形参中声明HttpServletRequest对象。
  • 调用对象的getParameter(参数名)。

太繁琐,并且需要手动类型转换。

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class RequestController {
@RequestMapping("/simpleParam")
public String simpleParam(HttpServletRequest request) {
String name = request.getParameter("name");
String ageStr = request.getParameter("age");

int age = Integer.parseInt(ageStr);
System.out.println(name + ":" + age);
return "OK";
}
}

用SpringBoot方式接收简单参数

  • 如果请求参数名与方法形参变量名相同,定义形参即可接收参数。(会自动进行类型转换。)
1
2
3
4
5
6
7
8
@RestController
public class RequestController {
@RequestMapping("/simpleParam")
public String simpleParam(String name, Integer age) {
System.out.println(name + ":" + age);
return "OK";
}
}
  • 如果方法形参名称与请求参数名称不匹配,就通过**@RequestParam*注解手动映射。(似乎没什么必要的样子,尽量让它们匹配呗。)*
1
2
3
4
5
6
7
8
@RestController
public class RequestController {
@RequestMapping("/simpleParam")
public String simpleParam(@RequestParam(name = "name") String username, Integer age) {
System.out.println(username + ":" + age);
return "OK";
}
}

注意:@RequestParam中的required属性默认为true,代表该请求参数必须传递,如果不传递将报错。如果该参数是可选的,可以将required属性设置为false。

实体对象参数の请求方式

简单实体对象

规则:请求参数名与形参中的实体对象属性名相同,即可通过POJO自动接收封装。

image-20231226025211260

复杂实体对象

规则:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接受嵌套POJO属性参数。

image-20231226025409963

数组集合参数の请求方式

  • 数组:请求参数名与形参中的数组变量名相同,直接使用数组封装。
    image-20231226032522840

  • 集合:请求参数名与形参中的集合变量名相同,通过**@RequestParam**绑定参数关系。
    image-20231226032645580

日期参数の请求方式

使用**@DateTimeFormat**注解完成日期参数格式转换。

image-20231226033255889

JSON参数の请求方式

JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接收参数,需要使用@RequestBody标识。

image-20231020135341882

路径参数の请求方式

通过请求URL直接传递参数,使用{…}来标识该路径参数,需要使用**@PathVariable**获取路径参数。

image-20231020140154018

响应数据

image-20231226035409478

image-20231021001548399

统一响应结果

从上图中,我们可以发现,服务器向前端返回的数据格式并不统一,会提高项目管理的成本。我们有必要对响应结果进行规范地统一化处理。👇
这个结果应具备通用性,要能基本满足所有的业务场景,便于项目的管理和维护。

例如下图,我们用一个实体对象Result来统一响应给前端数据,包含code、msg、data:

image-20231021001734100

案例

image-20231226044222273

Controller类的编写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@RestController
public class EmpController {
@RequestMapping("/listEmp")
public Result list() {
// 1. 加载并解析emp.xml
String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
System.out.println(file);
List<Emp> empList = XmlParserUtils.parse(file, Emp.class);

// 2. 对数据进行转换处理 - gender, job
empList.stream().forEach(emp -> {
// 处理 gender 1: 男, 2:女
String gender = emp.getGender();
if ("1".equals(gender)) {
emp.setGender("男");
} else if ("2".equals(gender)) {
emp.setGender("女");
}

// 处理job - 1: 讲师, 2: 班主任, 3: 就业指导
String job = emp.getJob();
if ("1".equals(job)) {
emp.setJob("讲师");
} else if ("2".equals(job)) {
emp.setJob("班主任");
} else if ("3".equals(job)) {
emp.setJob("就业指导");
}
});

// 3. 响应数据
return Result.success(empList);
}
}

然后直接在浏览器输入地址访问:
image-20231226050518018

分层解耦

三层架构

image-20231021003836564

  • Controller:控制层,接收前端发送的请求,对请求进行处理,并响应数据。
  • Service:业务逻辑层,处理具体的业务逻辑。
  • DAO:数据访问层(Data Access Objest) aka 持久层,负责数据访问操作,包括数据的增、删、改、查。

image-20231021004513700

image-20231021004757784

分层解耦(IOC-DI)概念理解

  • 内聚:软件中各个功能模块内部的功能联系。
  • 耦合:衡量软件中各个层/模块之间的依赖、关联的程度。
  • 软件设计原则:高内聚低耦合

image-20231021134401509

**控制反转(Inversion Of Control,IOC)**:对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。
**依赖注入(Dependency Injection, DI)**:容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。
Bean对象:IOC容器中创建、管理的对象,称之为bean。

IOC详解

Bean的声明

要把某个对象交给IOC容器管理,需要在对应的类上加上如下注解之一:

注解 说明 位置
@Component 声明bean的基础注解 不属于以下三类时,用此注解
@Controller @Component的衍生注解 标注在控制器类上
@Service @Component的衍生注解 标注在业务类上
@Repository @Component的衍生注解 标注在数据访问类上(由于与mybatis整合,用的少)

注意事项:

  • 声明bean的时候,可以通过value属性指定bean的名字,如果没有指定,默认为类名首字母小写。
  • 使用以上四个注解都可以声明bean,但是在springboot集成web开发中,声明控制器bean只能用@Controller。

组件扫描

  • 前面声明bean的四大注解,要想生效,还需要被组件扫描注解@ComponentScan扫描。
  • @ComponentScan注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解@SpringBootApplication中,默认扫描的范围是启动类所在包及其子包。

DI详解

Bean注入

  • @Autowired注解,默认按照类型进行自动装配,如果存在多个相同类型的bean,将会报出如下错误:
    image-20231022005146048

  • 通过以下几种方案来解决:

@Primary

1
2
3
4
@Primary
@Service
public class EmpServiceA implements EmpService {
}

@Autowired + @Qualifier(“bean的名称”)

1
2
3
4
5
6
@RestController
public class EmpController {
@Autowired
@Qualifier("empServiceA")
private EmpService empService;
}

@Resource(name = “bean的名称”)

1
2
3
4
5
@RestController
public class EmpController {
@Resource(name = "empServiceB")
private EmpService empService;
}

面试题:@Resource与@Autowired的区别

  • @Autowired是spring框架提供的注解,而@Resource是JDK提供的注解。
  • @Autowired默认是按照类型注入,而@Resource默认是按照名称注入。

MyBatis

是一款优秀的持久层框架,用于简化JDBC的开发。

入门程序“用MyBatis查询所有用户数据”の步骤

  1. 准备工作:创建SpringBoot工程、数据库表user、实体类User
    image-20231228180037132
    image-20231228175209325
  2. 引入MyBatis的相关依赖(在pom.xml),配置MyBatis(数据库连接信息)
    image-20231228182614471
  3. 编写SQL语句(有两种方式:注解、XML,此处用注解的方式)
    image-20231228180647639
    在使用MyBatis开发的过程中只需定义这个Mapper接口就行了,不需要定义它的实现类,因为程序运行时,框架底层会自动生成这个接口的实现类对象。
  4. 单元测试
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @SpringBootTest
    class SpringbootMybatisQuickstart1ApplicationTests {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void test1() {
    List<User> userList = userMapper.list();
    userList.stream().forEach(user -> {
    System.out.println(user);
    });
    }
    }

配置SQL提示

image-20231229035301209
image-20231229035606103

JDBC介绍

JDBC (Java DataBase Connectivity),就是使用Java语言操作关系型数据库的一套API。

本质上就是sun公司官方定义的一套错做所有关系型数据库的规范,即接口。各个厂商去实现这套接口,提供数据库驱动jar包。然后我们就可以使用这套接口(JDBC)进行编程,真正执行的代码是驱动jar包中的实现类。(还是相当繁琐的,MyBatis就好用得多。)

image-20231229045959054

使用SpringBoot整合MyBatis进行数据库操作

image-20231229050653496

数据库连接池

数据库连接池是个容器,负责分配、管理数据库连接(Connection)。它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。另外它还会释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏。

优势:

  • 资源重用
  • 提升系统响应速度
  • 避免数据库连接遗漏

标准接口:DataSource

是sun公司官方提供的数据库连接池接口,由第三方组织实现此接口。
功能:获取连接

1
connection getConnection() throws SQLException;

常见产品:

Lombok工具包

是一个实用的Java类库,,能通过注解的形式自动生成构造器、getter / setter、equals、hashcode、toString等方法,还可以自动化生成日志变量,提高java开发效率。

image-20231229053647386

Lombok会在编译时,自动生成对应的java代码。我们使用Lombok时,还需要安装一个Lombok的插件(Idea有自带)。

①MyBatis基础操作

环境准备

  • 准备数据库表,如emp。
  • 创建SpringBoot工程,引入对应的起步依赖。
    image-20231229055152548
  • application.properties中引入数据库连接信息。
    image-20231229055551656
  • 创建对应的实体类,如Emp (实体类属性采用驼峰命名)。
    image-20231229055752702
  • 准备Mapper接口,如EmpMapper
    image-20231229055907419

删除

根据主键删除
  • SQL语句

    1
    delete from emp where id = 17;
  • 接口方法

    1
    2
    @Delete("delete from emp where id = #{id}")
    public void delete(Integer id);

注意:如果mapper接口方法形参只有一个普通类型的参数,#{…}里面的属性名可以随便写,如:#{id}、#{value}。不过还是建议两者保持一致,增强可读性。

预编译SQL

image-20231229182848970

优势:

  • 性能更高
  • 更安全(防止SQL注入)
SQL注入

SQL注入是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。

参数占位符
  • #{…}

执行SQL时,会将#{…}替换为?,生成预编译SQL,会自动设置参数值。
传递参数的情况都使用#{...}

  • ${…}
    拼接SQL——直接将参数拼接在SQL语句中。这样就存在SQL注入问题。
    一般是对表名、列表进行动态设置时使用。

新增

  • SQL语句

    1
    2
    insert into emp(username,name,gender,image,job,entrydate,dept_id,create_time,update_time)
    values ('zhangsan','张三',1,'1.jpg',2,'2012-10-09',2,'2022-10-01 10:00:00','2022-10-01 10:00:00');
  • 接口方法:

    1
    2
    3
    @Insert("insert into emp(username,name,gender,image,job,entrydate,dept_id,create_time,update_time)" +
    "values(#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")
    public void insert(Emp emp);

    注意:如果有多个参数,可以使用实体类封装起来。

主键返回
1
2
3
4
@Options(useGenerated = true, keyProperty = "id")// 会自动将生成的主键值赋值给emp对象的id属性
@Insert("insert into emp(username,name,gender,image,job,entrydate,dept_id,create_time,update_time)" +
"values(#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")
public void insert(Emp emp);

更新

  • SQL语句

    1
    update emp set username='zhangsan',name='张三',gender=1,image='1.jpg',job=2,entrydate='2012-01-01',dept_id=2,update_time='2022-10-01 12:12:12' where id=19;
  • 接口方法

    1
    2
    @Update("update emp set username=#{username},name=#{name},gender=#{gender},image=#{image},job=#{job},entrydate={entrydate},dept_id=#{deptId},update_time=#{updateTime} where id=#{id}")
    public void update(Emp emp);

查询

  • SQL语句

    1
    select * from emp where id = 19;
  • 接口方法

    1
    2
    @Select("select * from emp where id = #{id}")
    public Emp getById(Integer id);
注意

实体类属性名与数据库表查询返回的字段名一致,MyBatis才会自动封装数据。(例子中针对dpet_id、create_time、update_time这三个数据)

解决方案①:给字段起别名,让别名与实体类属性一致。

1
2
3
@Select("select id, username, password, name, gender, image, job, entrydate, " +
"dpet_id deptId, create_time createTime, update_time updateTime from emp where id = #{id}")
public void getById(Integer id);

解决方案②:通过@Results,@Result注解手动结果映射的方式封装

1
2
3
4
5
6
7
@Results({
@Result(column = "dpet_id", property = "deptId"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")
})
@Select("select * from emp where id = #{id}")
public Emp getById(Integer id);

(推荐⭐) 解决方案③:开启MyBatis的驼峰命名自动映射开关

1
2
# 开启驼峰命名自动映射,即从数据库字段名a_column映射到Java属性名aColumn。
mybatis.configuration.map-underscore-to-camel-case=true

②XML映射文件

规范:

  • XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放在相同包下(同包同名)
  • XML映射文件的namespace属性与Mapper接口全限定名一致。
  • XML映射文件中的SQL语句的id与Mapper接口中的方法名一致,并保持返回类型一致。

image-20231229194108295

动态SQL

image-20231229195241913
image-20231229195647731
image-20231229200135712

登录拦截

  • 登陆标记:用户登录成功后,每一次请求中都可以获取到该标记。
  • 统一拦截:过滤器 Filter、拦截器 Interceptor

会话

  • 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。再一次会话中可以包含多次请求和响应。
  • 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据
  • 会话跟踪方案:
    • 客户端会话跟踪技术:Cookie
    • 服务端会话跟踪技术:Session
    • 令牌技术

image-20231230125349673

AOP (面向切面编程)

动态代理是该思想最主流的实现方式。SpringAOP底层就是用动态代理技术实现的。

SpringAOP快速入门

  • 导入依赖
  • 编写AOP程序:针对于特定方法根据业务需要进行编程
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Slf4j
    @Component
    @Aspect // 称被Aspect标注的类为切面类
    public class TimeAspect {
    @Around("execution(* com.xxx.service.*.*(...))") // 引号中填入切入点表达式
    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;
    }
    }

场景:记录方法运行耗时、记录操作日志、权限控制、事务管理等。
优势:代码无侵入、减少重复代码、提高开发效率、维护方便。

核心概念

  • 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)
  • 通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)
  • 切入点:PointCut,匹配连接点的额条件,通知仅会在切入点方法执行时被应用
  • 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)
  • 目标对象:Target,通知所应用的对象

执行流程

一旦进行了AOP开发,那最终运行的就不是原始的目标对象了,而是基于目标对象所生成的代理对象。

中间学过的内容

待整理。

HttpClient

HttpClient是Apache Jakarta Common下的子项目,可以用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。

核心API

  • HttpClient (构建器,是一个接口)
  • HttpClients (是一个实现类)
  • CloseableHttpClient
  • HttpGet
  • HttpPost

发送请求步骤

  • 创建HttpClient对象
  • 创建Http请求对象
  • 调用HttpClient的execute方法发送请求

Spring Cache

Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。

常用注解:

  • EnableCaching 开启缓存注解功能,通常加在启动类上
  • Cacheable 在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法返回值放到缓存中
  • CachePut 将方法的返回值放到缓存中
  • CacheEvict 将一条或多条数据从缓存中删除

Spring Task

cron表达式

是一个字符串,用来定义任务触发的时间。
构成规则:分为6或7个域,由空格分隔开,每个域代表一个含义。
每个域的含义分别为:秒、分钟、小时、日、月、周、年(可选)。

cron在线生成器:https://cron.qqe2.com/

简单案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 自定义定时任务类
*/
@Component
@Slf4j
public class MyTask {
/**
* 定时任务 每隔5秒触发一次
*/
@Scheduled(cron = "0/5 * * * * ?")
public void executeTask() {
log.info("定时任务开始执行:{}", new Date());
}
}

WebSocket

是基于TCP的一种新的网络协议。它实现了浏览器与服务器双全工通信,即:浏览器和服务器只需完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。
image-20231221154438418

简单案例实现步骤:

  1. 直接使用websocket.html页面作为WebSocket客户端
  2. 导入WebSocket的maven坐标
  3. 导入WebSocket服务端组件
  4. 导入配置类WebSocketConfiguration,注册WebSocket的服务端组件
  5. 导入定时任务类WebSocketTask,定时向客户端推送数据