《苍穹外卖》SpringBoot后端开发项目核心知识点与常见问题整理(DAY1 to DAY3)

news2025/3/12 21:17:47

目录

  • 一、在本地部署并启动Nginx服务
    • 1. 解压Nginx压缩包
    • 2. 启动Nginx服务
    • 3. 验证Nginx是否启动成功:
  • 二、导入接口文档
    • 1. 黑马程序员提供的YApi平台
    • 2. YApi Pro平台
    • 3. 推荐工具:Apifox
  • 三、Swagger
    • 1. 常用注解
      • 1.1 @Api与@ApiModel
      • 1.2 @ApiModelProperty与@ApiOperation
  • 四、基于JWT和ThreadLocal动态获取员工ID
    • 1. 在pom.xml中引入JWT依赖
    • 2. 在application.yaml中配置JWT参数
    • 3. 使用JWT获取当前登录员工ID
    • 4. 通过拦截器解析JWT
    • 5. 使用ThreadLocal传递员工ID
    • 6. 在Service中获取员工ID
  • 五、DTO的使用原因
    • 1. 实体类 Employee
    • 2. DTO EmployeeDTO
    • 3. 使用 DTO 的场景
      • 3.1 查询员工信息
      • 3.2 更新员工信息
      • 3.3 新增员工
    • 4. DTO、VO和实体类的区别
  • 六、为什么使用 XML 注解而不是 MyBatis 注解
    • 1. 使用 XML 注解的原因
      • 1.1 动态 SQL 支持
      • 1.2 SQL 与代码分离
      • 1.3 复用性
      • 1.4 工具支持
    • 2. MyBatis 注解的局限性
      • 2.1 动态 SQL 支持有限
      • 2.2 可读性差
      • 2.3 维护困难
  • 七、Spring Boot 的请求映射规则
    • 1. 类级别路径
    • 2. 方法级别路径
      • 2.1 分页查询
      • 2.2 根据 ID 查询菜品
      • 2.3 修改菜品
      • 2.4 新增菜品
      • 2.5 批量删除菜品
    • 3. 如何区分不同的功能
    • 4. 示例请求
      • 4.1 新增菜品
      • 4.2 修改菜品
      • 4.3 批量删除菜品
      • 4.4 分页查询菜品
      • 4.5 根据 ID 查询菜品
  • 八、接口设计中的是否必需原则
    • 1. 请求参数说明
      • 1.1 Java代码分析
        • 1.1.1 必需参数
      • 1.2 XML映射文件分析
        • 1.2.1 可选参数
    • 2. 返回响应说明
      • 2.1 Java代码分析
        • 2.1.1 必需参数
        • 2.1.2 可选参数
      • 2.2 返回响应示例
        • 2.2.1 成功响应(带数据)
        • 2.2.2 成功响应(不带数据)
        • 2.2.3 失败响应
  • 九、阿里云OSS配置指南
    • 1. 注册阿里云OSS账号
    • 2. 获取关键信息
    • 3. 更新配置文件
    • 4, 重新运行服务
  • 十、SpringBoot配置类详解
    • 1. 生产环境与开发环境配置
      • 1.1 配置文件命名规则
      • 1.2 激活环境配置
      • 1.3 占位符替换
        • 示例
    • 2. Spring 前缀与自定义前缀配置
      • 2.1 Spring 内置配置
      • 2.2 自定义配置
    • 3. 自定义配置类的注册与使用
      • 3.1 创建配置类
        • 3.1.1 JWT 配置类
        • 3.1.2 阿里云 OSS 配置类
      • 3.2 使用配置类
        • 3.2.1 在 Service 中使用配置类
      • 3.3 配置类的注册
  • 十一、SpringBoot注解汇总
    • 1. Spring MVC 相关注解
    • 2. Swagger 相关注解
    • 3. Spring 核心注解
    • 4. AOP 相关注解
    • 5. Lombok 相关注解
    • 6. MyBatis 相关注解
    • 7. 配置相关注解


本文旨在为刚完成《苍穹外卖》项目DAY1至DAY3章节学习的学员提供难点解析与核心知识点梳理。鉴于该项目由黑马程序员于2023年推出,在实际构建过程中可能会遇到诸如网站链接失效、阿里OSS服务不可用等问题。本文将针对这些常见问题提供解决方案,并帮助学员更好地掌握项目关键内容。

视频链接:黑马程序员Java项目实战《苍穹外卖》,最适合新手的SpringBoot+SSM的企业级Java项目实战
网盘资料:苍穹外卖讲义&前后端源码


一、在本地部署并启动Nginx服务

在开发过程中,我们经常需要使用Nginx来部署前端项目或作为反向代理服务器。

1. 解压Nginx压缩包

首先,确保你已经从黑马程序员资料中下载了Nginx的压缩包。接下来,按照以下步骤解压:

选择解压路径

  • 将Nginx压缩包解压到一个全英文路径中。例如:
    D:\nginx
    
  • 注意:路径中不要包含中文或特殊字符,否则可能会导致Nginx无法正常运行。

2. 启动Nginx服务

解压完成后,按照以下步骤启动Nginx:

进入Nginx目录

  • 打开解压后的Nginx文件夹,找到nginx.exe文件。路径通常为:
    C:\nginx\nginx.exe
    

启动Nginx

  • 双击nginx.exe文件,启动Nginx服务。
  • 启动后,Nginx会在后台运行,你可以在任务管理器中看到nginx.exe进程。

