Skip to content

SpringBoot-1 学习笔记 ​

前后端分离式开发的流程,以及较简单代码的逻辑实现,能够阅读接口文档实现项目代码的开发。本文按个人学习路径整理复习要点,熟练度 4/5,侧重快速回顾,忽略细节铺垫。

1. 学习接收前端的参数 ​

  • 四种接收姿势与注解一一对应:查询参数 ?name=xx → @RequestParam;路径参数 /user/{id} → @PathVariable;JSON 请求体 → @RequestBody;表单参数可直接用实体对象接收。
  • @RequestParam 默认必传,可选参数要写 required = false;前端参数名与形参不一致时用 @RequestParam("name") 显式指定。
  • @PathVariable("id") 要跟 @RequestMapping 里的 {id} 占位符一一对应。
  • 一个方法只能有一个 @RequestBody;GET 请求不能带请求体,POST 传 JSON 时必须设 Content-Type: application/json。
  • 前端传的日期字符串需要 @DateTimeFormat(pattern = "yyyy-MM-dd") 才能绑定到 Date 字段。
java
// 查询参数:/user?name=zs&age=20
@GetMapping("/user")
public Result getByName(@RequestParam String name,
                        @RequestParam(required = false) Integer age) {
    return Result.success(userService.getByName(name, age));
}

// 路径参数:/user/1
@GetMapping("/user/{id}")
public Result getById(@PathVariable Integer id) {
    return Result.success(userService.getById(id));
}

// JSON 请求体
@PostMapping("/user")
public Result add(@RequestBody User user) {
    return Result.success(userService.add(user));
}

2. 学习简单的开发流程,分层开发 ​

  • 标准三层:Controller(接收参数、响应结果)→ Service(业务逻辑、事务)→ Mapper(数据访问)。Controller 不写业务,Service 不直接拼 SQL。
  • Service 习惯"接口 + 实现类",实现类加 @Service;Mapper 接口加 @Mapper(或在启动类加 @MapperScan 统一扫包)。
  • 注入推荐构造器注入(final 字段)利于测试;简单项目用 @Autowired 字段注入也可。
  • Lombok 减少样板代码:@Data 生成 getter/setter/toString,@Slf4j 提供 log 对象。
  • 统一返回 Result{code, message, data} 封装,前端按 code 判断成功失败,接口只返回数据语义。
  • 易错点:Controller 直接调 Mapper 跳过 Service;实现类忘加 @Service 导致启动报"找不到 Bean"。
java
@RestController
@RequestMapping("/emp")
public class EmpController {
    @Autowired
    private EmpService empService;

    @GetMapping("/{id}")
    public Result getById(@PathVariable Integer id) {
        return Result.success(empService.getById(id));
    }
}

@Service
public class EmpServiceImpl implements EmpService {
    @Autowired
    private EmpMapper empMapper;

    @Override
    public Emp getById(Integer id) {
        return empMapper.findById(id);
    }
}

3. 学习Mybatis ​

  • 半自动 ORM:SQL 自己写,结果映射交给框架。接口方法 + 注解即可工作,@Mapper 让 Spring 生成代理实现。
  • 简单 SQL 用注解 @Select/@Insert/@Update/@Delete;复杂或动态 SQL 改用 XML。
  • #{} 是预编译占位符,安全防 SQL 注入;${} 直接字符串拼接,仅可用于表名/列名等无法占位处,有注入风险慎用。
  • 多参数方法必须用 @Param("xx") 命名参数,否则 #{} 取不到值。
  • 实体字段驼峰与数据库列下划线不对应时,开启驼峰映射 map-underscore-to-camel-case: true,否则属性全为 null。
  • 易错点:接口忘加 @Mapper;返回值类型与 SQL 结果不匹配;#{} 里写错参数名。
java
@Mapper
public interface EmpMapper {
    @Select("SELECT * FROM emp WHERE id = #{id}")
    Emp findById(Integer id);

    @Insert("INSERT INTO emp(name, dept_id) VALUES(#{name}, #{deptId})")
    int insert(Emp emp);
}
yaml
# application.yml
mybatis:
  configuration:
    map-underscore-to-camel-case: true

