SpringBoot Web 入门核心知识点(快速开发案例 + 分层解耦实战)

大家好!SpringBoot 作为 Spring 生态的 "轻量级脚手架",凭借自动配置、起步依赖、内嵌服务器等特性,彻底简化了 Java Web 开发的繁琐配置。很多新手从 SSM 框架转向 SpringBoot 时,既好奇它的 "零配置" 魔力,也容易在 "分层开发" 和 "解耦设计" 上踩坑。

这篇文章会从 SpringBoot Web 的核心原理入手,通过一个完整的用户管理案例实现快速入门,再深入讲解分层解耦的设计思想与实践技巧,帮你夯实 SpringBoot Web 开发的基础。

一、SpringBoot Web 核心认知:为什么它是 Web 开发的首选?

1.1 什么是 SpringBoot Web?

SpringBoot Web 是 SpringBoot 针对 Web 开发提供的一站式解决方案,它整合了Spring MVC、Tomcat(内嵌服务器)、Jackson(JSON 序列化) 等核心组件,通过起步依赖(Starter) 实现一键引入,通过自动配置省去繁琐的 XML 或 Java 配置,让开发者专注于业务逻辑。

1.2 SpringBoot Web 的核心优势

  • 零配置启动:无需手动配置 Spring MVC 的 DispatcherServlet、视图解析器等,启动类一键运行;
  • 内嵌服务器:内置 Tomcat、Jetty 等服务器,无需手动部署,直接运行 JAR 包即可启动 Web 服务;
  • 起步依赖spring-boot-starter-web一个依赖即可引入 Web 开发所需的所有组件,避免依赖版本冲突;
  • 无缝集成:与 MyBatis、Redis、Spring Data JPA 等框架无缝集成,降低技术栈整合成本。

1.3 核心概念:起步依赖与自动配置

  • 起步依赖(Starter) :是 Maven 的依赖集合,如spring-boot-starter-web包含了 Spring MVC、Tomcat、Jackson 等依赖,只需引入一个即可;
  • 自动配置(AutoConfiguration) :SpringBoot 通过@EnableAutoConfiguration注解,根据类路径下的依赖自动配置 Bean(如检测到spring-webmvc则自动配置 DispatcherServlet)。

二、SpringBoot Web 快速入门:搭建第一个 Web 项目

2.1 环境准备

  • JDK:8 及以上(推荐 JDK 8/11);
  • 构建工具:Maven/Gradle(本文使用 Maven);
  • IDE:IntelliJ IDEA 2020+;
  • SpringBoot 版本:3.5.0。

2.2 创建 SpringBoot Web 项目(IDEA 版)

方式 1:通过 Spring Initializr 创建
  1. 打开 IDEA,点击File > New > Project

  2. 左侧选择Spring Initializr,选择 JDK 版本,点击Next

  3. 填写项目信息:

    • Group:com.xxx(如com.demo);
    • Artifact:springboot-web-demo
    • Type:Maven Project
    • Java Version:17
  4. 选择依赖:在Web分类下勾选Spring Web,点击Next

  5. 选择项目存储路径,点击Create,IDEA 会自动下载依赖并生成项目结构。

方式 2:手动创建 Maven 项目
  1. 创建普通 Maven 项目,在pom.xml中添加 SpringBoot 父依赖和 Web 起步依赖:
xml 复制代码
<!-- SpringBoot父依赖(统一版本管理) -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.5.0</version>
    <relativePath/>
</parent>

<dependencies>
    <!-- Web起步依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

<!-- 打包插件(可执行JAR包) -->
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

2.3 核心项目结构

SpringBoot Web 项目遵循标准的 Maven 结构,核心目录如下:

plaintext 复制代码
springboot-web-demo
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── demo
│   │   │           ├── SpringbootWebDemoApplication.java  // 启动类
│   │   │           ├── controller  // 控制器层(接收请求)
│   │   │           ├── service     // 服务层(业务逻辑)
│   │   │           ├── mapper      // 数据访问层(操作数据库)
│   │   │           └── entity      // 实体类(封装数据)
│   │   └── resources
│   │       ├── application.properties  // 配置文件
│   │       ├── static  // 静态资源(CSS/JS/图片)
│   │       └── templates  // 模板文件(Thymeleaf等)
│   └── test  // 测试目录
└── pom.xml