3. 验证Nginx是否启动成功:

  • 打开浏览器,访问以下地址(其中80是默认端口可省略不写):
    http://localhost:80
    
  • 如果看到此页面,说明Nginx已成功启动。
    在这里插入图片描述
  • 注意:Nginx默认不会随系统自动启动,因此每次重启电脑后,都需要手动启动Nginx

二、导入接口文档

在开发过程中,接口管理平台是团队协作和项目管理的重要工具。以下是几个常用平台的对比:

1. 黑马程序员提供的YApi平台

  • 地址:http://yapi.smart-xwork.cn/
  • 状态:已弃用
  • 功能:适合用于接口管理和文档生成。

2. YApi Pro平台

  • 地址:https://yapi.pro/
  • 问题:需要挂梯子才能访问,且极易卡顿,使用体验不佳。

3. 推荐工具:Apifox

  • 地址:https://apifox.com/
  • 优势:
    • 无需梯子即可访问。
    • 性能流畅,支持接口文档、Mock数据、自动化测试等功能。
    • 支持导入YApi数据格式的接口文档,方便无缝迁移现有项目。

在这里插入图片描述


三、Swagger

Swagger 是一种用于设计、构建、记录和使用 RESTful Web 服务的开源框架。它提供了一套工具,帮助开发者设计、构建、文档化和测试 API。

启动服务:访问 http://localhost:8080/doc.html
在这里插入图片描述

1. 常用注解

通过注解可以控制生成的接口文档,使接口文档拥有更好的可读性,常用注解如下:

注解说明
@Api用在类上,例如Controller,表示对类的说明
@ApiModel用在类上,例如entity、DTO、VO
@ApiModelProperty用在属性上,描述属性信息
@ApiOperation用在方法上,例如Controller的方法,说明方法的用途、作用

1.1 @Api与@ApiModel

在这里插入图片描述

在这里插入图片描述

1.2 @ApiModelProperty与@ApiOperation

在这里插入图片描述
在这里插入图片描述


四、基于JWT和ThreadLocal动态获取员工ID

在开发员工管理系统时,新增员工时需要记录创建人和修改人的ID。如果直接使用固定值,会导致数据不准确,无法真实反映操作者。

public void save(EmployeeDTO employeeDTO) {
    Employee employee = new Employee();
    employee.setCreateUser(10L); // 固定值
    employee.setUpdateUser(10L); // 固定值
    employeeMapper.insert(employee);
}

因此我们要通过JWT和ThreadLocal动态获取当前登录员工的ID,并实现数据的准确记录。在使用 JWT(JSON Web Token)进行身份验证时,通常需要在项目中引入相关的依赖库,并在配置文件中设置 JWT 的参数。以下是如何在 Spring Boot 项目中引入 JWT 并进行配置的简单说明:

1. 在pom.xml中引入JWT依赖

为了使用 JWT,我们需要引入一个 JWT 库,比如 java-jwt(由 Auth0 提供)。

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.4.0</version> <!-- 使用最新版本 -->
</dependency>

2. 在application.yaml中配置JWT参数

application.yaml 文件中定义 JWT 的相关配置,例如密钥、过期时间和令牌名称。

sky:
  jwt:
    # 设置 JWT 签名加密时使用的秘钥
    admin-secret-key: itcast
    # 设置 JWT 过期时间(单位:毫秒)
    admin-ttl: 7200000 # 2小时
    # 设置前端传递过来的令牌名称
    admin-token-name: token
  • admin-secret-key:用于签名和验证 JWT 的密钥。必须保密,且长度足够复杂以确保安全性。
  • admin-ttl:JWT 的有效期(以毫秒为单位)。例如,7200000 表示 2 小时。
  • admin-token-name:前端传递 JWT 时使用的参数名称。例如,前端可能会在请求头或请求参数中传递 token=xxx

3. 使用JWT获取当前登录员工ID

员工登录成功后,系统会生成JWT令牌并返回给前端。JWT中包含了当前登录员工的ID信息。

/**
 * 员工管理
 */
@RestController
@RequestMapping("/admin/employee")
@Api(tags = "员工相关接口")
@Slf4j
public class EmployeeController {

    @Autowired
    private EmployeeService employeeService;
    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 登录
     *
     * @param employeeLoginDTO
     * @return
     */
    @PostMapping("/login")
    public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {
        log.info("员工登录:{}", employeeLoginDTO);

        Employee employee = employeeService.login(employeeLoginDTO);

        //登录成功后,生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.EMP_ID, employee.getId());
        String token = JwtUtil.createJWT(
                jwtProperties.getAdminSecretKey(),
                jwtProperties.getAdminTtl(),
                claims);

        EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder()
                .id(employee.getId())
                .userName(employee.getUsername())
                .name(employee.getName())
                .token(token)
                .build();

        return Result.success(employeeLoginVO);
    }
    ...
}

4. 通过拦截器解析JWT

在每次请求时,前端会携带JWT令牌。通过拦截器解析JWT,获取当前登录员工的ID。

/**
 * jwt令牌校验的拦截器
 */