4. 学习xml文件配置 ​

  • XML 映射文件用于复杂与动态 SQL;namespace 必须等于 Mapper 接口全限定名,id 等于方法名,写错启动即报错。
  • 动态 SQL 三件套:<where> + <if> 拼查询条件(自动处理多余的 AND)、<set> 拼更新、<foreach> 批量操作。
  • <if test="name != null and name != ''"> 里写的是参数名,不要带 #{};字符串判空要同时判断 != ''。
  • 特殊字符 < > 需转义或用 <![CDATA[ ... ]]>;LIKE 模糊查询用 CONCAT('%', #{name}, '%') 防注入。
  • 用 mybatis.mapper-locations: classpath:mapper/*.xml 指定 XML 位置,接口与 XML 默认要求同名同包。
  • resultMap 解决字段名不一致,以及一对一 association、一对多 collection 关联映射。
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.EmpMapper">
    <select id="listByCondition" resultType="com.example.pojo.Emp">
        SELECT * FROM emp
        <where>
            <if test="name != null and name != ''">
                AND name LIKE CONCAT('%', #{name}, '%')
            </if>
            <if test="deptId != null">
                AND dept_id = #{deptId}
            </if>
        </where>
    </select>

    <delete id="deleteBatch">
        DELETE FROM emp WHERE id IN
        <foreach collection="ids" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>
</mapper>
yaml
mybatis:
  mapper-locations: classpath:mapper/*.xml

5. 学习了Maven后端代码管理 ​

  • Maven 是项目构建与依赖管理工具,一切写在 pom.xml:坐标(groupId/artifactId/version)+ 依赖 + 插件,坐标唯一标识一个依赖。
  • 依赖引入 spring-boot-starter-* 会自动带出传递依赖;scope 控制生效范围(compile/test/provided);Spring Boot 父工程统一锁定依赖版本。
  • 生命周期:clean → compile → test → package → install;install 把构件装进本地仓库供其它模块引用,跳过测试用 -DskipTests。
  • 仓库链路:本地 ~/.m2 缓存 → 中央/私服仓库;国内在 settings.xml 配阿里云镜像加速下载。
  • 多模块工程:父 pom 用 <modules> 聚合、<dependencyManagement> 统一版本,子模块用 <parent> 继承。
  • 易错点:依赖下载失败先查镜像与网络;打包缺依赖多为 scope 配错;依赖版本冲突用 exclusions 排除传递依赖。
xml
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.18</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
xml
<!-- settings.xml:国内镜像加速 -->
<mirror>
    <id>aliyunmaven</id>
    <mirrorOf>central</mirrorOf>
    <url>https://maven.aliyun.com/repository/public</url>
</mirror>

6. 学习Restful开发规范 ​

  • REST 风格:URL 只表示资源(用名词、复数),HTTP 方法表示动作——GET 查、POST 增、PUT 改、DELETE 删。
  • 具体资源用路径参数定位:GET /users/1 查单个;集合操作用 GET /users,不要在 URL 里写动词(如 getUserList)。
  • 状态码语义化:200 成功、201 创建成功、404 资源不存在、500 服务端错误,前端靠它快速定位问题。
  • 前后端分离下统一 JSON 交互 + Result 封装;接口路径习惯加 /api 前缀,如 /api/users。
  • 易错点:URL 出现动词、用 GET 做删除、修改不区分 PUT/POST、@PathVariable 忘写与占位符匹配。
java
@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping              // GET /api/users     列表
    public Result list() { ... }

    @GetMapping("/{id}")     // GET /api/users/1   单个
    public Result get(@PathVariable Long id) { ... }

    @PostMapping             // POST /api/users    新增
    public Result save(@RequestBody User user) { ... }

    @PutMapping("/{id}")     // PUT /api/users/1   修改
    public Result update(@PathVariable Long id, @RequestBody User user) { ... }

    @DeleteMapping("/{id}")  // DELETE /api/users/1 删除
    public Result delete(@PathVariable Long id) { ... }
}

7. 学习LogBack日志技术 ​

  • Spring Boot 默认集成 Logback;级别由低到高 trace < debug < info < warn < error,只输出不低于配置级别的日志。
  • Lombok @Slf4j 直接获得 log 对象,用 {} 占位符传参,避免字符串拼接(性能更好、可读性更好)。
  • 配置:logging.level.包名=debug 控制级别(想看 MyBatis SQL 就调 debug);logging.file.name 输出到文件。
  • 生产常用 logback-spring.xml:ConsoleAppender 控制台 + RollingFileAppender 按天/大小滚动归档,自定 pattern。
  • 关键操作(登录、增删改)打 info,异常打 log.error(..., e) 带堆栈,不打印密码/token 等敏感信息。
  • 易错点:级别配太高看不到想要的日志;文件名写 logback.xml 则无法使用 Spring profile 扩展,要用 logback-spring.xml。
java
@Slf4j
@Service
public class UserService {
    public User getById(Integer id) {
        log.info("查询用户, id={}", id);
        try {
            return userMapper.findById(id);
        } catch (Exception e) {
            log.error("查询用户失败, id={}", id, e);
            throw e;
        }
    }
}
xml
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>logs/springboot.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>logs/springboot.%d{yyyy-MM-dd}.log</fileNamePattern>
        <maxHistory>30</maxHistory>
    </rollingPolicy>
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

8. 学习PageHelper分页插件 ​

  • PageHelper 是 MyBatis 分页插件,通过拦截器自动在 SQL 后拼 LIMIT,无需手写分页 SQL。
  • 用法:PageHelper.startPage(pageNum, pageSize) 后【紧接着】第一条查询语句才生效,中间不能插其它 SQL。
  • 结果用 PageInfo 包装或强转 Page<T>,取 total(总条数)、pages(总页数)、list(当前页数据)。
  • reasonable: true 时页码越界自动修正回第一/最后一页,防止超大页码拖垮数据库。
  • 入参校验:pageNum/pageSize 为空或 ≤0 时给默认值(如 1/10)。
  • 易错点:startPage 后执行了多条 SQL 导致分页错乱;与 Druid 等其它拦截器顺序冲突;Spring Boot 3 注意 MyBatis 版本兼容。
java
// 必须紧跟查询语句
PageHelper.startPage(pageNum, pageSize);
List<Emp> list = empMapper.listByCondition(emp);
PageInfo<Emp> pageInfo = new PageInfo<>(list);
// pageInfo.getTotal(); pageInfo.getPages(); pageInfo.getList();
java
// 或者直接强转 Page
Page<Emp> page = (Page<Emp>) empMapper.listByCondition(emp);
page.getTotal();
page.getPages();
yaml
pagehelper:
  helper-dialect: mysql
  reasonable: true

9. Spring事务管理 ​

  • 事务保证一组写操作"要么全部成功、要么全部回滚",满足 ACID;Spring 用 @Transactional 声明式管理,免去手动开启/提交/回滚。
  • 注解加在 Service 实现类的【方法】上最合适,并配 rollbackFor = Exception.class,让所有异常(含 checked 异常)都回滚。
  • 默认只回滚 RuntimeException;事务失效三坑:异常被 try-catch 吞掉、方法非 public、同类内部 this.xxx() 调用(不走代理)。
  • 传播行为 propagation:REQUIRED(默认,加入当前事务)、REQUIRES_NEW(另开新事务,常用于记账等独立操作)。
  • 纯查操作不用加事务;涉及多表写入(转账、下单)必须包在事务里,且事务方法要短,避免长事务占用连接。
java
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountMapper accountMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void transfer(Integer fromId, Integer toId, BigDecimal amount) {
        accountMapper.deduct(fromId, amount);   // 扣款
        // 若这里抛出异常,上面的扣款自动回滚
        accountMapper.add(toId, amount);        // 加款
    }
}

10. 阿里云OSS存储 ​

  • OSS 是对象存储服务:文件传到云端,数据库只存访问 URL,减轻服务器磁盘与带宽压力。
  • 核心概念:Bucket(存储空间)、Endpoint(地域节点)、AccessKeyId/AccessKeySecret(身份凭证)、objectName(对象键)。
  • 上传三步:new OSSClientBuilder().build(endpoint, ak, sk) → putObject(bucket, objectName, inputStream) → shutdown()。
  • 文件名用 UUID + 原文件名重命名,避免同名覆盖;用日期做目录前缀分层,如 upload/2026/09/xxx.jpg。
  • 凭证通过 @Value 注入或环境变量读取,绝不能硬编码进代码;AccessKey 泄露会被刷流量,务必保密。
  • 易错点:Endpoint 与 Bucket 地域不一致会报错;Bucket 默认私有,需签名 URL(generatePresignedUrl)才能访问;大文件上传用服务端直传或 STS 临时凭证;删除用 ossClient.deleteObject。
java
@Value("${aliyun.oss.endpoint}")       private String endpoint;
@Value("${aliyun.oss.accessKeyId}")    private String accessKeyId;
@Value("${aliyun.oss.accessKeySecret}") private String accessKeySecret;
@Value("${aliyun.oss.bucketName}")     private String bucketName;

public String upload(MultipartFile file) throws IOException {
    OSS ossClient = new OSSClientBuilder()
            .build(endpoint, accessKeyId, accessKeySecret);
    String objectName = "upload/" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM")) + "/"
            + UUID.randomUUID().toString().replace("-", "")
            + "-" + file.getOriginalFilename();
    ossClient.putObject(bucketName, objectName, file.getInputStream());
    ossClient.shutdown();
    return "https://" + bucketName + "." + endpoint + "/" + objectName;
}
yaml
aliyun:
  oss:
    endpoint: oss-cn-hangzhou.aliyuncs.com
    access-key-id: your-access-key-id
    access-key-secret: your-access-key-secret
    bucket-name: your-bucket-name

以「技术栈」为核心的个人知识库