SpringCloud学习(补漏)
学习文档地址
https://b11et3un53m.feishu.cn/wiki/FJAnwOhpIihMkLkOKQocdWZ7nUc
1 MybatisPlus
https://baomidou.com/reference/annotation/
1.1 使用的基本步骤
1.2 常用注解
1.3 常用配置
1.4 核心功能
1.4.1 条件构造器
用法及建议
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.select("id", "username", "info", "balance")
.like("username", "o")
.ge("balance", 1000);
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
1.4.2 自定义sql
xml
<update id="updateBalanceByIds">
update user set balance = balance - #{amount}
${ew.customSqlSegment}
</update>
mapper
void updateBalanceByIds(@Param(Constants.WRAPPER) QueryWrapper<User> wrapper,@Param("amount") int amount);
逻辑
List<Long> ids = List.of(1L, 3L, 4L);
int amount = 200;
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.in("id", ids);
// 调用自定义
userMapper.updateBalanceByIds(wrapper, amount);
基于wrapper的多表关联查询
1.4.3 Service接口
图示
举例:
1、创建IUserService接口并且继承IService接口
2、定义UserServiceImpl实现类,实现IUserService接口,并继承ServiceImpl实现类
3、在UserServiceImpl就可以直接调用MP(MybatisPlus)相关的方法,如getById;
UserServiceImpl中对应的mapper就是baseMapper,这个就等于是注入的userMapper=>这个是ServiceImpl已经有的,可以直接拿来用
关于Autowired注入的调整
1.4.3.1 IService中的Lambda查询
@Override
public List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {
return lambdaQuery()
.like(name!=null, User::getUsername, name)
.eq(status!=null, User::getStatus, status)
.ge(minBalance!=null, User::getBalance, minBalance)
.le(maxBalance!=null, User::getBalance, maxBalance)
.list();
}
1.4.3.2 IService中的Lambda更新
lambdaUpdate()
.set(User::getBalance, user.getBalance() - money)
.set(user.getBalance() - money == 0,User::getStatus, 2)
.eq(User::getId, id)
.eq(User::getBalance, user.getBalance()) // 乐观锁:先比较再更新
.update();
1.4.3.3 批量新增
&rewriteBatchedStatements=true
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
1.5 扩展功能
1.5.1 MyBatisPlus插件使用
jdbc:mysql://127.0.0.1:3306/mp?useSSL=false&serverTimezone=Asia/Shanghai
1.5.2 DB静态工具–高版本的mp才有
stream流的map提取后收集
users.stream().map(User::getId).collect(Collectors.toList());
stream流的根据某个返回值分组收集
addressVOS.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
@Override
public List<UserVO> queryUserAndAddressByIds(List<Long> ids) {
// 查用户
List<User> users = listByIds(ids);
if (CollUtil.isEmpty(users)) {
return Collections.emptyList();
}
// 获取用户id集合
List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
// 根据id集合查所有地址
List<Address> addressList = Db.lambdaQuery(Address.class)
.in(Address::getUserId, userIds).list();
// 将地址转vo
List<AddressVO> addressVOS = BeanUtil.copyToList(addressList, AddressVO.class);
// 所有vo地址根据id分类
Map<Long, List<AddressVO>> AddressVOMap = new HashMap<>(0);
if(CollUtil.isNotEmpty(addressList)) {
AddressVOMap = addressVOS.stream()
.collect(Collectors.groupingBy(AddressVO::getUserId));
}
// 转vo返回
List<UserVO> list = new ArrayList<>(users.size());
for (User user : users) {
// 转vo
UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
userVO.setAddresses(AddressVOMap.get(user.getId()));
list.add(userVO);
}
return list;
}
1.5.3 逻辑删除
1.5.3 枚举处理器
1.5.4 JSON处理器
(1)@TableName(autoResultMap = true)
(2)
@TableField(typeHandler = JacksonTypeHandler.class)
private UserInfo info;
1.6 分页功能
1.6.1 配置(先配置,底层是拦截器拦截)
1.6.2 通用分页实体
(1)先定义一个通用实体
(2)需要查询的实体继承(1)
@Override
public PageDTO<UserVO> queryUsersPage(UserQuery query) {
String name = query.getName();
Integer status = query.getStatus();
Page<User> page = Page.of(query.getPageNo(), query.getPageSize());
if (StrUtil.isNotBlank(query.getSortBy())){
page.addOrder(new OrderItem().setColumn(query.getSortBy()).setAsc(query.getIsAsc()));// query.getSortBy(),query.getIsAsc()
}else{
page.addOrder(new OrderItem().setColumn("update_time").setAsc(false));
}
Page<User> p = lambdaQuery()
.like(name != null, User::getUsername, name)
.eq(status != null, User::getStatus, status)
.page(page);
PageDTO<UserVO> pageDTO = new PageDTO<>();
pageDTO.setTotal(p.getTotal()); // 条数
pageDTO.setPages(p.getPages()); // 页面
List<User> records = p.getRecords();
if(CollUtil.isEmpty(records)){
pageDTO.setList(Collections.emptyList());
}else{
pageDTO.setList(BeanUtil.copyToList(records, UserVO.class));
}
return pageDTO;
}
1.6.3 基于分页和封装条件编写通用MP分页实体
PageQuery – 泛型
package com.itheima.mp.query;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.mp.domain.po.User;
import io.swagger.annotations.ApiModel;
import lombok.Data;
@Data
@ApiModel(value = "分页查询实体")
public class PageQuery {
private Integer pageNo = 1;
private Integer pageSize = 5;
private String sortBy;
private Boolean isAsc = true;
public <T>Page<T> toMpPage(OrderItem...items){
Page<T> page = Page.of(pageNo, pageSize);
if (StrUtil.isNotBlank(sortBy)){
page.addOrder(new OrderItem().setColumn(sortBy).setAsc(isAsc));// query.getSortBy(),query.getIsAsc()
}else if(items != null){
page.addOrder(items);
}
return page;
}
public <T>Page<T> toMpPage(String column, boolean isAsc){
return toMpPage(new OrderItem().setColumn(column).setAsc(isAsc));
}
public <T>Page<T> toMpPageDefaultSortByCreateTime(){
return toMpPage(new OrderItem().setColumn("create_time").setAsc(isAsc));
}
public <T>Page<T> toMpPageDefaultSortByUpdateTime(){
return toMpPage(new OrderItem().setColumn("update_time").setAsc(isAsc));
}
}
PageDTO – 泛型加函数式接口
package com.itheima.mp.domain.dto;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.UserVO;
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "分页查询结果")
public class PageDTO<T> {
private Long total;
private Long pages;
private List<T> list;
// 其他对象转PageDTO对象,使用static变为静态方法,让别人创建时就可以调用
// <PO, VO>不可以放在static之前,是因为类的泛型参数是在类实例化时确定的,而静态方法在实例化之前就可以被调用,因此无法直接引用类的泛型参数
public static <PO, VO> PageDTO<VO> of(Page<PO> p, Class<VO> voClass){
PageDTO<VO> pageDTO = new PageDTO<>();
pageDTO.setTotal(p.getTotal()); // 条数
pageDTO.setPages(p.getPages()); // 页面
List<PO> records = p.getRecords();
if(CollUtil.isEmpty(records)){
pageDTO.setList(Collections.emptyList());
}else{
// 变量名一样的PO到VO的转换
pageDTO.setList(BeanUtil.copyToList(records, voClass)); // VO是没有.class的,所以要传参进来
}
return pageDTO;
}
// 变量名不一样的PO到VO的转换
// 那就要传行为,也就是一个行为即函数,那就要传一个函数式接口了 Function
public static <PO, VO> PageDTO<VO> of(Page<PO> p, Function<PO, VO> convertor){
PageDTO<VO> pageDTO = new PageDTO<>();
pageDTO.setTotal(p.getTotal()); // 条数
pageDTO.setPages(p.getPages()); // 页面
List<PO> records = p.getRecords();
if(CollUtil.isEmpty(records)){
pageDTO.setList(Collections.emptyList());
}else{
// 利用流处理,map()里的传参就是函数式接口,之后再收集为list
pageDTO.setList(records.stream().map(convertor).collect(Collectors.toList())); // VO是没有.class的,所以要传参进来
}
return pageDTO;
}
}
缺点:
这是定义在两个类内部了,也就是与MP强耦合,
可以单独写方法,然后写实体与MP转换的,以后有别的,就再写
1.6.2简化结果:
2 Docker
https://b11et3un53m.feishu.cn/wiki/MWQIw4Zvhil0I5ktPHwcoqZdnec
2.1 MobaXterm
2.2 docker安装
Docker安装3个命令搞定,其他的都太麻烦了,命令开始后基本选第一个就都完事了
sudo yum install curl
bash <(curl -sSL https://gitee.com/SuperManito/LinuxMirrors/raw/main/ChangeMirrors.sh)
bash <(curl -sSL https://linuxmirrors.cn/docker.sh)
启动和校验
# 启动Docker
systemctl start docker
# 停止Docker
systemctl stop docker
# 重启
systemctl restart docker
# 设置开机自启
systemctl enable docker
# 执行docker ps命令,如果不报错,说明安装启动成功
docker ps
2.3 部署mysql
docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql
命令解读
镜像源问题
https://blog.csdn.net/qq_62404706/article/details/141687367?spm=1001.2014.3001.5501
Navicat查看
镜像与容器
2.4 常见命令
docker ps 查看是否有docker容器运行,不要总docker run,这样的话是在创建容器
案例
// 查看 https://docker.fxxk.dedyn.io/_/nginx
// 拉取
docker pull nginx
// 查看
docker images
// 保存镜像
docker save --help
docker save -o nginx.tar nginx:latest //nginx:latest :后面是版本号(必要)
// 删除镜像
docker rmi nginx:latest
// 因为保存了nginx.tar 可以load回来
docker load -i nginx.tar
// 运行nginx -d是后台运行 -e 环境变量,没有不写就行
docker run -d --name nginx -p 80:80 nginx
//ps 也可以加格式化方式访问,格式会更加清爽 加 -a 查看不止运行中的
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"
// 停止
docker stop nginx
// 进入容器
docker exec -it nginx bash
// 删除容器
docker rm nginx // 要先停止运行或者加 -f
起别命
# 修改/root/.bashrc文件
vi /root/.bashrc
内容如下:
# .bashrc
# User specific aliases and functions
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
alias dps='docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"'
alias dis='docker images'
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
执行命令,让别名生效
source /root/.bashrc
2.5 数据卷
容器提供程序的运行环境,但是程序运行产生的数据、程序运行依赖的配置都应该与容器解耦。
定义
数据卷(volume)是一个虚拟目录,是容器内目录与宿主机目录之间映射的桥梁。
以Nginx为例,我们知道Nginx中有两个关键的目录:
html
:放置一些静态资源conf
:放置配置文件
如果我们要让Nginx代理我们的静态资源,最好是放到html
目录;如果我们要修改Nginx的配置,最好是找到conf
下的nginx.conf
文件。
但遗憾的是,容器运行的Nginx所有的文件都在容器内部。所以我们必须利用数据卷将两个目录与宿主机目录关联,方便我们操作。如图:
在上图中:
- 我们创建了两个数据卷:
conf
、html
- Nginx容器内部的
conf
目录和html
目录分别与两个数据卷关联。 - 而数据卷conf和html分别指向了宿主机的
/var/lib/docker/volumes/conf/_data
目录和/var/lib/docker/volumes/html/_data
目录
这样以来,容器内的conf
和html
目录就 与宿主机的conf
和html
目录关联起来,我们称为挂载。此时,我们操作宿主机的/var/lib/docker/volumes/html/_data
就是在操作容器内的/usr/share/nginx/html/_data
目录。只要我们将静态资源放入宿主机对应目录,就可以被Nginx代理了。
小提示:
<font style="background-color:rgb(255,245,235);">/var/lib/docker/volumes</font>
这个目录就是默认的存放所有容器数据卷的目录,其下再根据数据卷名称创建新目录,格式为<font style="background-color:rgb(255,245,235);">/数据卷名/_data</font>
。
为什么不让容器目录直接指向宿主机目录呢?
- 因为直接指向宿主机目录就与宿主机强耦合了,如果切换了环境,宿主机目录就可能发生改变了。由于容器一旦创建,目录挂载就无法修改,这样容器就无法正常工作了。
- 但是容器指向数据卷,一个逻辑名称,而数据卷再指向宿主机目录,就不存在强耦合。如果宿主机目录发生改变,只要改变数据卷与宿主机目录之间的映射关系即可。
不过,我们通过由于数据卷目录比较深,不好寻找,通常我们
也允许让容器直接与宿主机目录挂载而不使用数据卷
一般是宿主机和数据卷产生映射,数据卷和容器产生映射,这样宿主机跟容器就有了映射
案例和提示
挂载本地目录或文件
# 挂载本地目录
-v 本地目录:容器内目录
# 挂载本地文件
-v 本地文件:容器内文件
2.6 自定义镜像
定义与java镜像
镜像结构
Dockerfile
https://docs.docker.com/engine/reference/builder/
2.7 网络
容器的网络IP其实是一个虚拟的IP,其值并不固定与某一个容器绑定,如果我们在开发时写死某个IP,而在部署时很可能MySQL容器的IP会发生变化,连接会失败。
所以,我们必须借助于docker的网络功能来解决这个问题
2.8 部署前端的坑点
如果不加network ,不把nginx加入到自己定义的网段,nginx找不到相应的域名,那么就启动不了Nginx
2.9 部署后端的坑点
这个local是-后面的,记得重构为applicantion-local.yaml
2.10 docker compose
2.10 Nginx
cmd的相关命令
# 启动nginx
start nginx.exe
# 停止
nginx.exe -s stop
# 重新加载配置
nginx.exe -s reload
# 重启
nginx.exe -s restart
3 微服务
3.1 单体架构优缺点
JMeter高并发测试-hi接口
黑马商城测试.jmx
总结->单体缺点: 某个接口并发次数太高,tomcat资源占用过高,影响其他接口
3.2 微服务1
3.2.1 SpringCloud
https://spring.io/projects/spring-cloud
3.2.2 拆分原则
3.2.3 Maven多模块项目和微服务架构项目
Maven多模块项目和微服务架构是两个不同的概念,它们分别属于软件开发的不同领域。
- Maven多模块项目:
- Maven是一个项目管理和构建自动化工具,它使用
pom.xml
文件来描述项目的构建过程、依赖关系等。 - 一个Maven多模块项目(Multi-module Maven project)是指一个包含多个子模块(module)的项目,每个子模块都有自己的
pom.xml
文件,并且可以独立编译和运行。这些模块通常在同一个版本控制仓库中,可以一起构建和部署。 - 多模块项目的优点在于可以更好地组织和管理大型项目,每个模块负责不同的功能,有助于团队协作和代码的模块化。
- Maven是一个项目管理和构建自动化工具,它使用
- 微服务架构:
- 微服务架构是一种软件开发架构风格,它将一个单一应用程序拆分成一组小服务,每个服务运行在其独立的进程中,并通过轻量级的通信机制(通常是HTTP RESTful API)进行交互。
- 微服务架构的目的是提高软件系统的可维护性、可扩展性和部署的灵活性。每个微服务可以独立开发、部署、扩展和替换。
- 在微服务架构中,每个服务通常由一个小团队负责,团队可以选择合适的技术栈来构建服务。
Maven多模块项目与微服务架构的关系:
- Maven多模块项目可以作为微服务架构中单个服务的构建方式。例如,一个微服务可能由多个Maven模块组成,每个模块负责不同的功能或组件。
- 但是,仅仅使用Maven多模块项目并不意味着你的架构就是微服务架构。微服务架构更关注的是服务的独立性、可扩展性和部署的灵活性,而Maven多模块项目更多关注于项目的构建和管理。
总结来说,Maven多模块项目是一种项目管理和构建的方法,而微服务架构是一种软件设计和部署的架构风格。两者可以结合使用,但它们本身并不等同。一个项目可以是Maven多模块的,但不一定是微服务架构;反之,一个微服务架构的项目可能并不使用Maven作为构建工具。
3.2.4 远程调用
RestTemplate
3.2.5 服务治理
3.2.4 的问题
注册中心原理
Nacos注册中心
nacos.sql
服务注册
服务发现(负载均衡)
连不上数据库
3.2.6 OpenFeign
介绍
快速入门
之前
现在
连接池
3.3 微服务2
3.3.1 网关
快速入门
路由属性
springboot网关拦截器与SpringMVC的拦截器(自动装配+条件注解只在微服务生效不在网关生效)
直接启动gateway是会找不到MvcConfig这个文件的,因为MvccConfig(META-INF)这个文件是放在common模块的,gateway底层不是Springmvc那一套,所以用条件生效的注解 红框 ,mvcc核心api有说明就要加载,没有就不用,gateway就不会加载,启动就不会报错
网关-》过滤器(基于响应式编程)-》拦截器(SpringMVC)-》服务
微服务登录解决方案(openfeign)
3.3.2 配置管理
nacos配置管理
https://b11et3un53m.feishu.cn/wiki/UMgpwmmQKisWBIkaABbcwAPonVf
3.3 服务保护和分布式事务
https://b11et3un53m.feishu.cn/wiki/QfVrw3sZvihmnPkmALYcUHIDnff