【自用】JavaWeb学习笔记
Maven
是一款用于管理和构建Java项目的工具。
作用:
- 依赖管理:方便地管理项目的依赖资源(jar包),避免版本冲突问题
- 同一项目结构:提供标准、统一的项目结构
- 项目构建:标准跨平台(Linux、Windows、MacOS)的自动化项目的构建方式
配置步骤
Maven坐标
- Maven中的坐标是资源的唯一标识,通过该坐标可以唯一定位资源位置。
- 使用坐标来定义项目或引入项目中需要的依赖。
生命周期
为了对所有的maven项目构建过程进行抽象和统一。
maven中有3套相互独立的生命周期:
- clean:清理工作
- default:核心工作,如:编译、测试、打包、安装、部署等
- site:生成报告、发布站点等
需要关注的阶段:
- clean:移除上一次构建生成的文件
- compile:编译项目源代码
- test:使用合适的单元测试框架运行测试(junit)
- package:将编译后的文件打包,如:jar、war等
- install:安装项目到本地仓库
注意:在同一套生命周期中,当运行后面的阶段时,前面的阶段都会运行。
执行指定生命周期的两种方式:
- 在idea中,右侧的maven工具栏,选中对应的生命周期,双击执行。
- 在命令行中,通过命令执行。
Web相关基础知识回顾
HTTP(Hyper Text Transfer Protocol)
超文本传输协议,规定了浏览器和服务器之间数据传输的规则。
特点:
- 基于TCP协议:面向连接,安全
- 基于请求-响应模型的:一次请求对应一次响应
- HTTP协议是无状态的协议:对于事务处理没有记忆能力。每次请求-响应都是独立的。
缺点:多次请求间不能共享数据
优点:速度快
HTTP请求数据格式
请求行:请求数据第一行(请求方式、资源路径、协议)
请求头:第二行开始,格式key: value
请求体:POST请求,存放请求参数
两种请求方式的区别:
GET:请求参数在请求行中,没有请求体,如:/brand/findAll?name=OPPO&status=1。GET请求大小是有限制的。
POST:请求参数在请求体中,POST请求大小是没有限制的。
HTTP响应数据格式
1 | 200 OK -- 响应行 |
响应行:响应数据第一行(协议、状态码、描述)
响应头:第二行开始,格式key: value
响应体:最后一部分,存放响应参数
状态码:
响应头:
比较
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
postman
postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。
作用:常用于进行接口测试
请求响应
简单参数の请求方式
用原始方式获取请求参数(了解即可)
- Controller方法形参中声明HttpServletRequest对象。
- 调用对象的getParameter(参数名)。
太繁琐,并且需要手动类型转换。
1 |
|
用SpringBoot方式接收简单参数
- 如果请求参数名与方法形参变量名相同,定义形参即可接收参数。(会自动进行类型转换。)
1 |
|
- 如果方法形参名称与请求参数名称不匹配,就通过**@RequestParam*注解手动映射。(似乎没什么必要的样子,尽量让它们匹配呗。)*
1 |
|
注意:@RequestParam中的required属性默认为true,代表该请求参数必须传递,如果不传递将报错。如果该参数是可选的,可以将required属性设置为false。
实体对象参数の请求方式
简单实体对象
规则:请求参数名与形参中的实体对象属性名相同,即可通过POJO自动接收封装。
复杂实体对象
规则:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接受嵌套POJO属性参数。
数组集合参数の请求方式
数组:请求参数名与形参中的数组变量名相同,直接使用数组封装。
集合:请求参数名与形参中的集合变量名相同,通过**@RequestParam**绑定参数关系。
日期参数の请求方式
使用**@DateTimeFormat**注解完成日期参数格式转换。
JSON参数の请求方式
JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接收参数,需要使用@RequestBody标识。
路径参数の请求方式
通过请求URL直接传递参数,使用{…}来标识该路径参数,需要使用**@PathVariable**获取路径参数。
响应数据
统一响应结果
从上图中,我们可以发现,服务器向前端返回的数据格式并不统一,会提高项目管理的成本。我们有必要对响应结果进行规范地统一化处理。👇
这个结果应具备通用性,要能基本满足所有的业务场景,便于项目的管理和维护。
例如下图,我们用一个实体对象Result来统一响应给前端数据,包含code、msg、data:
案例
Controller类的编写:
1 |
|
然后直接在浏览器输入地址访问:
分层解耦
三层架构
- Controller:控制层,接收前端发送的请求,对请求进行处理,并响应数据。
- Service:业务逻辑层,处理具体的业务逻辑。
- DAO:数据访问层(Data Access Objest) aka 持久层,负责数据访问操作,包括数据的增、删、改、查。
分层解耦(IOC-DI)概念理解
- 内聚:软件中各个功能模块内部的功能联系。
- 耦合:衡量软件中各个层/模块之间的依赖、关联的程度。
- 软件设计原则:高内聚低耦合。
**控制反转(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,将会报出如下错误:
通过以下几种方案来解决:
@Primary
1 |
|
@Autowired + @Qualifier(“bean的名称”)
1 |
|
@Resource(name = “bean的名称”)
1 |
|
面试题:@Resource与@Autowired的区别
- @Autowired是spring框架提供的注解,而@Resource是JDK提供的注解。
- @Autowired默认是按照类型注入,而@Resource默认是按照名称注入。
MyBatis
是一款优秀的持久层框架,用于简化JDBC的开发。
入门程序“用MyBatis查询所有用户数据”の步骤
- 准备工作:创建SpringBoot工程、数据库表user、实体类User
- 引入MyBatis的相关依赖(在pom.xml),配置MyBatis(数据库连接信息)
- 编写SQL语句(有两种方式:注解、XML,此处用注解的方式)
在使用MyBatis开发的过程中只需定义这个Mapper接口就行了,不需要定义它的实现类,因为程序运行时,框架底层会自动生成这个接口的实现类对象。 - 单元测试
1
2
3
4
5
6
7
8
9
10
11
12
13
class SpringbootMybatisQuickstart1ApplicationTests {
private UserMapper userMapper;
public void test1() {
List<User> userList = userMapper.list();
userList.stream().forEach(user -> {
System.out.println(user);
});
}
}
配置SQL提示
JDBC介绍
JDBC (Java DataBase Connectivity),就是使用Java语言操作关系型数据库的一套API。
本质上就是sun公司官方定义的一套错做所有关系型数据库的规范,即接口。各个厂商去实现这套接口,提供数据库驱动jar包。然后我们就可以使用这套接口(JDBC)进行编程,真正执行的代码是驱动jar包中的实现类。(还是相当繁琐的,MyBatis就好用得多。)
使用SpringBoot整合MyBatis进行数据库操作
数据库连接池
数据库连接池是个容器,负责分配、管理数据库连接(Connection)。它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。另外它还会释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏。
优势:
- 资源重用
- 提升系统响应速度
- 避免数据库连接遗漏
标准接口:DataSource
是sun公司官方提供的数据库连接池接口,由第三方组织实现此接口。
功能:获取连接
1 | connection getConnection() throws SQLException; |
常见产品:
- C3P0
- DBCP
- Druid (德鲁伊)
目前常用,阿里巴巴开源,功能强大,性能优秀,Java语言最好的数据库连接池之一。
https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter - Hikari (追光者)
目前常用,SpringBoot默认。
Lombok工具包
是一个实用的Java类库,,能通过注解的形式自动生成构造器、getter / setter、equals、hashcode、toString等方法,还可以自动化生成日志变量,提高java开发效率。
Lombok会在编译时,自动生成对应的java代码。我们使用Lombok时,还需要安装一个Lombok的插件(Idea有自带)。
①MyBatis基础操作
环境准备
- 准备数据库表,如emp。
- 创建SpringBoot工程,引入对应的起步依赖。
- application.properties中引入数据库连接信息。
- 创建对应的实体类,如Emp (实体类属性采用驼峰命名)。
- 准备Mapper接口,如EmpMapper
删除
根据主键删除
SQL语句
1
delete from emp where id = 17;
接口方法
1
2
public void delete(Integer id);
注意:如果mapper接口方法形参只有一个普通类型的参数,#{…}里面的属性名可以随便写,如:#{id}、#{value}。不过还是建议两者保持一致,增强可读性。
预编译SQL
优势:
- 性能更高
- 更安全(防止SQL注入)
SQL注入
SQL注入是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。
参数占位符
- #{…}
执行SQL时,会将#{…}替换为?,生成预编译SQL,会自动设置参数值。
传递参数的情况都使用#{...}
。
- ${…}
拼接SQL——直接将参数拼接在SQL语句中。这样就存在SQL注入问题。
一般是对表名、列表进行动态设置时使用。
新增
SQL语句
1
2insert 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
public void insert(Emp emp);注意:如果有多个参数,可以使用实体类封装起来。
主键返回
1 |
更新
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
public void update(Emp emp);
查询
SQL语句
1
select * from emp where id = 19;
接口方法
1
2
public Emp getById(Integer id);
注意
实体类属性名与数据库表查询返回的字段名一致,MyBatis才会自动封装数据。(例子中针对dpet_id、create_time、update_time这三个数据)
解决方案①:给字段起别名,让别名与实体类属性一致。
1 |
|
解决方案②:通过@Results,@Result注解手动结果映射的方式封装
1 |
|
(推荐⭐) 解决方案③:开启MyBatis的驼峰命名自动映射开关
1 | # 开启驼峰命名自动映射,即从数据库字段名a_column映射到Java属性名aColumn。 |
②XML映射文件
规范:
- XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放在相同包下(同包同名)。
- XML映射文件的namespace属性与Mapper接口全限定名一致。
- XML映射文件中的SQL语句的id与Mapper接口中的方法名一致,并保持返回类型一致。
动态SQL
登录拦截
- 登陆标记:用户登录成功后,每一次请求中都可以获取到该标记。
- 统一拦截:过滤器 Filter、拦截器 Interceptor
会话
- 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。再一次会话中可以包含多次请求和响应。
- 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
- 会话跟踪方案:
- 客户端会话跟踪技术:Cookie
- 服务端会话跟踪技术:Session
- 令牌技术
AOP (面向切面编程)
动态代理是该思想最主流的实现方式。SpringAOP底层就是用动态代理技术实现的。
SpringAOP快速入门
- 导入依赖
- 编写AOP程序:针对于特定方法根据业务需要进行编程
1
2
3
4
5
6
7
8
9
10
11
12
13
// 称被Aspect标注的类为切面类
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;
}
}
场景:记录方法运行耗时、记录操作日志、权限控制、事务管理等。
优势:代码无侵入、减少重复代码、提高开发效率、维护方便。
核心概念
- 连接点: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 | /** |
WebSocket
是基于TCP的一种新的网络协议。它实现了浏览器与服务器双全工通信,即:浏览器和服务器只需完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。
简单案例实现步骤:
- 直接使用websocket.html页面作为WebSocket客户端
- 导入WebSocket的maven坐标
- 导入WebSocket服务端组件
- 导入配置类WebSocketConfiguration,注册WebSocket的服务端组件
- 导入定时任务类WebSocketTask,定时向客户端推送数据