@Component
@Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {

    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 校验jwt
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判断当前拦截到的是Controller的方法还是其他资源
        if (!(handler instanceof HandlerMethod)) {
            //当前拦截到的不是动态方法,直接放行
            return true;
        }

        //1、从请求头中获取令牌
        String token = request.getHeader(jwtProperties.getAdminTokenName());

        //2、校验令牌
        try {
            log.info("jwt校验:{}", token);
            Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
            Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
            log.info("当前员工id:", empId);
            BaseContext.setCurrentId(empId);
            //3、通过,放行
            return true;
        } catch (Exception ex) {
            //4、不通过,响应401状态码
            response.setStatus(401);
            return false;
        }
    }
}

5. 使用ThreadLocal传递员工ID

通过ThreadLocal实现线程隔离,将当前登录员工的ID传递给Service层。

public class BaseContext {
    public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    public static void setCurrentId(Long id) {
        threadLocal.set(id);
    }

    public static Long getCurrentId() {
        return threadLocal.get();
    }

    public static void removeCurrentId() {
        threadLocal.remove();
    }
}

6. 在Service中获取员工ID

在Service层中,从ThreadLocal中获取当前登录员工的ID,并设置为创建人和修改人。

public void save(EmployeeDTO employeeDTO) {
    Employee employee = new Employee();
    employee.setCreateUser(BaseContext.getCurrentId()); // 动态获取当前登录员工ID
    employee.setUpdateUser(BaseContext.getCurrentId()); // 动态获取当前登录员工ID
    employeeMapper.insert(employee);
}

五、DTO的使用原因

在项目中,Employee 是实体类(Entity),用于表示数据库中的员工记录,而 EmployeeDTO 是数据传输对象(DTO),用于在不同层之间传递数据。以下是使用 DTO 的主要原因和优势:

1. 实体类 Employee

package com.sky.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.time.LocalDateTime;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;           // 员工ID
    private String username;   // 用户名
    private String name;       // 姓名
    private String password;   // 密码(敏感字段)
    private String phone;      // 手机号
    private String sex;        // 性别
    private String idNumber;   // 身份证号
    private Integer status;    // 状态
    private LocalDateTime createTime; // 创建时间(内部字段)
    private LocalDateTime updateTime; // 更新时间(内部字段)
    private Long createUser;   // 创建人(内部字段)
    private Long updateUser;   // 更新人(内部字段)
}

2. DTO EmployeeDTO

package com.sky.dto;

import lombok.Data;
import java.io.Serializable;

@Data
public class EmployeeDTO implements Serializable {

    private Long id;           // 员工ID
    private String username;   // 用户名
    private String name;       // 姓名
    private String phone;      // 手机号
    private String sex;        // 性别
    private String idNumber;   // 身份证号
}

3. 使用 DTO 的场景

3.1 查询员工信息

  • 前端只需要员工的基本信息(如 idusernamenamephonesexidNumber)。
  • 后端返回 EmployeeDTO,过滤掉敏感字段(如 password)和内部字段(如 createTime)。

3.2 更新员工信息

  • 前端传递 EmployeeDTO 作为请求体,后端根据 DTO 更新员工信息。
  • 避免前端传递不必要的字段(如 passwordcreateTime)。

3.3 新增员工

  • 前端传递 EmployeeDTO 作为请求体,后端将 DTO 转换为实体类并保存到数据库。
  • 避免前端传递内部字段(如 createTimeupdateTime)。

4. DTO、VO和实体类的区别

特性DTOVOEntity
目的数据传输数据展示或封装值表示数据库中的数据结构
使用场景跨层数据传输(如Controller-Service)展示层或领域模型数据库操作、业务逻辑
可变性可变(通常有setter)通常不可变(无setter)可变(用于持久化和业务逻辑)
字段与传输需求相关与展示或业务逻辑相关与数据库表字段严格对应
行为通常无行为可能包含简单行为(如格式化)包含业务逻辑和验证规则
示例UserDTOUserVOUserEntity

六、为什么使用 XML 注解而不是 MyBatis 注解

1. 使用 XML 注解的原因

1.1 动态 SQL 支持

  • XML 提供了强大的动态 SQL 支持,例如 <if><foreach><choose> 等标签。
  • 在复杂的查询场景中,动态 SQL 可以更灵活地构建 SQL 语句。

1.2 SQL 与代码分离

  • 将 SQL 语句写在 XML 文件中,可以使 SQL 与 Java 代码分离,便于维护和管理。
  • 对于复杂的 SQL 语句,XML 文件的可读性更高。

1.3 复用性

  • XML 文件中的 SQL 语句可以在多个 Mapper 接口中复用。
  • 例如,可以在不同的 Mapper 接口中引用同一个 SQL 片段。

1.4 工具支持

  • MyBatis 提供了丰富的工具支持 XML 文件的编写和调试。
  • 例如,MyBatis Generator 可以自动生成 XML 映射文件。

2. MyBatis 注解的局限性

2.1 动态 SQL 支持有限

  • MyBatis 注解对动态 SQL 的支持较弱,复杂的 SQL 语句难以用注解实现。
  • 例如,@Select 注解无法直接实现 <foreach> 这样的动态 SQL。
<select id="getSetmealIdsByDishIds" resultType="java.lang.Long">
  select setmeal_id from setmeal_dish where dish_id in
  <foreach collection="dishIds" item="dishId" separator="," open="(" close=")">
      #{dishId}
  </foreach>
</select>

2.2 可读性差

  • 复杂的 SQL 语句写在注解中会导致代码冗长,可读性差。
  • 例如,一个包含多个条件的查询语句会显得非常混乱。

2.3 维护困难

  • SQL 语句与 Java 代码混合在一起,维护起来不如 XML 文件方便。
  • 修改 SQL 语句时需要重新编译 Java 代码。

