目录
Web后端基础
Maven
Maven坐标
什么是坐标?
Maven中的坐标是资源的唯一标识 , 通过该坐标可以唯一定位资源位置
使用坐标来定义项目或引入项目中需要的依赖
Maven坐标主要组成:
groupId:定义当前Maven项目隶属组织名称(通常是域名反写,例如:com.itheima)
artifactId:定义当前Maven项目名称(通常是模块名称,例如 order-service、goods-service)
version:定义当前项目版本号
SNAPSHOT: 功能不稳定、尚处于开发中的版本,即快照版本
RELEASE: 功能趋于稳定、当前更新停止,可以用于发行的版本
导入Maven项目
在IDEA中导入Maven项目,有两种方式。
方式一:
File
->Project Structure
->Modules
->Import Module
->选择maven项目的pom.xml
。
方式二:
Maven面板
->+(Add Maven Projects)
->选择maven项目的pom.xml
。
依赖配置
基本配置
依赖:指当前项目运行所需要的jar包。一个项目中可以引入多个依赖:
例如:在当前工程中,我们需要用到logback来记录日志,此时就可以在maven工程的pom.xml文件中,引入logback的依赖。具体步骤如下:
在pom.xml中编写
<dependencies>
标签在
<dependencies>
标签中使用<dependency>
引入坐标定义坐标的
groupId
、artifactId
、version
点击刷新按钮,引入最新加入的坐标。
刷新依赖:保证每一次引入新的依赖,或者修改现有的依赖配置,都可以加入最新的坐标
排除依赖
排除依赖:指主动断开依赖的资源,被排除的资源无需指定版本。
配置形式如下:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.1.4</version> <!--排除依赖, 主动断开依赖的资源--> <exclusions> <exclusion> <groupId>io.micrometer</groupId> <artifactId>micrometer-observation</artifactId> </exclusion> </exclusions> </dependency>
生命周期
Maven对项目构建的生命周期划分为3套(相互独立):
- clean:清理工作。
- default:核心工作。如:编译、测试、打包、安装、部署等。
- site:生成报告、发布站点等。
我们看到这三套生命周期,里面有很多很多的阶段,这么多生命周期阶段,其实我们常用的并不多,主要关注以下几个:
clean:移除上一次构建生成的文件
compile:编译项目源代码
test:使用合适的单元测试框架运行测试(junit)
package:将编译后的文件打包,如:jar、war等
install:安装项目到本地仓库
说明:在同一套生命周期中,我们在执行后面的生命周期时,前面的生命周期都会执行。
Maven的生命周期是抽象的,这意味着生命周期本身不做任何实际工作。在Maven的设计中,实际任务(如源代码编译)都交由插件来完成。
在日常开发中,当我们要执行指定的生命周期时,有两种执行方式:
在idea工具右侧的maven工具栏中,选择对应的生命周期,双击执行
在DOS命令行中,通过maven命令执行(mvn clean, mvn compile, mvn test...)
单元测试
单元测试:就是针对最小的功能单元(方法),编写测试代码对其正确性进行测试。
JUnit:最流行的Java测试框架之一,提供了一些功能,方便程序进行单元测试(第三方公司提供)。
使用了JUnit单元测试框架进行测试,将会有以下优势:
测试代码与源代码分开,便于维护。
可根据需要进行自动化测试。
可自动分析测试结果,产出测试报告。
测试类的命名规范为:XxxxTest
测试方法的命名规定为:public void xxx(){...}
断言
JUnit提供了一些辅助方法,用来帮我们确定被测试的方法是否按照预期的效果正常工作,这种方式称为断言。
断言方法 描述 assertEquals(Object exp, Object act, String msg) 检查两个值是否相等,不相等就报错。 assertNotEquals(Object unexp, Object act, String msg) 检查两个值是否不相等,相等就报错。 assertNull(Object act, String msg) 检查对象是否为null,不为null,就报错。 assertNotNull(Object act, String msg) 检查对象是否不为null,为null,就报错。 assertTrue(boolean condition, String msg) 检查条件是否为true,不为true,就报错。 assertFalse(boolean condition, String msg) 检查条件是否为false,不为false,就报错。 assertSame(Object exp, Object act, String msg) 检查两个对象引用是否相等,不相等,就报错。
常见注解
在JUnit中还提供了一些注解,还增强其功能,常见的注解有以下几个:
注解 说明 备注 @Test 测试类中的方法用它修饰才能成为测试方法,才能启动执行 单元测试 @BeforeEach 用来修饰一个实例方法,该方法会在每一个测试方法执行之前执行一次。 初始化资源(准备工作) @AfterEach 用来修饰一个实例方法,该方法会在每一个测试方法执行之后执行一次。 释放资源(清理工作) @BeforeAll 用来修饰一个静态方法,该方法会在所有测试方法之前只执行一次。 初始化资源(准备工作) @AfterAll 用来修饰一个静态方法,该方法会在所有测试方法之后只执行一次。 释放资源(清理工作) @ParameterizedTest 参数化测试的注解 (可以让单个测试运行多次,每次运行时仅参数不同) 用了该注解,就不需要@Test注解了 @ValueSource 参数化测试的参数来源,赋予测试方法参数 与参数化测试注解配合使用 @DisplayName 指定测试类、测试方法显示的名称 (默认为类名、方法名)
依赖范围
作用范围:
主程序范围有效。(main文件夹范围内)
测试程序范围有效。(test文件夹范围内)
是否参与打包运行。(package指令范围内)
可以在pom.xml中配置
<scope></scope>
属性来控制依赖范围。scope的取值常见的如下:
scope值 主程序 测试程序 打包(运行) 范例 compile(默认) Y Y Y log4j test - Y - junit provided Y Y - servlet-api runtime - Y Y jdbc驱动
Maven常见问题
问题现象:Maven项目中添加的依赖,未正确下载,造成右侧Maven面板中的依赖报红,再次reload重新加载也不会再下载。
产生原因:由于网络原因,依赖没有下载完整导致的,在maven仓库中生成了xxx.lastUpdated文件,该文件不删除,不会再重新下载。
解决方案:
根据maven依赖的坐标,找到仓库中对应的 xxx.lastUpdated 文件,删除,删除之后重新加载项目即可。
通过命令 (del /s *.lastUpdated) 批量递归删除指定目录下的 xxx.lastUpdated 文件,删除之后重新加载项目即可。
重新加载依赖,依赖下载了之后,maven面板可能还会报红,此时可以关闭IDEA,重新打开IDEA加载此项目即可。
HTTP
HTTP概述
HTTP:Hyper Text Transfer Protocol(超文本传输协议),规定了浏览器与服务器之间数据传输的规则。
http是互联网上应用最为广泛的一种网络协议
http协议要求:浏览器在向服务器发送请求数据时,或是服务器在向浏览器发送响应数据时,都必须按照固定的格式进行数据传输
特点:
基于TCP协议: 面向连接,安全(TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于字节流的传输层通信协议,在数据传输方面更安全)
基于请求-响应模型: 一次请求对应一次响应(先请求后响应)
HTTP协议是无状态协议: 对于数据没有记忆能力。每次请求-响应都是独立的
缺点: 多次请求间不能共享数据
优点: 速度快
HTTP请求协议
介绍
请求协议:浏览器将数据以请求格式发送到服务器。包括:请求行、请求头 、请求体
常见的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请求的请求参数在请求行中,故不需要设置请求体
GET请求和POST请求的区别:
区别方式
GET请求
POST请求
请求参数
请求参数在请求行中。<br/>例:/brand/findAll?name=OPPO&status=1
请求参数在请求体中
请求参数长度
请求参数长度有限制(浏览器不同限制也不同)
请求参数长度没有限制
安全性
安全性低。原因:请求参数暴露在浏览器地址栏中。
安全性相对高
获取请求数据
Web服务器(Tomcat)对HTTP协议的请求数据进行解析,并进行了封装(HttpServletRequest),并在调用Controller方法的时候传递给了该方法。这样,就使得程序员不必直接对协议进行操作,让Web开发更加便捷。
package org.example; import jakarta.servlet.http.HttpServletRequest; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class RequestController { @RequestMapping("/request") public String request(HttpServletRequest request){ //1、获取请求方式 String method = request.getMethod(); System.out.println("请求方式:"+method); //2、获取请求url地址 String url = request.getRequestURL().toString(); System.out.println("请求地址:"+url); //3、获取请求协议 String protocol = request.getProtocol(); System.out.println("请求协议:"+protocol); //4、获取请求参数-name String name = request.getParameter("name"); System.out.println("请求参数-name:"+name); //5、获取请求头 String accept = request.getHeader("Accept"); System.out.println("请求头:"+accept); return "OK"; } }
HTTP响应协议
格式介绍
响应协议:服务器将数据以响应格式返回给浏览器。包括:响应行 、响应头 、响应体
响应状态码
状态码分类 说明 1xx 响应中 --- 临时状态码。表示请求已经接受,告诉客户端应该继续请求或者如果已经完成则忽略 2xx 成功 --- 表示请求已经被成功接收,处理已完成 3xx 重定向 --- 重定向到其它地方,让客户端再发起一个请求以完成整个处理 4xx 客户端错误 --- 处理发生错误,责任在客户端,如:客户端的请求一个不存在的资源,客户端未被授权,禁止访问等 5xx 服务器端错误 --- 处理发生错误,责任在服务端,如:服务端抛出异常,路由出错,HTTP版本不支持等 关于响应状态码,我们先主要认识三个状态码,其余的等后期用到了再去掌握:
200 ok
客户端请求成功
404 Not Found
请求资源不存在
500 Internal Server Error
服务端发生不可预期的错误
设置响应数据
Web服务器对HTTP协议的响应数据进行了封装(HttpServletResponse),并在调用Controller方法的时候传递给了该方法。这样,就使得程序员不必直接对协议进行操作,让Web开发更加便捷。
package org.example; import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; @RestController public class ResponseController { /* *方式一:HttpServletResponse设置相应数据 */ @RequestMapping("/response") public void response(HttpServletResponse response) throws IOException { //1、设置响应状态码 response.setStatus(200); //2、设置响应头 response.setHeader("name", "zhangsan"); //3、设置响应体 response.getWriter().write("hello world"); } /* *方式二:ResponseEntity ---Spring中提供的方式 */ @RequestMapping("/response2") public ResponseEntity<String> response2() { return ResponseEntity .status(200) .header("name","javaweb-ai") .body("<h1>hello world<h1>"); } }
响应状态码 和 响应头如果没有特殊要求的话,通常不手动设定。服务器会根据请求处理的逻辑,自动设置响应状态码和响应头。
三层架构
介绍
在我们进行程序设计以及程序开发时,尽可能让每一个接口、类、方法的职责更单一些(单一职责原则)。
单一职责原则:一个类或一个方法,就只做一件事情,只管一块功能。
这样就可以让类、接口、方法的复杂度更低,可读性更强,扩展性更好,也更利于后期的维护。
Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。
Service:业务逻辑层。处理具体的业务逻辑。
Dao:数据访问层(Data Access Object),也称为持久层。负责数据访问操作,包括数据的增、删、改、查。
内聚和耦合
内聚:软件中各个功能模块内部的功能联系。
耦合:衡量软件中各个层/模块之间的依赖、关联的程度。
软件设计原则:高内聚低耦合。
高内聚:指的是一个模块中各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高,即 "高内聚"。
低耦合:指的是软件中各个层、模块之间的依赖关联程序越低越好。
分层解耦
控制反转: Inversion Of Control,简称IOC。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。
对象的创建权由程序员主动创建转移到容器(由容器创建、管理对象)。这个容器称为:IOC容器或Spring容器。
依赖注入: Dependency Injection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。
程序运行时需要某个资源,此时容器就为其提供这个资源。
例:EmpController程序运行时需要EmpService对象,Spring容器就为其提供并注入EmpService对象。
bean对象:IOC容器中创建、管理的对象,称之为:bean对象。
控制反转IOC
要把某个对象交给IOC容器管理,需要在类上添加一个注解:
@Component
而Spring框架为了更好的标识web应用程序开发当中,bean对象到底归属于哪一层,又提供了@Component的衍生注解:
注解
说明
位置
@Component
声明bean的基础注解
不属于以下三类时,用此注解
@Controller
@Component的衍生注解
标注在控制层类上
@Service
@Component的衍生注解
标注在业务层类上
@Repository
@Component的衍生注解
标注在数据访问层类上(由于与mybatis整合,用的少)
注意1:声明bean的时候,可以通过注解的value属性指定bean的名字,如果没有指定,默认为类名首字母小写。
注意2:使用以上四个注解都可以声明bean,但是在springboot集成web开发中,声明控制器bean只能用@Controller。
依赖注入DI
@Autowired 进行依赖注入,常见的方式,有如下三种:
1). 属性注入
@RestController public class UserController { //方式一: 属性注入 @Autowired private UserService userService; }
优点:代码简洁、方便快速开发。
缺点:隐藏了类之间的依赖关系、可能会破坏类的封装性。
2). 构造函数注入
@RestController public class UserController { //方式二: 构造器注入 private final UserService userService; @Autowired //如果当前类中只存在一个构造函数, @Autowired可以省略 public UserController(UserService userService) { this.userService = userService; } }
优点:能清晰地看到类的依赖关系、提高了代码的安全性。
缺点:代码繁琐、如果构造参数过多,可能会导致构造函数臃肿。
注意:如果只有一个构造函数,@Autowired注解可以省略。(通常来说,也只有一个构造函数)
3). setter注入
/** * 用户信息Controller */ @RestController public class UserController { //方式三: setter注入 private UserService userService; @Autowired public void setUserService(UserService userService) { this.userService = userService; } }
优点:保持了类的封装性,依赖关系更清晰。
缺点:需要额外编写setter方法,增加了代码量。
如果同类型的bean存在多个:
方案一:使用@Primary注解
@Primary @Service public class UserServiceImpl implements UserService { }
方案二:使用@Qualifier注解
@RestController public class UserController { @Qualifier("userServiceImpl") @Autowired private UserService userService;
方案三:使用@Resource注解
@RestController public class UserController { @Resource(name = "userServiceImpl") private UserService userService;
@Autowird 与 @Resource的区别
@Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解
@Autowired 默认是按照类型注入,而@Resource是按照名称注入
JDBC
JDBC:(Java DataBase Connectivity),就是使用Java语言操作关系型数据库的一套API。
在 pom.xml 文件中引入依赖
<dependencies> <!-- MySQL JDBC driver --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.9.3</version> <scope>test</scope> </dependency> </dependencies>
- JDBC 程序执行 DML 语句?DQL 语句?
- DML 语句: int rowsAffected = statement.executeUpdate ();
- DQL 语句: ResultSet rs = statement.executeQuery ();
- DQL 语句执行完毕结果集 ResultSet 解析?
- resultSet.next (): 光标往下移动一行
- resultSet.getXxx (): 获取字段数据
预编译SQL
conn.prepareStatement("SELECT * FROM user WHERE username = ? AND password = ?"); pstmt.setString(1, "daqiao"); pstmt.setString(2, "123456"); ResultSet resultSet = pstmt.executeQuery();
为什么要使用预编译SQL?
- 安全,可以防治SQL注入(
' or '1' = '1
)- 性能更高
Mybatis
基础操作步骤
- 准备工作:
- 创建 SpringBoot 工程、引入 Mybatis 相关依赖
- 准备数据库表 user、实体类 User
- 配置 Mybatis(在 application.properties 中数据库连接信息)
#数据库访问的url地址 spring.datasource.url=jdbc:mysql://localhost:3306/web #数据库驱动类类名 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #访问数据库-用户名 spring.datasource.username=root #访问数据库-密码 spring.datasource.password=root@1234
- 编写 Mybatis 程序:编写 Mybatis 的持久层接口,定义 SQL (注解 / XML)
@Mapper public interface UserMapper { /** * 查询全部 */ @Select("select * from user") public List<User> findAll(); }
增删改查操作
删除
/** * 根据id删除 */ @Delete("delete from user where id = #{id}") public void deleteById(Integer id);
Mybatis的提供的符号,有两个,一个是
#{...}
,另一个是${...}
,区别如下:
符号 说明 场景 优缺点 #{…} 占位符。执行时,会将#{…}替换为?,生成预编译SQL 参数值传递 安全、性能高 (推荐) ${…} 拼接符。直接将参数拼接在SQL语句中,存在SQL注入问题 表名、字段名动态设置时使用 不安全、性能低 新增
/** * 添加用户 */ @Insert("insert into user(username,password,name,age) values(#{username},#{password},#{name},#{age})") public void insert(User user);
如果在SQL语句中,我们需要传递多个参数,我们可以把多个参数封装到一个对象中。然后在SQL语句中,我们可以通过
#{对象
属性
名}
的方式,获取到对象中封装的属性值。修改
/** * 根据id更新用户信息 */ @Update("update user set username = #{username},password = #{password},name = #{name},age = #{age} where id = #{id}") public void update(User user);
查询
/** * 根据用户名和密码查询用户信息 */ @Select("select * from user where username = #{username} and password = #{password}") public User findByUsernameAndPassword(@Param("username") String username, @Param("password") String password);
@param注解的作用是为接口的方法形参起名字的。(由于用户名唯一的,所以查询返回的结果最多只有一个,可以直接封装到一个对象中)
说明:基于官方骨架创建的springboot项目中,接口编译时会保留方法形参名,@Param注解可以省略 (#{形参名})。
XML映射配置
Mybatis的开发有两种方式:
- 注解
- XML
XML配置文件规范
使用Mybatis的注解方式,主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能,建议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中。
在Mybatis中使用XML映射文件方式开发,需要符合一定的规范:
XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)
XML映射文件的namespace属性为Mapper接口全限定名一致
XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.itheima.mapper.EmpMapper"> <!--查询操作--> <select id="findAll" resultType="com.itheima.pojo.User"> select * from user </select> </mapper>
#指定XML映射配置文件的位置 mybatis.mapper-locations=classpath:mapper/*.xml
SpringBoot配置文件
SpringBoot支持哪几类配置文件:
- application.properties
- application.yaml
- application.yml
简单的了解过springboot所支持的配置文件,以及不同类型配置文件之间的优缺点之后,接下来我们就来了解下yml配置文件的基本语法:
- 大小写敏感
- 数值前边必须有空格,作为分隔符
- 使用缩进表示层级关系,缩进时,不允许使用Tab键,只能用空格(idea中会自动将Tab转换为空格)
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
#
表示注释,从这个字符一直到行尾,都会被解析器忽略了解完yml格式配置文件的基本语法之后,接下来我们再来看下yml文件中常见的数据格式。在这里我们主要介绍最为常见的两类:
- 定义对象或Map集合
user: name: zhangsan age: 18 password: 123456
- 定义数组、list或set集合
hobby: - java - game - sport
注意:在yml格式的配置文件中,如果配置项的值是以 0 开头的,值需要使用 '' 引起来,因为以0开头在yml中表示8进制的数据。