通过过滤器实现用户状态检测
功能描述:
用户访问首页时,若为未登录状态,则跳转到登录页面
功能实现:
- 创建 LoginCheckFilter 类,添加 @WebFilter注解,将该类声明为过滤器
//filterName 表示过滤器的名称;urlPatterns 表示拦截的请求路径
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
- 在 SpringBootApplication 上使用@ServletComponentScan 注解后,Servlet、Filter、Listener 可以直接通过@WebServlet、@WebFilter、@WebListener 注解自动注册,无需其他代码。
@SpringBootApplication
@ServletComponentScan
- 通过spring 的路径匹配器来对请求进行筛选
//spring中的路径匹配器
AntPathMatcher PATH_MATCHER = new AntPathMatcher();
异常处理
使用@ControllerAdvice 注解可以给Controller控制器添加统一的操作或处理,结合@ExceptionHandler注解,就可以实现统一的异常处理
注:一定要打印异常信息
@ControllerAdvice(annotations = {Controller.class, RestController.class})
@ResponseBody //用于回传数据
@Slf4j
public class GlobalExceptionHandle {
//统一处理sql异常
@ExceptionHandler(SQLException.class)
public R<String> sqlExceptionHandle(SQLException e) {
//打印异常信息
log.error(e.getMessage());
return R.error("数据操作异常");
}
}
MybatisPlus雪花算法生成的 id 字段精度缺失问题
mybatis plus默认使用雪花算法生成 long 型的主键 id,而JavaScript在处理数字时,超过16位的数字会自动进行四舍五入,这将导致在获取前端的 id 字段时会丢失精度
解决方案:
springMvc在底层是使用消息转换器来实现 Java对象与 json格式的数据的相互转换
所以可以自定义一个消息转换器,让其在处理 long 型的数据时 将其转为字符串类型进行传输,然后再将自定义的消息处理器添加到springMvc的转换器集合中即可
1. 自定义消息处理器类:
package com.waimai.commom;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
/**
* 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
*/
public class JacksonObjectMapper extends ObjectMapper {
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public JacksonObjectMapper() {
super();
//收到未知属性时不报异常
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
//反序列化时,属性不存在的兼容处理
this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
SimpleModule simpleModule = new SimpleModule()
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
.addSerializer(BigInteger.class, ToStringSerializer.instance)
.addSerializer(Long.class, ToStringSerializer.instance)
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
//注册功能模块 例如,可以添加自定义序列化器和反序列化器
this.registerModule(simpleModule);
}
}
2. 在springMvc的配置类中,将自定义消息处理器添加到spring mvc的转换集合中
/**
* 扩展spring mvc的消息转换器
* @param converters
*/
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//创建消息对象转换器
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
//设置对象转换器,使用Jackson将Java对象转为json(自定义的对象映射器)
converter.setObjectMapper(new JacksonObjectMapper());
//将消息转换器添加到spring mvc的转换集合中(设置自定义的转换器下标,实现优先调用)
converters.add(0,converter);
}
Mybatis Plus公共字段自动填充
在数据库不同的表中,可能会存在一些相同的字段,这时可以使用mybatis plus的公共字段自动填充功能来简化开发
操作步骤:
- 在实体类中,为表中公共字段所对应的属性添加@TableField 注解,将其标识为公共字段
//fill 表示自动填充的策略
//fill = FieldFill.INSERT 执行insert操作时进行填充
//fill = FieldFill.INSERT_UPDATE 执行insert或updat操作时进行填充
//fill = FieldFill.UPDATE 执行updat操作时进行填充
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
- 创建一个类,实现 MetaObjectHandler 接口,当公共字段执行insert或update操作时,该类的方法会被自动调用
//将该类标识为 bean
@Component
public class CommonMetaHandle implements MetaObjectHandler {
/**
* 执行insert操作时,为公共字段进行赋值
* @param metaObject 元数据
*/
@Override
public void insertFill(MetaObject metaObject) {
//设置创建时间和操作时间
metaObject.setValue("createTime",LocalDateTime.now());
metaObject.setValue("updateTime",LocalDateTime.now());
//设置操作人信息
metaObject.setValue("createUser",1L);
metaObject.setValue("updateUser",1L);
}
/**
* 执行update操作时,为公共字段进行赋值
* @param metaObject 元数据
*/
@Override
public void updateFill(MetaObject metaObject) {
//设置操作时间和操作人信息
metaObject.setValue("updateTime",LocalDateTime.now());
metaObject.setValue("updateUser", 1L);
}
}
Mysql主从复制
mysql主从复制是一个异步复制过程,底层是基于mysql数据库自带的二进制日志功能。就是一台或多台MySQL数据库(slave,从库)从另一台数据库(master,主库)进行日志复制,然后再解析日志并应用到自身,最终实现从库数据与主库数据保持一致
mysql复制步骤:
- master将改变记录到二进制日志(binary log)
- slave将master的日志拷贝到它的中继日志(relay log)
- slave重复中继日志中的事件,将改变应用到自身
配置-主库master:
- 修改Mysql的配置文件 my.cnf,追加如下内容
[mysqld]
log-bin=mysql-bin # 启用二进制日志
server-id=100 # 服务器唯一id
- 重启mysql服务: systemctl restart mysqld
- 执行sql,创建一个用户(用户名:xiaoming,密码:123456)并给用户授权,slave必须通过master授权才能通过该用户复制中继日志
GRANT REPLICATION SLAVE ON *.* to 'xiaoming'@'%' identfied by '123456';
- 执行sql:show master status;
配置-从库slave:
- 修改Mysql的配置文件 my.cnf,追加如下内容
server-id=101 # 服务器唯一id
- 重启MySQL服务
- 执行sql
change master to master_host='192.168.75.128',master_user='xiaoming',master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=433;
- 执行show slave status进行验证
使用 Sharding-JDBC实现读写分离
将数据库拆分为主库和从库,主库负责处理事务性的增删改操作,从库负责处理查询操作,这样能够有效避免由于数据更新造成的行锁,使得整个系统的查询性能能够得到极大的改善
步骤:
-
创建MySQL主从复制环境
-
导入maven坐标
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
- 在springboot配置文件中配置读写分离规则
spring:
application:
name: reggie_take_out
shardingsphere:
datasource:
names:
master,slave
# 主数据源
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
# 从数据源
slave:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
masterslave:
# 读写分离配置 轮循
load-balance-algorithm-type: round_robin
# 主数据源mc
master-data-source-name: master
# 从数据源名称列表,多个使用逗号分隔
slave-data-source-names: slave
# 最终的数据源名称
name: dataSource
props:
sql:
show: true #开启sql显示
main:
allow-bean-definition-overriding: true # 开启bean定义覆盖
- 由于Sharding-JDBC 需要配置数据源,而druid 数据库连接池也会创建一个数据源对象,所以会造成bean 冲突,可以在配置文件中开启 bean定义覆盖
Nginx
neginx 命令
查看版本信息:
- ./nginx -v:在nginx 安装目录的 sbin目录下执行
检查配置文件的正确性:
- ./nginx -t:检查 conf/nginx.conf 文件配置是否有错误
启动和停止:
- ./nginx:启动nginx 服务(默认是80端口)
- ./nginx -s stop:停止nginx 服务
nginx 服务启动后默认有两个进程 master 和 worker,可修改配置文件来启动多个 worker 进程
重新加载配置文件:
- ./nginx -s reload:修改配置文件后需要重新加载才能生效
nginx配置文件结构
整体结构:分为三部分
- 全局块:和nginx 运行相关的全局配置
- events块:和网络连接相关的配置
- http块:代理、缓存、日志记录、虚拟主机配置
- http全局块
- server块
- server全局块
- location块
注意:http块中可以配置多个server块每个server块中也可以配置多个location块
nginx 具体应用
反向代理
代理模式介绍
- 正向代理:正向代理一般是在客户端设置代理服务器,通过代理服务器转发请求,最终访问到目标服务器(梯子)
- 反向代理:反向代理服务器位于用户与目标服务器之间,与正向代理的区别就是,服务器是设置在服务端(例如某个项目有多个服务,就可以使用反向代理来实现对请求的统一管理)
配置反向代理:
server{
listen 端口号;
server_name 服务名称;
location /{
porxy_pass url #将请求转发到指定服务
}
}
负载均衡
- 应用集群:将同一应用部署到多台服务器上,组成应用集群,接收负载均衡器分发的请求
- 负载均衡器:将用户请求根据对应的负载均衡算法分发到应用集群中的一台服务器进行处理
负载均衡策略:
- 轮询:默认配置
- 权重:权重数字越大,分发机率越大
- ip_hash:根据访问ip进行分发,可以保证同一个ip请求的都是同一个服务器
- least_conn:依据最少连接方式,分发到当前连接数最少的服务器
- url_hash:根据url进行分发,相同的url会请求到固定的服务器
- fair:根据响应时间,分发到响应时间短的服务器
Swagger 生成接口文档
使用swagger 只需按照它的规范去定义接口及接口相关信息,再通过swagger衍生出的工具就可以生成各种格式的接口文档,以及在线接口调试页面
但是直接使用 swagger 比较繁琐,可以使用 knife4j (底层使用的还是swagger)更便于操作
使用步骤:
-
导入坐标
-
在springboot启动类加上注解
@EnableSwagger2
@EnableKnife4j
-
导入knife4j 相关配置类
-
启动项目后,访问 doc.html
swagger 常用注解
- @Api:用在类上,表示对类的说明
- @ApiModel:用在类上,通常是实体类,表示返回响应数据的信息
- @ApiModelPropety:用在属性上,描述响应类的属性
- @ApiOperation:用在请求的方法上,描述方法的作用
- @ApiImplicitParams:用在请求的方法上,表示一组参数的说明
- @ApiImplicitParam:用在@ApiImplicitParams注解中,描述某个参数的信息