2.4 编写第一个接口:Hello World

步骤 1:编写启动类

启动类是 SpringBoot 项目的入口,需添加@SpringBootApplication注解:

java 复制代码
package com.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// 包含@Configuration、@EnableAutoConfiguration、@ComponentScan三个注解
@SpringBootApplication
public class SpringbootWebDemoApplication {
    public static void main(String[] args) {
        // 启动SpringBoot应用
        SpringApplication.run(SpringbootWebDemoApplication.class, args);
    }
}
步骤 2:编写控制器(Controller)

controller包下创建HelloController,处理 HTTP 请求:

java 复制代码
package com.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController //标识当前类可以处理请求,并且会创建当前类对象加入spring容器(内存)
public class HelloController {
    //处理指定资源路径的请求
    @RequestMapping("/hello")
    public String hello(String name){//这里的参数name必须要与前端传递的参数名字name一样,就可以接收数据
        System.out.println("后端处理/hello请求,并且接收数据:name="+name);
        return "hello " + name;
    }
}
步骤 3:启动项目并测试
  1. 右键启动类,选择Run 'SpringbootWebDemoApplication'
  2. 控制台出现Tomcat started on port(s): 8080 (http)表示启动成功;
  3. 打开浏览器,访问http://localhost:8080/hello/SpringBoot Web,页面显示Hello SpringBoot Web

三、实战案例:用户管理系统(CRUD)

接下来通过一个基于SpringBoot开发的web程序------用户管理系统,实现用户列表的渲染展示,让你快速掌握 SpringBoot Web 的开发流程。

3.1 创建实体类

pojo包下创建User类,封装用户数据:

java 复制代码
package com.tgt.pojo;

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

import java.time.LocalDateTime;

@Data//生成get/set/toString/hashCode/equals方法
@NoArgsConstructor//生成无参构造方法
@AllArgsConstructor//生成全参构造方法
public class User {
    private Integer id;
    private String username;
    private String password;
    private String name;
    private Integer age;
    private LocalDateTime updateTime;
}

注意 :需在pom.xml中添加 Lombok 依赖(简化代码):

xml 复制代码
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

3.2 编写前端HTML

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户列表数据</title>
    <style>
        /*定义css,美化表格*/
        table{
            border-collapse: collapse;
            width: 100%;
            margin-top: 20px;
            border: 1px solid #ccc;
            text-align: center;
            font-size: 14px;
        }
        tr {
            height: 40px;
        }
        th,td{
            border: 1px solid #ccc;
        }
        thead{
            background-color: #e8e8e8;
        }
        h1{
            text-align: center;
            font-family: 楷体;
        }
    </style>
</head>
<body>
    <div id="app">
        <h1>用户列表数据</h1>
        <!--定义一个表格,包括6列,分别是: ID, 用户名, 密码, 姓名, 年龄, 更新时间-->
        <table>
            <thead>
                <tr>
                    <th>ID</th>
                    <th>用户名</th>
                    <th>密码</th>
                    <th>姓名</th>
                    <th>年龄</th>
                    <th>更新时间</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="user in userList">
                    <td>{{user.id}}</td>
                    <td>{{user.username}}</td>
                    <td>{{user.password}}</td>
                    <td>{{user.name}}</td>
                    <td>{{user.age}}</td>
                    <td>{{user.updateTime}}</td>
                </tr>
            </tbody>
        </table>
    </div>

    <!--引入axios-->
    <script src="js/axios.min.js"></script>
    <script type="module">
        import { createApp } from './js/vue.esm-browser.js'
        createApp({
            data() {
                return {
                    userList: []
                }
            },
            methods: {
                async search(){
                    const result = await axios.get('/list');
                    this.userList = result.data;
                }
            },
            mounted() {
                this.search();
            }
        }).mount('#app')
    </script>
</body>
</html>

3.3 编写控制器(Controller)

controller包下创建UserController,处理查询用户列表请求:

java 复制代码
@RestController//标识当前类可以处理请求,并且创建当前类对象加入spring容器中
public class UserController {
    /**
     * 处理查询用户列表请求
     * @return List<User>
     *
     * 方法返回List<User>,spring框架会自动将其转换为json字符串返回给前端
     *   '[{"id":xx,"username":xx},{"id":xx,"username":xx},...]'
     */
    @RequestMapping("/list")
    public List<User> list(){
        //定义List<User>用于存储用户列表数据
        List<User> users = new ArrayList<>();

        //1.创建输入流对象操作user.txt文件
        try (
                // InputStream in = new FileInputStream("day04-02-web-springboot/src/main/resources/user.txt")
                //上面这样写在web项目中不可以,因为web项目会部署给tomcat,目录结构就不是这样的结构
                // 解决方案:在web项目要先获取类路径target/classes目录下资源文件的固定语法:类加载器对象.getResourceAsStream("资源文件名")
                InputStream in = UserController.class.getClassLoader().getResourceAsStream("user.txt")
        ){
            //2.读取文件中的数据,利用hutool工具一次性读取所有行数据封装到List<String>
            List<String> rows = IoUtil.readUtf8Lines(in, new ArrayList<>());

            //3.将List<String>转换为List<User>

            // 遍历List<String>的每个元素
            for (String s : rows) {
                // 将类似1,daqiao,1234567890,大乔,22,2024-07-15 15:05:45按照","分隔得到数组
                String[] split = s.split(",");

                // 将分隔出的数据封装到User对象中
                User user = new User(
                        Integer.valueOf(split[0]),
                        split[1],
                        split[2],
                        split[3],
                        Integer.valueOf(split[4]),
                        LocalDateTime.parse(split[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
                        //  将字符串转换为LocalDateTime
                        //  yyyy 格式化为4个数字的年2025, yy两个数字的年25
                        //  MM 格式化为2个数字的月
                        //  dd 格式化为2个数字的日
                        //  HH 格式化为24小时,hh格式为12小时
                        //  mm 格式化分钟
                        //  ss 格式化秒
                );

                // 将user对象添加List<User>集合中
                users.add(user);
            }
            return users;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

3.4 编写user.txt

txt 复制代码
1,daqiao,1234567890,大乔,22,2024-07-15 15:05:45
2,xiaoqiao,1234567890,小乔,18,2024-07-15 15:12:09
3,diaochan,1234567890,貂蝉,21,2024-07-15 15:07:16
4,lvbu,1234567890,吕布,28,2024-07-16 10:05:15
5,zhaoyun,1234567890,赵云,27,2024-07-16 11:03:28
6,zhangfei,1234567890,张飞,31,2024-07-16 11:03:28
7,guanyu,1234567890,关羽,34,2024-07-16 12:05:12
8,liubei,1234567890,刘备,37,2024-07-16 15:03:28

3.5 编写启动类

java 复制代码
package com.tgt;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication//标识当前类为启动类
public class WebApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebApplication.class,args);//启动程序
    }
}

运行main方法,启动服务器端程序、

在浏览器中输入(http://localhost:8080/user.html)

四、分层解耦:理解 SpringBoot 的设计思想

在我们进行程序设计以及程序开发时,尽可能让每一个接口、类、方法的职责更单一些(单一职责原则)。

由此引申出Controller、Service、Mapper 三层架构,这是 SpringBoot Web 开发的标准分层方式,其核心目的是解耦,让代码更易维护、扩展和测试。

4.1 为什么要分层?

在传统的单体代码中,所有逻辑写在一个类中,会导致:

  • 代码耦合度高:修改一个功能可能影响其他功能;
  • 可读性差:几百行代码混在一起,难以理解;
  • 可测试性差:无法单独测试某个功能点。

分层开发通过单一职责原则,将不同功能拆分到不同层级,每个层级只做一件事:

  • Controller 层:只负责接收请求、返回响应,不处理业务逻辑;
  • Service 层:只负责业务逻辑处理,不关心请求和数据存储;
  • Mapper 层:只负责数据访问,不处理业务逻辑。

4.2 分层架构的核心职责

层级 核心注解 核心职责 依赖关系
Controller(控制层) @RestController@RequestMapping 接收 HTTP 请求、参数校验、返回响应 依赖 Service 层
Service(服务层) @Service 业务逻辑处理、事务管理、数据校验 依赖 Mapper 层
Mapper/Dao(数据访问层) @Repository 数据库 CRUD 操作、数据映射 无(被 Service 层依赖)
Entity/Pojo(实体层) @Data(Lombok) 封装数据(对应数据库表 / 请求响应体) 被所有层级使用

4.3 实战案例示例:重写上一节案例的Controller

4.3.1 数据访问层:负责数据的访问操作,包含数据的增、删、改、查

com.tgt.dao中创建UserDao接口,代码如下

java 复制代码
package com.tgt.dao;

import java.util.List;

/**
 * @Description 用户数据数据访问接口
 * @Author tgt
 * @Date 2025-10-21
 */
public interface IUserDao {

    /**
     * 解析文件返回数据列表
     *
     * @return
     */
    List<String> GetDao();
}

com.tgt.dao.impl 中创建UserDaoImpl接口,代码如下:

java 复制代码
package com.tgt.dao.impl;

import cn.hutool.core.io.IoUtil;
import com.tgt.controller.UserController;
import com.tgt.dao.IUserDao;
import com.tgt.pojo.User;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

//数据访问层DAO:操作数据增删改查
public class UserDaoImpl implements IUserDao {
    /**
     * 解析文件返回数据列表
     *
     * @return
     */
    @Override
    public List<String> GetDao() {
        //定义List<User>用于存储用户列表数据
        List<User> users = new ArrayList<>();

        //1.创建输入流对象操作user.txt文件
        try (
                InputStream in = UserController.class.getClassLoader().getResourceAsStream("user.txt")
        ) {
            //2.读取文件中的数据,利用hutool工具一次性读取所有行数据封装到List<String>
            List<String> rows = IoUtil.readUtf8Lines(in, new ArrayList<>());

            return rows;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}
4.3.2 业务逻辑层:处理具体的业务逻辑

com.tgt.service中创建UserSerivce接口,代码如下:

java 复制代码
package com.tgt.service;

import com.tgt.pojo.User;
import java.util.List;

public interface IUserService {
    /**
     * 获取用户列表业务方法
     * @return
     */
    List<User> GetUserList();
}

com.tgt.service.impl 中创建UserSerivceImpl接口,代码如下:

java 复制代码
package com.tgt.service.Impl;

import com.tgt.dao.IUserDao;
import com.tgt.dao.impl.UserDaoImpl;
import com.tgt.pojo.User;
import com.tgt.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;


public class UserServiceImpl implements IUserService {

    //定义数据访问对象
    private IUserDao userDao =new IUserService();

    /**
     * 获取用户列表业务方法
     *
     * @return
     */
    @Override
    public List<User> GetUserList() {

        List<User> users = new ArrayList<>();

        //1.调用数据访问层方法获取磁盘数据List<String>
        List<String> rows = userDao.GetDao();

        //2.将List<String>转换为List<User>
        // 遍历List<String>的每个元素
        for (String s : rows) {
            // 将类似1,daqiao,1234567890,大乔,22,2024-07-15 15:05:45按照","分隔得到数组
            String[] split = s.split(",");

            // 将分隔出的数据封装到User对象中
            User user = new User(
                    Integer.valueOf(split[0]),
                    split[1],
                    split[2],
                    split[3],
                    Integer.valueOf(split[4]),
                    LocalDateTime.parse(split[5], DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
            );

            // 将user对象添加List<User>集合中
            users.add(user);
        }
        return users;
    }
}
4.3.3 控制层:接收前端发送的请求,对请求进行处理,并响应数据

com.itheima.controller 中创建UserController类,代码如下:

java 复制代码
package com.tgt.controller;

import cn.hutool.core.io.IoUtil;
import com.tgt.pojo.User;
import com.tgt.service.IUserService;
import com.tgt.service.Impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;


@RestController//标识当前类可以处理请求,并且创建当前类对象加入spring容器中
public class UserController {

    //定义业务对象
    
    private IUserService userService = new UserServiceImpl();

    /**
     * 处理查询用户列表请求
     *
     * @return List<User>
     */
    @RequestMapping("/list")
    public List<User> list() {
        return userService.GetUserList();
    }
}

4.3 解耦的关键:依赖注入与控制反转

之前我们在编写代码时,需要什么对象,就直接new一个就可以了。 这种做法呢,层与层之间代码就耦合了,当service层的实现变了之后, 我们还需要修改controller层的代码。 那应该怎么解耦呢?

SpringBoot 的依赖注入(DI)控制反转(IOC) 是实现分层解耦的核心机制:

  • 控制反转(IOC) :将对象的创建权交给 Spring 容器,而非手动new对象;
  • 依赖注入(DI) :Spring 容器自动将依赖的对象注入到当前类中(如@Autowired注入 UserService)。
示例:传统方式 vs 依赖注入

传统方式(耦合度高)

java 复制代码
// Controller中手动创建Service实例,耦合度高
@RestController
public class UserController {
    private UserService userService = new UserServiceImpl();
}

依赖注入方式(解耦)

java 复制代码
// 由Spring容器管理对象,通过@Autowired注入,降低耦合
@RestController
public class UserController {
    @Autowired
    private UserService userService;
}

4.4 进一步解耦:面向接口编程

我们先定义接口再实现类,这是面向接口编程的思想,其优势在于:

  • 降低耦合:Controller 层只依赖 Service 接口,不依赖具体实现类;
  • 便于扩展:如需修改业务逻辑,只需新增实现类,无需修改 Controller 代码;
  • 便于测试:可通过 Mock 对象模拟 Service 接口,实现单元测试。

4.5 分层解耦的最佳实践

  1. 层级边界清晰:禁止跨层级调用(如 Controller 直接调用 Mapper);
  2. 面向接口编程:Service 层优先定义接口,实现类负责具体逻辑;
  3. 依赖注入 :使用@Autowired或构造器注入,避免手动创建对象;
  4. 单一职责:每个类 / 方法只做一件事,避免 "全能类";
  5. 异常统一处理:通过全局异常处理器统一处理各层级的异常。

五、常见问题与解决方案

5.1 启动项目时提示 "找不到 Bean"

  • 原因 :组件未被 Spring 扫描(如忘记加@Service/@Repository注解);
  • 解决 :检查类上的注解是否正确,确保启动类的包路径包含所有组件(@SpringBootApplication默认扫描当前包及子包)。

5.2 接口返回 404

  • 原因 :请求路径错误、Controller 注解错误(如用@Controller而非@RestController);
  • 解决 :检查请求路径与@RequestMapping是否一致,确保返回数据的注解正确。

5.3 请求体传参时提示 "参数绑定失败"

  • 原因:请求体格式错误(非 JSON)、实体类字段与请求体字段不匹配;
  • 解决 :确保请求体为 JSON 格式,实体类字段名称与请求体一致(或使用@JsonProperty注解映射)。

六、总结与延伸

6.1 核心总结

  1. SpringBoot Web 通过起步依赖和自动配置,实现了 Web 开发的 "零配置" 启动;
  2. 标准的分层架构为Controller(控制层)、Service(服务层)、Mapper(数据访问层) ,各层级职责单一;
  3. 依赖注入和面向接口编程是实现分层解耦的关键,能大幅提升代码的可维护性;
  4. 实战案例中的 CRUD 接口是 Web 开发的基础,掌握后可扩展到更复杂的业务场景。

SpringBoot Web 的入门门槛低,但想要写出优雅、高效的代码,需要深入理解分层解耦的设计思想。建议在实际项目中多动手实践,逐步掌握 SpringBoot 的高级特性,最终形成自己的开发体系。

相关推荐
Dylan的码园4 小时前
链表与LinkedList
java·数据结构·链表
【非典型Coder】4 小时前
JVM 垃圾收集器中的记忆集与读写屏障
java·开发语言·jvm
feathered-feathered4 小时前
Redis【事务】(面试相关)与MySQL相比较,重点在Redis事务
android·java·redis·后端·mysql·中间件·面试
大大大大物~4 小时前
JVM 之 内存溢出实战【OOM? SOF? 哪些区域会溢出?堆、虚拟机栈、元空间、直接内存溢出时各自的特点?以及什么情况会导致他们溢出?并模拟溢出】
java·jvm·oom·sof
仪***沿4 小时前
探索三相、五相电机的容错控制奥秘
java
码界奇点5 小时前
基于Spring MVC与JdbcTemplate的图书管理系统设计与实现
java·spring·车载系统·毕业设计·mvc·源代码管理
⑩-5 小时前
拦截器注册InterceptorRegistry 实现讲解
java·spring
DKunYu5 小时前
3.负载均衡-LoadBalance
java·运维·spring cloud·微服务·负载均衡
第二只羽毛5 小时前
外卖订餐管理系统
java·大数据·开发语言·算法