七、Spring Boot 的请求映射规则

在 Spring Boot 中,请求的映射是通过 类级别的 @RequestMapping方法级别的 @PutMapping@GetMapping 等注解 共同决定的。

  • 类级别的 @RequestMapping
    • 定义了该类中所有方法的公共路径前缀。
    • 例如,@RequestMapping("/admin/dish") 表示该类中的所有方法都映射到 /admin/dish 路径下。
    • 管理端发出的请求,统一使用/admin作为前缀。
    • 用户端发出的请求,统一使用/user作为前缀。
  • 方法级别的 @PutMapping@GetMapping
    • 定义了具体的 HTTP 方法和路径。
    • 如果方法级别的注解没有指定路径,则默认使用类级别的路径。

1. 类级别路径

@RestController
@RequestMapping("/admin/dish")
public class DishController {
    // 方法定义...
}
  • 所有方法的公共路径前缀是 /admin/dish

2. 方法级别路径

2.1 分页查询

@GetMapping("/page")
public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO) {
    // 方法实现...
}
  • 完整路径是 /admin/dish/page

2.2 根据 ID 查询菜品

@GetMapping("/{id}")
public Result<DishVO> getById(@PathVariable Long id) {
    // 方法实现...
}
  • 完整路径是 /admin/dish/{id}

2.3 修改菜品

@PutMapping
public Result update(@RequestBody DishDTO dishDTO) {
    // 方法实现...
}
  • 由于 @PutMapping 没有指定路径,默认使用类级别的路径 /admin/dish

2.4 新增菜品

@PostMapping
public Result save(@RequestBody DishDTO dishDTO) {
    // 方法实现...
}
  • 由于 @PostMapping 没有指定路径,默认使用类级别的路径 /admin/dish

2.5 批量删除菜品

@DeleteMapping
public Result delete(@RequestParam List<Long> ids) {
    // 方法实现...
}
  • 由于 @DeleteMapping 没有指定路径,默认使用类级别的路径 /admin/dish

3. 如何区分不同的功能

Spring Boot 通过 HTTP 方法 来区分不同的功能。例如:

HTTP 方法路径功能
POST/admin/dish新增菜品
PUT/admin/dish修改菜品
DELETE/admin/dish批量删除菜品
GET/admin/dish/page分页查询菜品
GET/admin/dish/{id}根据 ID 查询菜品

4. 示例请求

4.1 新增菜品

  • HTTP 方法POST
  • URL/admin/dish
  • 请求体
    {
      "name": "宫保鸡丁",
      "price": 38.0,
      "flavors": [
        {
          "name": "微辣",
          "value": "少辣"
        }
      ]
    }
    

4.2 修改菜品

  • HTTP 方法PUT
  • URL/admin/dish
  • 请求体
    {
      "id": 1,
      "name": "宫保鸡丁",
      "price": 40.0,
      "flavors": [
        {
          "name": "微辣",
          "value": "少辣"
        }
      ]
    }
    

4.3 批量删除菜品

  • HTTP 方法DELETE
  • URL/admin/dish?ids=1,2,3
  • 请求参数ids=1,2,3

4.4 分页查询菜品

  • HTTP 方法GET
  • URL/admin/dish/page?page=1&pageSize=10
  • 请求参数page=1&pageSize=10

4.5 根据 ID 查询菜品

  • HTTP 方法GET
  • URL/admin/dish/1
  • 路径参数id=1

八、接口设计中的是否必需原则

参数的必需与非必需性是通过不同的方式来体现的,以下是具体案例

1. 请求参数说明

在这里插入图片描述

从接口文档中可以看到,请求参数包括以下几项:

参数名类型说明必需性示例值
categoryIdstring分类id可选101
namestring菜品名称可选官保鸡丁
pagestring页码必需1
pageSizestring每页记录数必需10
statusstring分类状态可选1
  • 必需参数

    • pagepageSize 是分页查询的必需参数,用于指定查询的页码和每页的记录数。
  • 可选参数

    • categoryIdnamestatus 是可选参数,用于过滤查询结果。

1.1 Java代码分析

在此 Java 代码中,DishPageQueryDTO 是一个数据传输对象(DTO),用于封装分页查询的参数。以下是代码的详细分析:

public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {
    // 1. 使用 PageHelper 进行分页
    PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());
    
    // 2. 调用 Mapper 进行查询
    Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);
    
    // 3. 返回分页结果
    return new PageResult(page.getTotal(), page.getResult());
}
1.1.1 必需参数
  • dishPageQueryDTO.getPage()dishPageQueryDTO.getPageSize() 是分页查询的必需参数。
  • 如果这两个参数为空或未提供,分页功能将无法正常工作。

1.2 XML映射文件分析

在 SQL 代码中,动态 SQL 语句根据传入的参数生成查询条件。以下是代码的详细分析:

