springboot实战项目-寰宇外卖重难点总结

思考

前端和后端的请求地址不同,前端发送的请求,是如何请求到后端服务的?

可以通过nginx反向代理将前端发送的动态请求由nginx转发到后端服务。nginx其他优点:1.提高访问速度。2.进行负载均衡。3.安全性高,保护后端服务安全。

nginx负载均衡策略:

1.轮询(默认):按时间顺序依次将请求分发到不同的后端服务器。
2.weight:权重,默认为1,权重越高,分配的请求越多。
3.ip_hash:每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器。
4.fair:按后端服务器的响应时间来分配请求,响应时间短的优先分配。
5.url_hash:按访问url的hash结果来分配请求,使相同的url定向到同一个后端服务器。
6.least_conn:最少连接数,哪个机器连接数少就分发到哪个机器。
7.自定义:自定义负载均衡策略。

Swagger接口文档生成工具

Knife4j是为了java MVC框架集成Swagger生成API文档的工具,前身是swagger-bootstrap-ui。

使用方式:1.导入Knife4j的maven坐标。2.在配置类中加入knife4j的相关配置。3.设置静态资源映射,为了接口文档页面的正常访问。
设置静态资源映射的原因:Swagger的页面是通过静态资源访问的,而SpringBoot默认的静态资源路径是“classpath:/static/”和“classpath:/public/”,所以需要设置静态资源映射。如果不设置静态资源映射,那么就会被当作动态请求,就会报404错误。

Swagger接口文档生成工具和YApi的区别

1.Swagger是后端生成接口文档的工具,而YApi是前后端都可以生成接口文档的工具。
2.Swagger是开发阶段使用的框架,帮助后端开发人员做接口测试。而YApi是设计阶段使用的工具,目的是管理和维护接口。

Knife4j的常见注解

1.@Api:用在类上,表示对类的说明。value:表示说明的内容,name:接口名称。
2.@ApiOperation:用在方法上,表示对方法的说明。value:表示说明的内容,notes:备注。
3.@ApiModelProperty:用在属性上,表述属性信息
4.@ApiModel:用在类上,表示对类的说明。value:表示说明的内容,description:描述。

作用是让接口文档的生成更加直观,方便前后端开发人员理解接口的用途和参数。

具体开发

1.新增员工:

1.1. 根据新增员工接口设计DTO,因为当前端提交的数据和实体类对应的属性差别过大时,建议使用DTO封装数据。

2.分页查询:

2.1. 使用PageHelper插件实现分页查询。
PageHelper.startPage是一个常用的MyBatis分页插件,用于在执行查询时自动处理分页逻辑。其基本实现原理如下:
2.1.1. 拦截器机制:PageHelper通过MyBatis的拦截器机制,在查询方法被调用前进行拦截。它会在执行查询之前,读取传入的分页参数(如页码和每页大小)。
2.1.2. 保存分页参数:在拦截器中,PageHelper会将分页参数保存到一个线程局部变量中,这样每个线程可以独立地存储自己的分页信息。
2.1.3. 修改SQL语句:在拦截查询语句时,PageHelper会对SQL进行修改,添加LIMIT和OFFSET子句,从而实现分页效果。具体来说,它会根据当前页码和每页大小计算出要跳过的记录数(OFFSET)和要获取的记录数(LIMIT)。
2.1.4. 执行查询:修改后的SQL被传递到数据库执行,返回相应的分页结果。
2.1.5. 返回结果:最终,PageHelper将结果封装成分页对象(如PageInfo),提供分页相关的信息(如总记录数、总页数等),方便调用者使用。

2.2. 分页查询的数据中时间格式化,让前端显示正确的日期格式:
2.2.1. 使用@JsonFormat注解,指定日期格式。(只能单次生效,下次碰到同类型的还要加上该注解)
2.2.2. 在WebMvcConfig配置类中,添加日期格式化的配置。(一劳永逸,所有日期格式化都生效)

具体实现代码:

/**
 * 对象映射器:基于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_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
    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(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);
    }
}
    /**
     * 扩展spring mvc框架的消息转换器
     * @param converters
     */
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("开始扩展消息转换器...");
        //创建消息转换器列表
        //创建一个消息转换器对象
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        //需要为消息转换器设置一个对象转换器,对象转换器可以将java对象序列化为json数据
        converter.setObjectMapper(new JacksonObjectMapper());
        //将消息转换器对象添加到消息转换器列表中
        converters.add(0, converter);
    }

3.实现公共字段自动填充(技术点:枚举、注解、AOP、反射)

3.1.自定义注解Autofill,用于标识需要进行公共字段自动填充的方法。
3.2.自定义切面类AutoFillAspect,用于统一拦截标有@Autofill注解的方法,通过反射为公共字段赋值。
3.3.Mapper的方法加入AutoFill注解。

4.店铺营业状态模块

4.1. 出现问题:


    /**
     * 获取店铺的营业状态
     *
     * @return
     */
    @ApiOperation("获取店铺的营业状态")
    @GetMapping("/status")
    public Result<Integer> getStatus() {
        Integer status = (Integer) redisTemplate.opsForValue().get(KEY);
        log.info("获取店铺的营业状态:{}", status == 1 ? "营业中" : "打烊中");

        return Result.success(Integer.parseInt(status.toString()));
    }
报错:Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap')] with root cause

java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap')



在这段代码处 @PutMapping("/{status}")
    @ApiOperation(value = "设置店铺的营业状态")
    public Result setStatus(@PathVariable Integer status) {
        log.info("设置店铺的营业状态:{}", status == 1 ? "营业中" : "打烊中");

        redisTemplate.opsForValue().set(KEY, status);
        return Result.success();
    }
    
    
报错:Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')] with root cause

java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')

原因:redis配置类里设置了value等序列化器,所以存储的数据都变成了String类型。不设置就可以了,只是在redis中查看数据时,看到的数据是乱码。并不影响使用。

posted @ 2024-10-02 13:58  crazybuddha  阅读(6)  评论(0编辑  收藏  举报