From 46f7212b9dd0334dbe209bda41f0b48b53fd715e Mon Sep 17 00:00:00 2001 From: zhangsan <646228430@qq.com> Date: Thu, 17 Apr 2025 18:55:50 +0800 Subject: [PATCH] =?UTF-8?q?Commit=20on=202025/04/17=20=E5=91=A8=E5=9B=9B?= =?UTF-8?q?=2018:55:50.30?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 自学/Docker指南.md | 50 ++-- 自学/JavaWeb——后端.md | 653 ------------------------------------------ 自学/Java笔记本.md | 50 ++-- 自学/Maven.md | 391 +++++++++++++++++++++++++ 自学/Mybatis.md | 403 ++++++++++++++++++++++++++ 自学/linux服务器.md | 2 + 自学/苍穹外卖.md | 256 ++++++++--------- 7 files changed, 984 insertions(+), 821 deletions(-) create mode 100644 自学/Maven.md create mode 100644 自学/Mybatis.md diff --git a/自学/Docker指南.md b/自学/Docker指南.md index a867de7..489924d 100644 --- a/自学/Docker指南.md +++ b/自学/Docker指南.md @@ -60,7 +60,7 @@ Docker是一个CS架构的程序,由两部分组成: 1. docker push,将本地镜像上传到远程仓库(例如 Docker Hub) -```text +```shell docker login #docker hub登录 # 假设已有本地镜像 myimage,需要先打上标签: docker tag myimage yourusername/myimage:latest @@ -70,34 +70,40 @@ docker push yourusername/myimage:latest 2. docker pull ,从远程仓库拉取镜像到本地。 -```text +```shell docker pull yourusername/myimage:latest ``` 3. docker save,将本地镜像保存为 tar 文件,方便备份或传输 -```text +```shell docker save -o myimage.tar yourusername/myimage:latest ``` 4. docker load,从 tar 文件中加载镜像到本地 Docker。 -```text +```shell docker load -i myimage.tar ``` 5. docker images ,查看本地镜像 -```text +```shell docker images ``` 6. docker build ,构建镜像 -t后面跟镜像名 -```text +```shell docker build -t yourusername/myimage:latest . ``` +7. 清理悬空、无名镜像 + +```shell +docker image prune +``` + #### 容器操作 @@ -855,33 +861,39 @@ build: ./web_app #两种写法是等效的 **构建镜像:**这个命令根据 docker-compose.yml 中各服务的配置构建镜像。如果你修改了 Dockerfile 或者项目代码需要打包进镜像时,就需要运行该命令来构建新的镜像。 -```text +```shell docker-compose build ``` **启动容器:**这个命令用于启动服务,参数 `-d` 表示以后台守护进程的方式运行。如果镜像不存在,它会自动构建镜像;但如果镜像已经存在,则默认直接使用现有的镜像启动容器。 -```text +```shell docker-compose up -d ``` +**进入容器:** +```shell +docker compose exec -it filebrowser sh +``` + +注意!一般进入数据卷挂载,直接在宿主机上操作容器内部就可以了!!!!! **只针对 pyapp 服务进行重构和启动,不影响其他服务运行** -```text +```shell docker-compose build pyapp ``` 启动容器并进入bash -```text +```shell docker compose run --rm -it pyapp /bin/bash ``` 运行脚本 -```text +```shell python typecho_markdown_upload/main.py ``` @@ -889,7 +901,7 @@ python typecho_markdown_upload/main.py **更新并重启容器** -```text +```shell docker-compose up --build -d ``` @@ -901,40 +913,40 @@ docker-compose up --build -d **查看服务的日志输出** -```text +```shell docker-compose logs flask_app --since 1h #只显示最近 1 小时 ``` **停止并删除所有由 docker-compose 启动的容器、网络等(默认不影响挂载卷)。** -```text +```shell docker-compose down #不能单独指定 ``` **删除停止的容器** -```text +```shell docker-compose rm docker-compose rm flask_app ``` **停止运行的容器** -```text +```shell docker-compose stop docker-compose stop flask_app #指定某个服务 ``` **启动服务** -```text +```shell docker-compose start #启动所有停止的.. docker-compose start flask_app ``` **重启服务(停止+启动)** -```text +```shell docker-compose restart docker-compose restart flask_app #指定某个服务 ``` @@ -953,7 +965,7 @@ docker-compose up生成的容器名默认是 `项目名_服务名_索引号` 如 -```text +```shell docker-compose -p my_custom_project up -d ``` diff --git a/自学/JavaWeb——后端.md b/自学/JavaWeb——后端.md index 1917b46..2cf69b3 100644 --- a/自学/JavaWeb——后端.md +++ b/自学/JavaWeb——后端.md @@ -43,251 +43,6 @@ Edit Configurations -## Maven - -![image-20240229132137502](https://pic.bitday.top/i/2025/03/19/u6rxj1-2.png) - -![image-20240229133408054](https://pic.bitday.top/i/2025/03/19/u6tcqw-2.png) - -Maven仓库分为: - -- 本地仓库:自己计算机上的一个目录(用来存储jar包) -- 中央仓库:由Maven团队维护的全球唯一的。仓库地址:https://repo1.maven.org/maven2/ -- 远程仓库(私服):一般由公司团队搭建的私有仓库 - -POM文件导入依赖的时候,先看本地仓库有没有,没有就看私服,再没有就从中央仓库下载。 - - - -### Maven创建/导入项目 - -**创建Maven项目** - -![image-20250307174233390](https://pic.bitday.top/i/2025/03/19/u6u4ni-2.png) - -勾选 **Create from archetype**(可选),也可以选择 **maven-archetype-quickstart** 等模版。 - -点击 Next,填写 GAV 坐标 。 - -GroupId:标识组织或公司(通常使用域名反写,如 `com.example`) - -ArtifactId:标识具体项目或模块(如 `my-app`、`spring-boot-starter-web`)。 - -Version:标识版本号(如 `1.0-SNAPSHOT`、`2.7.3`) - - - -**导入Maven项目** - -**(一)单独的Maven项目** - -打开 IDEA,在主界面选择 Open(或者在菜单栏选择 File -> Open)。 - -在文件选择对话框中,定位到已有项目的根目录(包含 `pom.xml` 的目录)。 - -选择该目录后,IDEA 会检测到 `pom.xml` 并询问是否导入为 Maven 项目,点击 **OK** 或 **Import** 即可。 - -IDEA 会自动解析 `pom.xml`,下载依赖并构建项目结构。 - - - -**(二)在现有Maven项目中导入独立的Maven项目** - -在已经打开的 IDEA 窗口中,使用 **File -> New -> Module from Existing Sources...** - -选择待导入项目的根目录(其中包含 `pom.xml`),IDEA 会将其导入为同一个工程下的另一个模块(Module)。 - -它们 看起来在一个工程里了,但**仍然是两个独立的** Maven 模块。 - - - -**(三)两个模块有较强的关联** - -1.新建一个上层目录,如下,MyProject1和MyProject2的内容拷贝过去。 - -```java -ParentProject/ -├── pom.xml <-- 父模块(聚合模块) -├── MyProject1/ <-- 子模块1 -│ └── pom.xml -└── MyProject2/ <-- 子模块2 - └── pom.xml -``` - -2.创建父级pom - -父模块 `pom.xml` 示例: - -```java - - 4.0.0 - com.example - ParentProject - 1.0-SNAPSHOT - pom //必写 - - - MyProject1 //必写 - MyProject2 - - - -``` - -3.修改子模块 `pom.xml` ,加上: - -```java - - com.example - ParentProject - 1.0-SNAPSHOT - ../pom.xml - -``` - -如果子模块中无需与父级不同的配置,**可以不写**,就自动继承父级配置;若写了同名配置,则表示你想要**覆盖或合并**父级配置。 - -4.File -> Open选择父级的pom,会自动导入其下面两个项目。 - - - -**(四)通过 Maven 依赖引用(一般导入官方依赖)** - -如果你的两个项目之间存在依赖关系(例如,第二个项目需要引用第一个项目打包后的 JAR),可以采用以下方式: - -在第一个项目(被依赖项目)执行 `mvn install` - -- 这会把打包后的产物安装到本地仓库(默认 `~/.m2/repository`)。 - -在第二个项目的 `pom.xml` 中添加依赖坐标 - -```java - - com.example - my-first-project - 1.0-SNAPSHOT - - -``` - -Maven 重建 - - - -### Maven坐标 - -什么是坐标? - -* Maven中的坐标是 == 资源的唯一标识 == 通过该坐标可以唯一定位资源位置 -* 使用坐标来定义项目或引入项目中需要的依赖 - -![image-20240302131843540](https://pic.bitday.top/i/2025/03/19/u6ps37-2.png) - -### 依赖管理 - -可以到mvn的中央仓库(https://mvnrepository.com/)中搜索获取依赖的坐标信息 - -```java - - - - ch.qos.logback - logback-classic - 1.2.11 - - - - junit - junit - 4.12 - test - - -``` - -更改之后可以在界面上看到一个maven刷新按钮,点击一下就开始联网下载依赖了,成功后可以看到 - -![image-20240302133241227](https://pic.bitday.top/i/2025/03/19/u6szry-2.png) - -#### 排除依赖 - -A依赖B,B依赖C,如果A不想将C依赖进来,可以同时排除C,被排除的资源**无需指定版本**。 - -```java - - com.itheima - maven-projectB - 1.0-SNAPSHOT - - - - - junit - junit - - - -``` - -#### 依赖范围 - -| **scope**值 | **主程序** | **测试程序** | **打包(运行)** | **范例** | -| --------------- | ---------- | ------------ | ---------------- | ----------- | -| compile(默认) | Y | Y | Y | log4j | -| test | - | Y | - | junit | -| provided | Y | Y | - | servlet-api | -| runtime | - | Y | Y | jdbc驱动 | - -注意!!!这里的scope如果是`test`,那么它的作用范围在`src/test/java`下,在`src/main/java`下无法导包! - - - -### Maven生命周期 - -主要关注以下几个: - -• clean:移除上一次构建生成的文件 (Target文件夹) - -• compile:编译 `src/main/java` 中的 Java 源文件至 `target/classes` - -• test:使用合适的单元测试框架运行测试(junit) - -• package:将编译后的文件打包,如:jar、war等 - -• install:将打包后的产物(如 `jar`)安装到本地仓库 - -#### 单元测试 - -1. 导入依赖junit - - ```java - - junit - junit - 4.12 - test - - ``` - -2. 在src/test/java下创建DemoTest类(*Test) - -3. 创建test方法 - - ```java - @Test - public void test1(){ - System.out.println("hello1"); - } - @Test - public void test2(){ - System.out.println("hello2"); - } - ``` - -4. 双击test生命周期 - -![image-20240302140156166](https://pic.bitday.top/i/2025/03/19/u6rc3p-2.png) - ## HTTP协议 ### 响应状态码 @@ -319,8 +74,6 @@ A依赖B,B依赖C,如果A不想将C依赖进来,可以同时排除C,被 ## 开发规范 - - ### REST风格 在前后端进行交互的时候,我们需要基于当前主流的REST风格的API接口进行交互。 @@ -1518,412 +1271,6 @@ public class SpringbootWebConfig2Application { -## Mybatis - -### 快速创建 - -![image-20240307125505211](https://pic.bitday.top/i/2025/03/19/u6pfoj-2.png) - -1. 创建springboot工程(Spring Initializr),并导入 mybatis的起步依赖、mysql的驱动包。创建用户表user,并创建对应的实体类User - ![image-20240307125820685](https://pic.bitday.top/i/2025/03/19/u6q96d-2.png) - -2. 在springboot项目中,可以编写main/resources/application.properties文件,配置数据库连接信息。 - - ``` - #驱动类名称 - spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver - #数据库连接的url - spring.datasource.url=jdbc:mysql://localhost:3306/mybatis - #连接数据库的用户名 - spring.datasource.username=root - #连接数据库的密码 - spring.datasource.password=1234 - ``` - -3. 在引导类所在包下,在创建一个包 mapper。在mapper包下创建一个接口 UserMapper - - ![image-20240307132356616](https://pic.bitday.top/i/2025/03/19/u6qtz4-2.png) - -@Mapper注解:表示是mybatis中的Mapper接口 - -​ -程序运行时:框架会自动生成接口的**实现类对象(代理对象)**,并交给Spring的IOC容器管理 - -@Select注解:代表的就是select查询,用于书写select查询语句 - -```java -@Mapper -public interface UserMapper { - //查询所有用户数据 - @Select("select * from user") - public List list(); -} -``` - - - -### 数据库连接池 - -数据库连接池是一个容器,负责管理和分配数据库连接(`Connection`)。 - -- 在程序启动时,连接池会创建一定数量的数据库连接。 -- 客户端在执行 SQL 时,从连接池获取连接对象,执行完 SQL 后,将连接归还给连接池,以供其他客户端复用。 -- 如果连接对象长时间空闲且超过预设的最大空闲时间,连接池会自动释放该连接。 - -**优势**:避免频繁创建和销毁连接,提高数据库访问效率。 - - - -Druid(德鲁伊) - -* Druid连接池是阿里巴巴开源的数据库连接池项目 - -* 功能强大,性能优秀,是Java语言最好的数据库连接池之一 - -把默认的 Hikari 数据库连接池切换为 Druid 数据库连接池: - -1. 在pom.xml文件中引入依赖 - - ```xml - - - com.alibaba - druid-spring-boot-starter - 1.2.8 - - ``` - -2. 在application.properties中引入数据库连接配置 - - ```properties - spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver - spring.datasource.druid.url=jdbc:mysql://localhost:3306/mybatis - spring.datasource.druid.username=root - spring.datasource.druid.password=123456 - ``` - - - -### SQL注入问题 - -SQL注入:由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。 - -在Mybatis中提供的参数占位符有两种:${...} 、#{...} - -- #{...} - - 执行SQL时,会将#{…}替换为?,生成预编译SQL,会自动设置参数值 - - 使用时机:参数传递,都使用#{…} - -- ${...} - - 拼接SQL。直接将参数拼接在SQL语句中,**存在SQL注入问题** - - 使用时机:如果对表名、列表进行动态设置时使用 - - - -### 日志输出 - -只建议开发环境使用:在Mybatis当中我们可以借助日志,查看到sql语句的执行、执行传递的参数以及执行结果 - -1. 打开application.properties文件 - -2. 开启mybatis的日志,并指定输出到控制台 - -```java -#指定mybatis输出日志的位置, 输出控制台 -mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl -``` - - - -### 驼峰命名法 - -在 Java 项目中,数据库表字段名一般使用 **下划线命名法**(snake_case),而 Java 中的变量名使用 **驼峰命名法**(camelCase)。 - -- [x] **小驼峰命名(lowerCamelCase)**: - -- 第一个单词的首字母小写,后续单词的首字母大写。 -- **例子**:`firstName`, `userName`, `myVariable` - -**大驼峰命名(UpperCamelCase)**: - -- 每个单词的首字母都大写,通常用于类名或类型名。 -- **例子**:`MyClass`, `EmployeeData`, `OrderDetails` - - - -表中查询的数据封装到实体类中 - -- 实体类属性名和数据库表查询返回的**字段名一致**,mybatis会自动封装。 -- 如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装。 - -![image-20221212103124490](https://pic.bitday.top/i/2025/03/19/u6o894-2.png) - -解决方法: - -1. 起别名 -2. 结果映射 -3. **开启驼峰命名** -4. **属性名和表中字段名保持一致** - - - -**开启驼峰命名(推荐)**:如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射 - -> 驼峰命名规则: abc_xyz => abcXyz -> -> - 表中字段名:abc_xyz -> - 类中属性名:abcXyz - - - -### 推荐的完整配置: - -```yaml -mybatis: - #mapper配置文件 - mapper-locations: classpath:mapper/*.xml - type-aliases-package: com.sky.entity - configuration: - #开启驼峰命名 - map-underscore-to-camel-case: true -``` - -`type-aliases-package: com.sky.entity`把 `com.sky.entity` 包下的所有类都当作别名注册,XML 里就可以直接写 `` 而不用写全限定名。可以多添加几个包,用逗号隔开。 - - - -### 增删改 - -- **增删改通用!:返回值为int时,表示影响的记录数,一般不需要可以设置为void!** - -**作用于单个字段** - -```java -@Mapper -public interface EmpMapper { - //SQL语句中的id值不能写成固定数值,需要变为动态的数值 - //解决方案:在delete方法中添加一个参数(用户id),将方法中的参数,传给SQL语句 - /** - * 根据id删除数据 - * @param id 用户id - */ - @Delete("delete from emp where id = #{id}")//使用#{key}方式获取方法中的参数值 - public void delete(Integer id); -} -``` - -![image-20240312122323753](https://pic.bitday.top/i/2025/03/19/u6mu7z-2.png) - -上图参数值分离,有效防止SQL注入 - - - -**作用于多个字段** - -```java -@Mapper -public interface EmpMapper { - //会自动将生成的主键值,赋值给emp对象的id属性 - @Options(useGeneratedKeys = true,keyProperty = "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); -} -``` - -在 **`@Insert`** 注解中使用 `#{}` 来引用 `Emp` 对象的属性,MyBatis 会自动从 `Emp` 对象中提取相应的字段并绑定到 SQL 语句中的占位符。 - -`@Options(useGeneratedKeys = true, keyProperty = "id")` 这行配置表示,插入时自动生成的主键会赋值给 `Emp` 对象的 `id` 属性。 - -``` -// 调用 mapper 执行插入操作 -empMapper.insert(emp); - -// 现在 emp 对象的 id 属性会被自动设置为数据库生成的主键值 -System.out.println("Generated ID: " + emp.getId()); -``` - - - -### 查 - -查询案例: - -- **姓名:要求支持模糊匹配** -- 性别:要求精确匹配 -- 入职时间:要求进行范围查询 -- 根据最后修改时间进行降序排序 - -重点在于模糊查询时where name like '%#{name}%' 会报错。 - -解决方案: - -使用MySQL提供的字符串拼接函数:`concat('%' , '关键字' , '%')` - -**`CONCAT()`** 如果其中任何一个参数为 **`NULL`**,`CONCAT()` 返回 **`NULL`**,`Like NULL`会导致查询不到任何结果! - -`NULL`和`''`是完全不同的 - -```java -@Mapper -public interface EmpMapper { - - @Select("select * from emp " + - "where name like concat('%',#{name},'%') " + - "and gender = #{gender} " + - "and entrydate between #{begin} and #{end} " + - "order by update_time desc") - public List list(String name, Short gender, LocalDate begin, LocalDate end); - -} -``` - - - -### XML配置文件规范 - -使用Mybatis的注解方式,主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能,建议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中。 - -在Mybatis中使用XML映射文件方式开发,需要符合一定的规范: - -1. XML映射**文件的名称**与Mapper**接口名称**一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名) - -2. XML映射文件的**namespace属性**为Mapper接口**全限定名**一致 - -3. XML映射文件中sql语句的**id**与Mapper接口中的**方法名**一致,并保持返回类型一致。 - -![image-20221212153529732](https://pic.bitday.top/i/2025/03/19/u6su5s-2.png) - -\ - - select * from emp - where name like concat('%',#{name},'%') - and gender = #{gender} - and entrydate between #{begin} and #{end} - order by update_time desc - - ``` - - **`id="list"`**:指定查询方法的名称,应该与 Mapper 接口中的方法名称一致。 - - **`resultType="edu.whut.pojo.Emp"`**:`resultType` 只在 **查询操作** 中需要指定。指定查询结果映射的对象类型,这里是 `Emp` 类。 - - - -这里有bug!!! - -`concat('%',#{name},'%')`这里应该用`` ``标签对name是否为`NULL`或`''`进行判断 - - - -### 动态SQL - -#### SQL-if,where - -``:用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL。 - -~~~xml - - 要拼接的sql语句 - -~~~ - -``只会在子元素有内容的情况下才插入where子句,而且会自动去除子句的开头的AND或OR,**加了总比不加好** - -```java - -``` - - - -#### SQL-foreach - -Mapper 接口 - -```java -@Mapper -public interface EmpMapper { - //批量删除 - public void deleteByIds(List ids); -} -``` - -XML 映射文件 - -`` 标签用于遍历集合,常用于动态生成 SQL 语句中的 IN 子句、批量插入、批量更新等操作。 - -```java - - -``` - -`open="("`:这个属性表示,在*生成的 SQL 语句开始*时添加一个 左括号 `(`。 - -`close=")"`:这个属性表示,在生成的 SQL 语句结束时添加一个 右括号 `)`。 - -例:批量删除实现 - -```java - - DELETE FROM emp WHERE id IN - - #{id} - - -``` - -实现效果类似:`DELETE FROM emp WHERE id IN (1, 2, 3);` - - - ## 登录校验 ### 会话技术 diff --git a/自学/Java笔记本.md b/自学/Java笔记本.md index 600c9b7..4303a97 100644 --- a/自学/Java笔记本.md +++ b/自学/Java笔记本.md @@ -1450,25 +1450,39 @@ public class Main { **类路径**是JVM在运行时用来查找类文件和资源文件的一组目录或JAR包。在许多项目(例如Maven或Gradle项目)中,`src/main/resources`目录下的内容在编译时会被复制到输出目录(如`target/classes`),`src/main/java` 下编译后的 class 文件也会放到这里。 ```text -MyProject/ -├── src/ -│ └── main/ -│ └── java/ -│ └── com/ -│ └── example/ -│ └── Main.java -├── resources/ -│ ├── emp.xml +src/ +├── main/ +│ ├── java/ +│ │ └── com/ +│ │ └── example/ +│ │ └── App.java +│ └── resources/ +│ ├── application.yml +│ └── static/ +│ └── logo.png +└── test/ + ├── java/ + │ └── com/ + │ └── example/ + │ └── AppTest.java + └── resources/ + └── test-data.json + +映射到 target/ 后: + +target/ +├── classes/ ← 主代码和资源的输出根目录 +│ ├── com/ +│ │ └── example/ +│ │ └── App.class ← 编译自 src/main/java/com/example/App.java +│ ├── application.yml ← 复制自 src/main/resources/application.yml │ └── static/ -│ └── tt.img -└── target/ - └── classes/ - ├── com/ - │ └── example/ - │ └── Main.class - ├── emp.xml - └── static/ - └── tt.img +│ └── logo.png ← 复制自 src/main/resources/static/logo.png +└── test-classes/ ← 测试代码和测试资源的输出根目录 + ├── com/ + │ └── example/ + │ └── AppTest.class ← 编译自 src/test/java/com/example/AppTest.java + └── test-data.json ← 复制自 src/test/resources/test-data.json ``` diff --git a/自学/Maven.md b/自学/Maven.md new file mode 100644 index 0000000..8468890 --- /dev/null +++ b/自学/Maven.md @@ -0,0 +1,391 @@ +## Maven + +![image-20240229132137502](https://pic.bitday.top/i/2025/03/19/u6rxj1-2.png) + +![image-20240229133408054](https://pic.bitday.top/i/2025/03/19/u6tcqw-2.png) + +Maven仓库分为: + +- 本地仓库:自己计算机上的一个目录(用来存储jar包) +- 中央仓库:由Maven团队维护的全球唯一的。仓库地址:https://repo1.maven.org/maven2/ +- 远程仓库(私服):一般由公司团队搭建的私有仓库 + +POM文件导入依赖的时候,先看本地仓库有没有,没有就看私服,再没有就从中央仓库下载。 + + + +### Maven创建/导入项目 + +#### **创建Maven项目** + +![image-20250307174233390](https://pic.bitday.top/i/2025/03/19/u6u4ni-2.png) + +勾选 **Create from archetype**(可选),也可以选择 **maven-archetype-quickstart** 等模版。 + +点击 Next,填写 GAV 坐标 。 + +GroupId:标识组织或公司(通常使用域名反写,如 `com.example`) + +ArtifactId:标识具体项目或模块(如 `my-app`、`spring-boot-starter-web`)。 + +Version:标识版本号(如 `1.0-SNAPSHOT`、`2.7.3`) + + + +#### 导入Maven项目 + +**(一)单独的Maven项目** + +打开 IDEA,在主界面选择 Open(或者在菜单栏选择 File -> Open)。 + +在文件选择对话框中,定位到已有项目的根目录(包含 `pom.xml` 的目录)。 + +选择该目录后,IDEA 会检测到 `pom.xml` 并询问是否导入为 Maven 项目,点击 **OK** 或 **Import** 即可。 + +IDEA 会自动解析 `pom.xml`,下载依赖并构建项目结构。 + + + +**(二)在现有Maven项目中导入独立的Maven项目** + +在已经打开的 IDEA 窗口中,使用 **File -> New -> Module from Existing Sources...** + +选择待导入项目的根目录(其中包含 `pom.xml`),IDEA 会将其导入为同一个工程下的另一个模块(Module)。 + +它们 看起来在一个工程里了,但**仍然是两个独立的** Maven 模块。 + + + +**(三)两个模块属于同一个工程下** + +可以用一个父pom进行统一管理! + +1.新建一个上层目录,如下,MyProject1和MyProject2的内容拷贝过去。 + +```xml +ParentProject/ +├── pom.xml <-- 父模块(聚合模块) +├── MyProject1/ <-- 子模块1 +│ └── pom.xml +└── MyProject2/ <-- 子模块2 + └── pom.xml +``` + +2.创建父级pom + +父模块 `pom.xml` 示例: + +```xml + + 4.0.0 + com.example + ParentProject + 1.0-SNAPSHOT + pom //必写 + + + MyProject1 //必写 + MyProject2 + + + +``` + +3.修改子模块 `pom.xml` ,加上: + +```xml + + com.example + ParentProject + 1.0-SNAPSHOT + ../pom.xml + +``` + +如果子模块中无需与父级不同的配置,**可以不写**,就自动继承父级配置;若写了同名配置,则表示你想要**覆盖或合并**父级配置。 + +4.File -> Open选择父级的pom,会自动导入其下面两个项目。 + +**但是,仅仅这样无法让模块之间产生联动!需要在此基础上进行(四)的操作!** + + + +**(四)通过 Maven 依赖引用** + +如果你的两个模块之间存在依赖关系(如第一个模块需要使用第二个模块的类)还必须在 MyProject1 的 POM 里**显式声明**对 MyProject2 的依赖。 + + MyProject1的pom.xml: + +```xml + + + + com.example + ParentProject + 1.0-SNAPSHOT + ../pom.xml + + + MyProject1 + jar + + + + + com.example + MyProject2 + + + + + +``` + +**如何打包?** + +- 在**父 POM 根目录**执行 `mvn clean package`/`mvn clean install`。 +- 先构建 MyProject2(因为 MyProject1 依赖它) + +- 父 POM 自身不产物,模块的 JAR 都在各自的 `target/` 下。 + + + +### Maven坐标 + +什么是坐标? + +* Maven中的坐标是 == 资源的唯一标识 == 通过该坐标可以唯一定位资源位置 +* 使用坐标来定义项目或引入项目中需要的依赖 + +![image-20240302131843540](https://pic.bitday.top/i/2025/03/19/u6ps37-2.png) + +### 依赖管理 + +可以到mvn的中央仓库(https://mvnrepository.com/)中搜索获取依赖的坐标信息 + +```xml + + + + ch.qos.logback + logback-classic + 1.2.11 + + + + junit + junit + 4.12 + test + + +``` + +更改之后可以在界面上看到一个maven刷新按钮,点击一下就开始联网下载依赖了,成功后可以看到 + +![image-20240302133241227](https://pic.bitday.top/i/2025/03/19/u6szry-2.png) + +#### 排除依赖 + +A依赖B,B依赖C,如果A不想将C依赖进来,可以同时排除C,被排除的资源**无需指定版本**。 + +```xml + + com.itheima + maven-projectB + 1.0-SNAPSHOT + + + + + junit + junit + + + +``` + +#### 依赖范围 + +| **scope**值 | **主程序** | **测试程序** | **打包(运行)** | **范例** | +| --------------- | ---------- | ------------ | ---------------- | ----------- | +| compile(默认) | Y | Y | Y | log4j | +| test | - | Y | - | junit | +| provided | Y | Y | - | servlet-api | +| runtime | - | Y | Y | jdbc驱动 | + +注意!!!这里的scope如果是`test`,那么它的作用范围在`src/test/java`下,在`src/main/java`下无法导包! + + + +### Maven 多模块工程 + +父 POM 用 `` 锁版本,子模块按需在 `` 中声明自己用的依赖。对“真正所有模块都要”的依赖,可以放到父 POM 顶层 ``,让它们自动继承。 + +父 POM(pom.xml): + +```xml + + 4.0.0 + com.example + parent-project + 1.0.0 + pom + + + + service-a + service-b + + + + + + + + org.springframework.boot + spring-boot-starter-web + 2.7.3 + + + + org.projectlombok + lombok + 1.18.20 + + + + + + + + org.projectlombok + lombok + + provided + + + + +``` + + + +子pom + +```xml + + 4.0.0 + + + + com.example + parent-project + 1.0.0 + ../pom.xml + + + service-a + jar + + + + + org.springframework.boot + spring-boot-starter-web + + + + + + +``` + + + +父pom的`pom`表示它只是一个 POM 模块,不会产出任何可执行的 JAR。 + +子pom的`../pom.xml`告诉 Maven 去哪个相对路径找父 POM 文件 + + + +注意:如果子模块A依赖于B模块,那么B模块中的依赖会传递给A,比如B中引入了`org.apache.httpcomponents`,那么A模块的类中可以直接import这个库。反过来不行!大坑! + + + +### Maven生命周期 + +主要关注以下几个: + +• clean:移除上一次构建生成的文件 (Target文件夹) + +• compile:编译 `src/main/java` 中的 Java 源文件至 `target/classes` + +• test:使用合适的单元测试框架运行测试(junit) + +• package:将编译后的文件打包,如:jar、war等 + +• install:将打包后的产物(如 `jar`)安装到本地仓库 + +**后面的生命周期执行的时候会自动执行前面所有生命周期!** + + + +#### compile: + +```text +src/ +├── main/ +│ ├── java/ +│ │ └── com/ +│ │ └── example/ +│ │ └── App.java +│ └── resources/ +│ ├── application.yml +│ └── static/ +│ └── logo.png +└── test/ + ├── java/ + │ └── com/ + │ └── example/ + │ └── AppTest.java + └── resources/ + └── test-data.json + +映射到 target/ 后: + +target/ +├── classes/ ← 主代码和资源的输出根目录 +│ ├── com/ +│ │ └── example/ +│ │ └── App.class ← 编译自 src/main/java/com/example/App.java +│ ├── application.yml ← 复制自 src/main/resources/application.yml +│ └── static/ +│ └── logo.png ← 复制自 src/main/resources/static/logo.png +└── test-classes/ ← 测试代码和测试资源的输出根目录 + ├── com/ + │ └── example/ + │ └── AppTest.class ← 编译自 src/test/java/com/example/AppTest.java + └── test-data.json ← 复制自 src/test/resources/test-data.json + +``` + + + +#### test: + +**扫描** `src/test/java` 下所有符合默认命名规则的测试类: + +- `**/Test*.java` +- `**/*Test.java` +- `**/*TestCase.java` + +**编译** 这些测试类到 `target/test-classes`。 + +**逐个执行**(默认是串行)所有这些编译后的测试类。 \ No newline at end of file diff --git a/自学/Mybatis.md b/自学/Mybatis.md new file mode 100644 index 0000000..a82d842 --- /dev/null +++ b/自学/Mybatis.md @@ -0,0 +1,403 @@ +## Mybatis + +### 快速创建 + +![image-20240307125505211](https://pic.bitday.top/i/2025/03/19/u6pfoj-2.png) + +1. 创建springboot工程(Spring Initializr),并导入 mybatis的起步依赖、mysql的驱动包。创建用户表user,并创建对应的实体类User + ![image-20240307125820685](https://pic.bitday.top/i/2025/03/19/u6q96d-2.png) + +2. 在springboot项目中,可以编写main/resources/application.properties文件,配置数据库连接信息。 + + ``` + #驱动类名称 + spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver + #数据库连接的url + spring.datasource.url=jdbc:mysql://localhost:3306/mybatis + #连接数据库的用户名 + spring.datasource.username=root + #连接数据库的密码 + spring.datasource.password=1234 + ``` + +3. 在引导类所在包下,在创建一个包 mapper。在mapper包下创建一个接口 UserMapper + + ![image-20240307132356616](https://pic.bitday.top/i/2025/03/19/u6qtz4-2.png) + +@Mapper注解:表示是mybatis中的Mapper接口 + +​ -程序运行时:框架会自动生成接口的**实现类对象(代理对象)**,并交给Spring的IOC容器管理 + +@Select注解:代表的就是select查询,用于书写select查询语句 + +```java +@Mapper +public interface UserMapper { + //查询所有用户数据 + @Select("select * from user") + public List list(); +} +``` + + + +### 数据库连接池 + +数据库连接池是一个容器,负责管理和分配数据库连接(`Connection`)。 + +- 在程序启动时,连接池会创建一定数量的数据库连接。 +- 客户端在执行 SQL 时,从连接池获取连接对象,执行完 SQL 后,将连接归还给连接池,以供其他客户端复用。 +- 如果连接对象长时间空闲且超过预设的最大空闲时间,连接池会自动释放该连接。 + +**优势**:避免频繁创建和销毁连接,提高数据库访问效率。 + + + +Druid(德鲁伊) + +* Druid连接池是阿里巴巴开源的数据库连接池项目 + +* 功能强大,性能优秀,是Java语言最好的数据库连接池之一 + +把默认的 Hikari 数据库连接池切换为 Druid 数据库连接池: + +1. 在pom.xml文件中引入依赖 + + ```xml + + + com.alibaba + druid-spring-boot-starter + 1.2.8 + + ``` + +2. 在application.properties中引入数据库连接配置 + + ```properties + spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver + spring.datasource.druid.url=jdbc:mysql://localhost:3306/mybatis + spring.datasource.druid.username=root + spring.datasource.druid.password=123456 + ``` + + + +### SQL注入问题 + +SQL注入:由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。 + +在Mybatis中提供的参数占位符有两种:${...} 、#{...} + +- #{...} + - 执行SQL时,会将#{…}替换为?,生成预编译SQL,会自动设置参数值 + - 使用时机:参数传递,都使用#{…} + +- ${...} + - 拼接SQL。直接将参数拼接在SQL语句中,**存在SQL注入问题** + - 使用时机:如果对表名、列表进行动态设置时使用 + + + +### 日志输出 + +只建议开发环境使用:在Mybatis当中我们可以借助日志,查看到sql语句的执行、执行传递的参数以及执行结果 + +1. 打开application.properties文件 + +2. 开启mybatis的日志,并指定输出到控制台 + +```java +#指定mybatis输出日志的位置, 输出控制台 +mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl +``` + + + +### 驼峰命名法 + +在 Java 项目中,数据库表字段名一般使用 **下划线命名法**(snake_case),而 Java 中的变量名使用 **驼峰命名法**(camelCase)。 + +- [x] **小驼峰命名(lowerCamelCase)**: + +- 第一个单词的首字母小写,后续单词的首字母大写。 +- **例子**:`firstName`, `userName`, `myVariable` + +**大驼峰命名(UpperCamelCase)**: + +- 每个单词的首字母都大写,通常用于类名或类型名。 +- **例子**:`MyClass`, `EmployeeData`, `OrderDetails` + + + +表中查询的数据封装到实体类中 + +- 实体类属性名和数据库表查询返回的**字段名一致**,mybatis会自动封装。 +- 如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装。 + +![image-20221212103124490](https://pic.bitday.top/i/2025/03/19/u6o894-2.png) + +解决方法: + +1. 起别名 +2. 结果映射 +3. **开启驼峰命名** +4. **属性名和表中字段名保持一致** + + + +**开启驼峰命名(推荐)**:如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射 + +> 驼峰命名规则: abc_xyz => abcXyz +> +> - 表中字段名:abc_xyz +> - 类中属性名:abcXyz + + + +### 推荐的完整配置: + +```yaml +mybatis: + #mapper配置文件 + mapper-locations: classpath:mapper/*.xml + type-aliases-package: com.sky.entity + configuration: + #开启驼峰命名 + map-underscore-to-camel-case: true +``` + +`type-aliases-package: com.sky.entity`把 `com.sky.entity` 包下的所有类都当作别名注册,XML 里就可以直接写 `` 而不用写全限定名。可以多添加几个包,用逗号隔开。 + + + +### 增删改 + +- **增删改通用!:返回值为int时,表示影响的记录数,一般不需要可以设置为void!** + +**作用于单个字段** + +```java +@Mapper +public interface EmpMapper { + //SQL语句中的id值不能写成固定数值,需要变为动态的数值 + //解决方案:在delete方法中添加一个参数(用户id),将方法中的参数,传给SQL语句 + /** + * 根据id删除数据 + * @param id 用户id + */ + @Delete("delete from emp where id = #{id}")//使用#{key}方式获取方法中的参数值 + public void delete(Integer id); +} +``` + +![image-20240312122323753](https://pic.bitday.top/i/2025/03/19/u6mu7z-2.png) + +上图参数值分离,有效防止SQL注入 + + + +**作用于多个字段** + +```java +@Mapper +public interface EmpMapper { + //会自动将生成的主键值,赋值给emp对象的id属性 + @Options(useGeneratedKeys = true,keyProperty = "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); +} +``` + +在 **`@Insert`** 注解中使用 `#{}` 来引用 `Emp` 对象的属性,MyBatis 会自动从 `Emp` 对象中提取相应的字段并绑定到 SQL 语句中的占位符。 + +`@Options(useGeneratedKeys = true, keyProperty = "id")` 这行配置表示,插入时自动生成的主键会赋值给 `Emp` 对象的 `id` 属性。 + +``` +// 调用 mapper 执行插入操作 +empMapper.insert(emp); + +// 现在 emp 对象的 id 属性会被自动设置为数据库生成的主键值 +System.out.println("Generated ID: " + emp.getId()); +``` + + + +### 查 + +查询案例: + +- **姓名:要求支持模糊匹配** +- 性别:要求精确匹配 +- 入职时间:要求进行范围查询 +- 根据最后修改时间进行降序排序 + +重点在于模糊查询时where name like '%#{name}%' 会报错。 + +解决方案: + +使用MySQL提供的字符串拼接函数:`concat('%' , '关键字' , '%')` + +**`CONCAT()`** 如果其中任何一个参数为 **`NULL`**,`CONCAT()` 返回 **`NULL`**,`Like NULL`会导致查询不到任何结果! + +`NULL`和`''`是完全不同的 + +```java +@Mapper +public interface EmpMapper { + + @Select("select * from emp " + + "where name like concat('%',#{name},'%') " + + "and gender = #{gender} " + + "and entrydate between #{begin} and #{end} " + + "order by update_time desc") + public List list(String name, Short gender, LocalDate begin, LocalDate end); + +} +``` + + + +### XML配置文件规范 + +使用Mybatis的注解方式,主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能,建议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中。 + +在Mybatis中使用XML映射文件方式开发,需要符合一定的规范: + +1. XML映射**文件的名称**与Mapper**接口名称**一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名) + +2. XML映射文件的**namespace属性**为Mapper接口**全限定名**一致 + +3. XML映射文件中sql语句的**id**与Mapper接口中的**方法名**一致,并保持返回类型一致。 + +![image-20221212153529732](https://pic.bitday.top/i/2025/03/19/u6su5s-2.png) + +\ + + select * from emp + where name like concat('%',#{name},'%') + and gender = #{gender} + and entrydate between #{begin} and #{end} + order by update_time desc + + ``` + + **`id="list"`**:指定查询方法的名称,应该与 Mapper 接口中的方法名称一致。 + + **`resultType="edu.whut.pojo.Emp"`**:`resultType` 只在 **查询操作** 中需要指定。指定查询结果映射的对象类型,这里是 `Emp` 类。 + + + +这里有bug!!! + +`concat('%',#{name},'%')`这里应该用`` ``标签对name是否为`NULL`或`''`进行判断 + + + +### 动态SQL + +#### SQL-if,where + +``:用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL。 + +~~~xml + + 要拼接的sql语句 + +~~~ + +``只会在子元素有内容的情况下才插入where子句,而且会自动去除子句的开头的AND或OR,**加了总比不加好** + +```java + +``` + + + +#### SQL-foreach + +Mapper 接口 + +```java +@Mapper +public interface EmpMapper { + //批量删除 + public void deleteByIds(List ids); +} +``` + +XML 映射文件 + +`` 标签用于遍历集合,常用于动态生成 SQL 语句中的 IN 子句、批量插入、批量更新等操作。 + +```java + + +``` + +`open="("`:这个属性表示,在*生成的 SQL 语句开始*时添加一个 左括号 `(`。 + +`close=")"`:这个属性表示,在生成的 SQL 语句结束时添加一个 右括号 `)`。 + +例:批量删除实现 + +```java + + DELETE FROM emp WHERE id IN + + #{id} + + +``` + +实现效果类似:`DELETE FROM emp WHERE id IN (1, 2, 3);` \ No newline at end of file diff --git a/自学/linux服务器.md b/自学/linux服务器.md index 24977f6..c102e68 100644 --- a/自学/linux服务器.md +++ b/自学/linux服务器.md @@ -748,6 +748,8 @@ kill xxx ## File Browser 文件分享 +https://github.com/filebrowser/filebrowser + [Docker 部署 File Browser 文件管理系统_filebrowser docker-CSDN博客](https://blog.csdn.net/qq_41906909/article/details/144726676) 1.创建数据目录 diff --git a/自学/苍穹外卖.md b/自学/苍穹外卖.md index deb4358..c26fdf5 100644 --- a/自学/苍穹外卖.md +++ b/自学/苍穹外卖.md @@ -1,5 +1,9 @@ # 苍穹外卖 +## 踩坑总结 + + + ## 项目简介 ### 整体介绍 @@ -593,64 +597,6 @@ public class EmpController { ### 文件上传 -#### 本地存储 - -文件上传时在服务端会产生一个临时文件,请求响应完成之后,这个临时文件被自动删除,并没有进行保存。下面呢,我们就需要完成将上传的文件保存在服务器的本地磁盘上。 - -代码实现: - -1. 在服务器本地磁盘上创建images目录,用来存储上传的文件(例:E盘创建images目录) -2. 使用MultipartFile类提供的API方法,把临时文件转存到本地磁盘目录下 - -> MultipartFile 常见方法: -> -> - String getOriginalFilename(); //获取原始文件名 -> - void transferTo(File dest); //将接收的文件转存到磁盘文件中 -> - long getSize(); //获取文件的大小,单位:字节 -> - byte[] getBytes(); //获取文件内容的字节数组 -> - InputStream getInputStream(); //获取接收到的文件内容的输入流 - -```java -@Slf4j -@RestController -public class UploadController { - - @PostMapping("/upload") - public Result upload(String username, Integer age, MultipartFile image) throws IOException { - log.info("文件上传:{},{},{}",username,age,image); - - //获取原始文件名 - String originalFilename = image.getOriginalFilename(); - - //构建新的文件名 - String extname = originalFilename.substring(originalFilename.lastIndexOf("."));//文件扩展名 - String newFileName = UUID.randomUUID().toString()+extname;//随机名+文件扩展名 - - //将文件存储在服务器的磁盘目录 - image.transferTo(new File("E:/images/"+newFileName)); - - return Result.success(); - } - -} -``` - -在SpringBoot中,文件上传时默认单个文件最大大小为1M - -那么如果需要上传大文件,可以在application.properties进行如下配置: - -```java -#配置单个文件最大上传大小 -spring.servlet.multipart.max-file-size=10MB - -#配置单个请求最大上传大小(一次请求可以上传多个文件) -spring.servlet.multipart.max-request-size=100MB -``` - -**不推荐!** - - - #### 阿里云OSS存储 pom文件中添加如下依赖: @@ -684,60 +630,65 @@ pom文件中添加如下依赖: 上传文件的工具类 ```java -package edu.whut.utils; -import com.aliyun.oss.OSS; -import com.aliyun.oss.OSSClientBuilder; -import com.aliyun.oss.common.auth.CredentialsProviderFactory; -import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider; -import com.aliyun.oss.model.PutObjectRequest; -import com.aliyun.oss.model.PutObjectResult; -import com.aliyuncs.exceptions.ClientException; -import org.springframework.stereotype.Component; -import org.springframework.web.multipart.MultipartFile; - -import java.io.*; -import java.util.UUID; - - /** * 阿里云 OSS 工具类 */ -@Component -public class AliOSSUtils { +public class AliOssUtil { - private String endpoint = "https://oss-cn-hangzhou.aliyuncs.com"; - private String bucketName = "zyjavaweb"; + private String endpoint; + private String accessKeyId; + private String accessKeySecret; + private String bucketName; /** - * 实现上传图片到OSS + * 文件上传 + * + * @param bytes + * @param objectName + * @return */ - public String upload(MultipartFile file) throws IOException, ClientException { + public String upload(byte[] bytes, String objectName) { - InputStream inputStream = file.getInputStream(); - // 避免文件覆盖 - String originalFilename = file.getOriginalFilename(); - String extname = originalFilename.substring(originalFilename.lastIndexOf("."));//文件扩展名 - String fileName = UUID.randomUUID().toString() + extname; + // 创建OSSClient实例。 + OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); - //上传文件到 OSS - EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider(); - OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider); - PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, fileName, inputStream); - PutObjectResult result = ossClient.putObject(putObjectRequest); + try { + // 创建PutObject请求。 + ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes)); + } catch (OSSException oe) { + System.out.println("Caught an OSSException, which means your request made it to OSS, " + + "but was rejected with an error response for some reason."); + System.out.println("Error Message:" + oe.getErrorMessage()); + System.out.println("Error Code:" + oe.getErrorCode()); + System.out.println("Request ID:" + oe.getRequestId()); + System.out.println("Host ID:" + oe.getHostId()); + } catch (ClientException ce) { + System.out.println("Caught an ClientException, which means the client encountered " + + "a serious internal problem while trying to communicate with OSS, " + + "such as not being able to access the network."); + System.out.println("Error Message:" + ce.getMessage()); + } finally { + if (ossClient != null) { + ossClient.shutdown(); + } + } - //文件访问路径 - String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName; - // 关闭ossClient - ossClient.shutdown(); - return url;// 把上传到oss的路径返回 + //文件访问路径规则 https://BucketName.Endpoint/ObjectName + StringBuilder stringBuilder = new StringBuilder("https://"); + stringBuilder + .append(bucketName) + .append(".") + .append(endpoint) + .append("/") + .append(objectName); + + log.info("文件上传到:{}", stringBuilder.toString()); + + return stringBuilder.toString(); } - } - ``` -使用时传入MultipartFile类型的文件 - ### 加密算法 @@ -1256,54 +1207,97 @@ public class SpringDataRedisTest { - 在Java程序中发送HTTP请求 - 接收响应数据 +**HttpClient的maven坐标:** + +```xml + + org.apache.httpcomponents + httpclient + 4.5.13 + +``` + +**HttpClient的核心API:** + +- HttpClient:Http客户端对象类型,使用该类型对象可发起Http请求。 +- HttpClients:可认为是构建器,可创建HttpClient对象。 +- CloseableHttpClient:实现类,实现了HttpClient接口。 +- HttpGet:Get方式请求类型。 +- HttpPost:Post方式请求类型。 + **HttpClient发送请求步骤:** - 创建HttpClient对象 - 创建Http请求对象 - 调用HttpClient的execute方法发送请求 - +**测试用例** ```java -public static String doGet(String url,Map paramMap){ - // 创建Httpclient对象 +@SpringBootTest +public class HttpClientTest { + + /** + * 测试通过httpclient发送GET方式的请求 + */ + @Test + public void testGET() throws Exception{ + //创建httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); - String result = ""; - CloseableHttpResponse response = null; + //创建请求对象 + HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status"); - try{ - URIBuilder builder = new URIBuilder(url); - if(paramMap != null){ - for (String key : paramMap.keySet()) { - builder.addParameter(key,paramMap.get(key)); //将传入的参数 `paramMap` 中的每一个键值对转换为 URL 的查询字符串形式 - } - } - URI uri = builder.build(); + //发送请求,接受响应结果 + CloseableHttpResponse response = httpClient.execute(httpGet); - //创建GET请求 - HttpGet httpGet = new HttpGet(uri); + //获取服务端返回的状态码 + int statusCode = response.getStatusLine().getStatusCode(); + System.out.println("服务端返回的状态码为:" + statusCode); - //发送请求 - response = httpClient.execute(httpGet); + HttpEntity entity = response.getEntity(); + String body = EntityUtils.toString(entity); + System.out.println("服务端返回的数据为:" + body); - //判断响应状态 - if(response.getStatusLine().getStatusCode() == 200){ - result = EntityUtils.toString(response.getEntity(),"UTF-8"); - } - }catch (Exception e){ - e.printStackTrace(); - }finally { - try { - response.close(); - httpClient.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - return result; + //关闭资源 + response.close(); + httpClient.close(); } + @Test + public void testPOST() throws Exception{ + // 创建httpclient对象 + CloseableHttpClient httpClient = HttpClients.createDefault(); + + //创建请求对象 + HttpPost httpPost = new HttpPost("http://localhost:8080/admin/employee/login"); + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("username","admin"); + jsonObject.put("password","123456"); + + StringEntity entity = new StringEntity(jsonObject.toString()); + //指定请求编码方式 + entity.setContentEncoding("utf-8"); + //数据格式 + entity.setContentType("application/json"); + httpPost.setEntity(entity); + + //发送请求 + CloseableHttpResponse response = httpClient.execute(httpPost); + + //解析返回结果 + int statusCode = response.getStatusLine().getStatusCode(); + System.out.println("响应码为:" + statusCode); + + HttpEntity entity1 = response.getEntity(); + String body = EntityUtils.toString(entity1); + System.out.println("响应数据为:" + body); + + //关闭资源 + response.close(); + httpClient.close(); + } +} ```