<select id="pageQuery" resultType="com.sky.vo.DishVO">
    select d.* , c.name as categoryName 
    from dish d 
    left outer join category c on d.category_id = c.id
    <where>
        <if test="name != null">
            and d.name like concat('%',#{name},'%')
        </if>
        <if test="categoryId != null">
            and d.category_id = #{categoryId}
        </if>
        <if test="status != null">
            and d.status = #{status}
        </if>
    </where>
    order by d.create_time desc
</select>
1.2.1 可选参数
  • namecategoryIdstatus 是可选参数,通过 <if> 标签动态生成查询条件。
  • 如果某个参数为 null,则对应的条件不会添加到 SQL 查询中。

通过这种设计,分页查询接口既满足了基本的查询需求,又提供了灵活的过滤选项,适用于不同的业务场景。


2. 返回响应说明

在 API 设计中,返回响应的数据结构通常需要遵循一定的规范,以确保客户端能够准确处理和理解服务器的响应。Result<T> 类是一个典型的统一响应格式,根据不同的设计需求,我们发现 code 一定是必需的,而 msgdata 在某些情况下是可选的,而在某些情况可能是必需的。接下来,我们将详细探讨这种设计背后的原因。

在这里插入图片描述
在这里插入图片描述

2.1 Java代码分析

package com.sky.result;

import lombok.Data;

import java.io.Serializable;

`/**
 * 后端统一返回结果
 * @param <T>
 */
@Data
public class Result<T> implements Serializable {

    private Integer code; //编码:1成功,0和其它数字为失败
    private String msg; //错误信息
    private T data; //数据

    public static <T> Result<T> success() {
        Result<T> result = new Result<T>();
        result.code = 1;
        return result;
    }

    public static <T> Result<T> success(T object) {
        Result<T> result = new Result<T>();
        result.data = object;
        result.code = 1;
        return result;
    }

    public static <T> Result<T> error(String msg) {
        Result result = new Result();
        result.msg = msg;
        result.code = 0;
        return result;
    }

}
2.1.1 必需参数

code 是必需的

  • 作用code 用于表示请求的处理结果状态,通常是一个数字。
    • 例如: 1 表示成功,0 或其他数字表示失败。
  • 为什么必需
    • 明确状态:客户端需要知道请求是否成功。code 提供了一个明确的状态标识,客户端可以根据它决定后续操作。
    • 标准化:统一的 code 值可以让客户端以一致的方式处理所有 API 的响应。
    • 错误处理:当请求失败时,code 可以帮助客户端快速定位问题类型(如权限不足、资源不存在等)。
2.1.2 可选参数

msg 是可选的

  • 作用msg 用于提供额外的错误信息或成功提示。
    • 例如:成功时:msg 可以为空或包含提示信息(如“操作成功”),失败时:msg 可以包含具体的错误描述(如“用户未找到”)。
  • 为什么可选
    • 成功时可能不需要:在请求成功的情况下,客户端可能只需要 data,而不需要额外的提示信息。
    • 减少冗余:如果 msg 是必需的,即使没有实际意义的信息(如成功时的默认提示),也需要返回,这会增加响应的冗余。
    • 灵活性:在某些场景下,错误信息可能由其他方式提供(如日志或专门的错误处理机制),因此 msg 可以省略。

data 是可选的

  • 作用data 用于承载实际的响应数据。
    • 例如:查询接口返回的列表或对象,创建接口返回的新创建的资源。
  • 为什么可选
    • 某些操作不需要返回数据:例如,删除操作或简单的状态更新操作可能不需要返回任何数据。
    • 减少冗余:如果 data 是必需的,即使没有数据也需要返回一个空对象或 null,这会增加响应的冗余。
    • 灵活性:某些接口可能只需要返回状态信息(如 codemsg),而不需要额外的数据。

2.2 返回响应示例

2.2.1 成功响应(带数据)
{
  "code": 1,
  "msg": "操作成功",
  "data": {
    "id": 123,
    "name": "John Doe"
  }
}
2.2.2 成功响应(不带数据)
{
  "code": 1
}
2.2.3 失败响应
{
  "code": 0,
  "msg": "用户未找到"
}

这种设计符合 API 设计的最佳实践,能够满足大多数场景的需求,同时保持简洁和一致性。


九、阿里云OSS配置指南

在使用对象存储服务时,可能会遇到Bucket失效的情况,从而导致服务无法正常运行。为了解决这个问题,我们需要重新配置一个新的Bucket,并更新相关配置文件。参考教程:Java利用阿里云OSS/本地存储实现文件上传功能

1. 注册阿里云OSS账号

首先,访问阿里云-对象存储OSS官网注册账号。新用户可免费试用20GB存储空间,有效期为3个月。

在这里插入图片描述

2. 获取关键信息

注册完成后,开通对象存储服务OSS,创建第一个Bucket,并获取以下四个关键信息:

  1. Endpoint:OSS服务的访问地址。
  2. Access Key ID:用于身份验证的访问密钥ID。
  3. Access Key Secret:用于身份验证的访问密钥。
  4. Bucket Name:新创建的Bucket名称。

在这里插入图片描述

3. 更新配置文件

将上述获取的信息填写到对应的YAML配置文件sky-server/src/main/resources/application-dev.yml目录下

sky:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    host: localhost
    port: 3306
    database: sky_take_out
    username: your_username
    password: your_password
  alioss:
    endpoint: your_endpoint
    access-key-id: your_accessKeyId
    access-key-secret: your_keySecret
    bucket-name: your_bucketName
    
#    endpoint: oss-cn-hangzhou.aliyuncs.com
#    access-key-id: LTAI5tPeFLzsPPT8gG3LPW64
#    access-key-secret: U6k1brOZ8gaOIXv3nXbulGTUzy6Pd7
#    bucket-name: sky-take-out

4, 重新运行服务

保存配置文件后,重新运行服务并打开前端页面,发现图片能够正常上传了!

在这里插入图片描述
并且我们可以在自己的对象存储OSS文件管理中监控到从前端页面上传的图片文件

在这里插入图片描述


十、SpringBoot配置类详解

在 Spring Boot 项目中,配置文件是管理应用程序行为的关键部分。通过合理使用配置类,我们可以轻松实现多环境配置、自定义配置以及配置的动态注入。

1. 生产环境与开发环境配置

Spring Boot 支持多环境配置,通常我们会为不同环境(如开发、测试、生产)创建不同的配置文件。通过 spring.profiles.active 属性,可以动态切换环境。

1.1 配置文件命名规则

  • 默认配置文件application.yaml(或 application.properties)。
  • 环境特定配置文件application-{profile}.yaml,其中 {profile} 是环境标识,如 devprod

1.2 激活环境配置

application.yaml 中,通过 spring.profiles.active 指定激活的环境:

spring:
  profiles:
    active: dev  # 激活开发环境
  • dev 的含义:表示加载 application-dev.yaml 文件。
  • prod 的含义:表示加载 application-prod.yaml 文件。

1.3 占位符替换

在配置文件中,可以使用 ${} 占位符引用其他配置项的值。Spring Boot 会在启动时自动替换这些占位符。

示例
  • application.yaml

    spring:
      datasource:
        url: jdbc:mysql://${sky.datasource.host}:${sky.datasource.port}/${sky.datasource.database}
    
  • application-dev.yaml

    sky:
      datasource:
        host: localhost
        port: 3306
        database: devdb
    
  • 最终替换结果

    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/devdb
    

2. Spring 前缀与自定义前缀配置

Spring Boot 的配置文件支持两种类型的配置:Spring 内置配置自定义配置

2.1 Spring 内置配置

Spring Boot 使用 spring 作为内置配置的命名空间,用于配置框架本身的行为,例如:

  • 数据源配置spring.datasource
  • Redis 配置spring.redis
  • Profile 激活spring.profiles.active
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: root
  redis:
    host: localhost
    port: 6379

2.2 自定义配置

自定义配置通常用于业务相关的配置项。我们可以使用自定义前缀(如 sky)来组织这些配置。

sky:
  jwt:
    admin-secret-key: itcast
    admin-ttl: 7200000
  alioss:
    endpoint: oss-cn-shenzhen.aliyuncs.com
    access-key-id: LTAI5tRK6gZ9f6CyHCaWh7e8

3. 自定义配置类的注册与使用

Spring Boot 提供了 @ConfigurationProperties 注解,用于将配置文件中的值绑定到 Java 对象中。

3.1 创建配置类

通过 @ConfigurationProperties 注解,指定配置的前缀,并定义与配置项对应的字段。

3.1.1 JWT 配置类
package com.sky.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "sky.jwt")
@Data
public class JwtProperties {
    private String adminSecretKey;
    private long adminTtl;
    private String adminTokenName;
}
3.1.2 阿里云 OSS 配置类
package com.sky.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "sky.alioss")
@Data
public class AliOssProperties {
    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;
}

3.2 使用配置类

在其他组件(如 Service、Controller)中,可以通过 @Autowired 注入配置类,直接使用配置值。

3.2.1 在 Service 中使用配置类
package com.sky.service;

import com.sky.properties.AliOssProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OssService {

    @Autowired
    private AliOssProperties aliOssProperties;

    public void printOssConfig() {
        System.out.println("Endpoint: " + aliOssProperties.getEndpoint());
        System.out.println("AccessKeyId: " + aliOssProperties.getAccessKeyId());
    }
}

3.3 配置类的注册

  • @Component:将配置类注册为 Spring 的 Bean。
  • @ConfigurationProperties:指定配置的前缀,并绑定配置项。

十一、SpringBoot注解汇总

1. Spring MVC 相关注解

  1. @RestController
    标记一个类为 RESTful 控制器,返回值直接作为响应体(通常是 JSON 格式),它结合了 @Controller@ResponseBody 的功能,适用于开发 RESTful API。

  2. @RequestMapping
    映射 HTTP 请求到控制器方法,可以指定请求路径、请求方法(如 GET、POST)等。

  3. @PostMapping
    专门处理 HTTP POST 请求,是 @RequestMapping(method = RequestMethod.POST) 的简写。

  4. @GetMapping
    专门处理 HTTP GET 请求,是 @RequestMapping(method = RequestMethod.GET) 的简写。

  5. @DeleteMapping
    专门处理 HTTP DELETE 请求,是 @RequestMapping(method = RequestMethod.DELETE) 的简写。

  6. @PutMapping
    专门处理 HTTP PUT 请求,是 @RequestMapping(method = RequestMethod.PUT) 的简写。

  7. @RequestBody
    将 HTTP 请求体(通常是 JSON 格式)绑定到方法参数,适用于接收客户端传递的复杂数据。

  8. @ResponseBody
    将方法的返回值直接作为 HTTP 响应体(通常是 JSON 格式),而不是跳转到视图页面。

  9. @PathVariable
    将 URL 路径中的变量绑定到方法参数,适用于 RESTful 风格的 URL。

2. Swagger 相关注解

  1. @Api
    标记一个类为 Swagger 资源,用于生成 API 文档。

  2. @ApiModel
    描述 Swagger 模型类,通常用于实体类,生成文档中的模型信息。

  3. @ApiModelProperty
    描述 Swagger 模型类的属性,用于生成文档中属性的详细信息。

  4. @ApiOperation
    描述 API 接口的操作,用于生成文档中接口的详细信息。

3. Spring 核心注解

  1. @Autowired
    自动注入 Spring Bean,用于依赖注入。

  2. @Bean
    声明一个方法返回的对象为 Spring Bean,通常用于配置类中。

  3. @Configuration
    标记一个类为配置类,用于定义 Bean 或其他配置。

  4. @Service
    标记一个类为服务层组件,通常用于业务逻辑层。

  5. @Component
    标记一个类为 Spring 组件,泛指任何 Spring 管理的 Bean。

  6. @SpringBootApplication
    标记主启动类,包含 @Configuration@EnableAutoConfiguration@ComponentScan,用于启动 Spring Boot 应用。

  7. @EnableTransactionManagement
    启用 Spring 的事务管理,通常用于配置类中。

4. AOP 相关注解

  1. @Aspect
    标记一个类为切面,用于定义横切关注点(如日志、事务)。

  2. @Before
    在目标方法执行前执行切面逻辑。

  3. @Pointcut
    定义切点表达式,用于指定哪些方法需要被切面处理。

5. Lombok 相关注解

  1. @Slf4j
    自动生成日志对象 log,简化日志记录代码。

  2. @Data
    自动生成 gettersettertoStringequalshashCode 等方法。

  3. @Builder
    为类生成 Builder 模式支持,简化对象的创建。

  4. @NoArgsConstructor
    生成无参构造方法。

  5. @AllArgsConstructor
    生成全参构造方法。

6. MyBatis 相关注解

  1. @Mapper
    标记一个接口为 MyBatis 的 Mapper 接口,用于定义数据库操作。

  2. @Delete
    映射 SQL 删除操作。

  3. @Select
    映射 SQL 查询操作。

  4. @Insert
    映射 SQL 插入操作。

  5. @AutoFill
    自定义注解,用于自动填充字段(如创建时间、更新时间)。

7. 配置相关注解

  1. @ConfigurationProperties
    将配置文件中的值绑定到 Java 对象中,通常用于加载自定义配置,其中 prefix:指定配置项的前缀,从配置文件中加载特定部分的配置。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2313938.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QT系列教程(20) Qt 项目视图便捷类

视频连接 https://www.bilibili.com/video/BV1XY41127t3/?vd_source8be9e83424c2ed2c9b2a3ed1d01385e9 Qt项目视图便捷类 Qt项目视图提供了一些便捷类&#xff0c;包括QListWidget, QTableWidget&#xff0c; QTreeWidget等。我们分别介绍这几个便捷类。 我们先创建一个Qt …

动态扩缩容引发的JVM堆内存震荡:从原理到实践的GC调优指南

目录 一、典型案例&#xff1a;系统发布后的GC雪崩事件 &#xff08;一&#xff09;故障现象 1. 刚刚启动时 GC 次数较多 2. 堆内存锯齿状波动 3. GC日志特征&#xff1a;Allocation Failure &#xff08;二&#xff09;问题定位 二、原理深度解析&#xff1a;JVM内存弹…

AI智能眼镜主控芯片:技术演进与产业生态的深度解析

一、AI智能眼镜的技术挑战与主控芯片核心诉求 AI智能眼镜作为XR&#xff08;扩展现实&#xff09;技术的代表产品&#xff0c;其核心矛盾在于性能、功耗与体积的三角平衡。主控芯片作为设备的“大脑”&#xff0c;需在有限空间内实现复杂计算、多模态交互与全天候续航&#xf…

微服务拆分-远程调用

我们在查询购物车列表的时候&#xff0c;它有一个需求&#xff0c;就是不仅仅要查出购物车当中的这些商品信息&#xff0c;同时还要去查到购物车当中这些商品的最新的价格和状态信息&#xff0c;跟购物车当中的快照进行一个对比&#xff0c;从而去提醒用户。 现在我们已经做了服…

[网络爬虫] 动态网页抓取 — Selenium 介绍 环境配置

&#x1f31f;想系统化学习爬虫技术&#xff1f;看看这个&#xff1a;[数据抓取] Python 网络爬虫 - 学习手册-CSDN博客 0x01&#xff1a;Selenium 工具介绍 Selenium 是一个开源的便携式自动化测试工具。它最初是为网站自动化测试而开发的&#xff0c;类似于我们玩游戏用的按…

【RAGFlow】windows本地pycharm运行

原因 由于官方只提供了docker部署&#xff0c;基于开源代码需要实现自己内部得逻辑&#xff0c;所以需要本地pycharm能访问&#xff0c;且docker运行依赖得其余组件&#xff0c;均需要使用开发服务器得配置。 修改过程 安装python 项目依赖于Python 版本&#xff1a;>3.1…

树莓派5首次开机保姆级教程(无显示器通过VNC连接树莓派桌面)

第一次开机详细步骤 步骤一&#xff1a;树莓派系统烧录1 搜索打开烧录软件“Raspberry Pi Imager”2 选择合适的设备、系统、SD卡3 烧录配置选项 步骤二&#xff1a;SSH远程树莓派1 树莓派插电2 网络连接&#xff08;有线或无线&#xff09;3 确定树莓派IP地址 步骤三&#xff…

html-表格标签

一、表格标签 1. 表格的主要作用 表格主要用于显示&#xff64;展示数据,因为它可以让数据显示的非常的规整,可读性非常好&#xff61;特别是后台展示数据 的时候,能够熟练运用表格就显得很重要&#xff61;一个清爽简约的表格能够把繁杂的数据表现得很有条理&#xff61; 总…

大模型安全新范式:DeepSeek一体机内容安全卫士发布

2月以来&#xff0c;DeepSeek一体机几乎成为了政企市场AI消费的最强热点。 通过一体机的方式能够缩短大模型部署周期&#xff0c;深度结合业务场景&#xff0c;降低中小企业对于大模型的使用门槛。据不完全统计&#xff0c;已约有超过60家企业基于DeepSeek推出一体机产品。 但…

数据分析绘制随时间顺序变化图加入线性趋势线——numpy库的polyfit计算一次多项式拟合

import pandas as pd import numpy as np import matplotlib.pyplot as plt# 导入数据 data pd.read_csv(rC:\Users\11712\notebooktrain1.csv)# 假设数据包含 date_time 和 speed 列 data[date_time] pd.to_datetime(data[date_time]) # 确保时间列是 datetime 类型 data.s…

密闭空间可燃气体监测终端:守护城市命脉,智驭燃气安全!

近年来&#xff0c;陕西省高度重视燃气安全&#xff0c;出台了一系列政策文件&#xff0c;旨在全面加强城镇燃气安全监管&#xff0c;防范化解重大安全风险。2023年&#xff0c;陕西省安委会印发《全省城镇燃气安全专项整治工作方案》&#xff0c;明确要求聚焦燃气经营、输送配…

阿里千问大模型(Qwen2.5-VL-7B-Instruct)部署

参考链接 知乎帖子 B站视频 huggingface 镜像网站&#xff08;不太全&#xff0c;比如 Qwen/Qwen2.5-VL-7B-Instruct就没有&#xff09; huggingface 5种下载方式汇总 通过huggingface-cli下载模型 不一样的部分是预训练权重的下载和demo 首先安装huggingface_hub pip insta…

【Go学习实战】03-3-文章评论及写文章

【Go学习实战】03-3-文章评论及写文章 文章评论注册valine获取凭证加载评论页面 写文章修改cdn位置完善功能查看页面 发布文章POST发布文章发布文章测试 查询文章详情查询详情测试 修改文章修改文章测试 写文章图片上传前端后端逻辑测试 文章评论 这里我们的博客因为是个轻量级…

从零开始用AI开发游戏(一)

1. 核心玩法设计 核心目标&#xff1a;玩家需在随机生成的3D迷宫中寻找出口&#xff0c;躲避陷阱、收集道具、解开谜题。核心机制&#xff1a; 随机生成迷宫&#xff1a;每次游戏生成不同结构的迷宫&#xff08;递归分割算法或深度优先搜索&#xff09;。第一人称视角&#xf…

AI-大模型中的流式输出与非流式输出

1.前言 在大模型API开发中&#xff0c;流式与非流式输出对应着两种不同的数据交互&#xff0c;在代码中stream中通过参数true与false来进行设定。 2.流式输出与非流式输出的原理 2.1.非流式输出-请求一次响应返回完整数据 非流式输出&#xff0c;传统的请求-响应模式&#xf…

【HarmonyOS Next】鸿蒙加固方案调研和分析

【HarmonyOS Next】鸿蒙加固方案调研和分析 一、前言 根据鸿蒙应用的上架流程&#xff0c;本地构建app文件后&#xff0c;上架到AGC平台&#xff0c;平台会进行解析。根据鸿蒙系统的特殊设置&#xff0c;仿照IOS的生态闭环方案。只能从AGC应用市场下载app进行安装。这样的流程…

蓝桥杯javaB组备战第二天 题目 区间次方和 编号3382

这是一个前缀和问题&#xff0c;但是不同于以为前缀和问题 前缀和问题求解思路&#xff1a; 创建一个前缀数组 s[] ,存储输入的元素的a[1]到a[n]的和 及&#xff1a;s[1] s[i-1]a[i] ,i>1 这样比暴力算法的复杂度要低很多可以将 时间复杂度从O(q*n*m)下降到 O(n*mq) …

《Android 平台架构系统启动流程详解》

目录 一、平台架构模块 1.1 Linux 内核 1.2 硬件抽象层 (HAL) 1.3 Android 运行时 1.4 原生 C/C 库 1.5 Java API 框架 1.6 系统应用 二、系统启动流程 2.1 Bootloader阶段 2.2 内核启动 2.3 Init进程&#xff08;PID 1&#xff09; 2.4 Zygote与System Serv…

强化学习(赵世钰版)-学习笔记(3.最优策略与贝尔曼最优方程)

这是本章在课程中的位置&#xff0c;属于基础工具中的最后一章&#xff0c;主要讨论了最优状态值&#xff08;Optimal State Value&#xff09;与最优策略&#xff08;Optimal Policy&#xff09;&#xff0c;并介绍了对应的计算方法-贝尔曼最优方程&#xff08;Bellman Optima…

六十天前端强化训练之第十一天之事件机制超详解析

欢迎来到编程星辰海的博客讲解 目录 一、事件模型演进史 1.1 原始事件模型&#xff08;DOM Level 0&#xff09; 1.2 DOM Level 2事件模型 1.3 DOM Level 3事件模型 二、事件流深度剖析 2.1 捕获与冒泡对比实验 2.2 事件终止方法对比 三、事件委托高级应用 3.1 动态元…