Java Spring Boot 全面使用教程

Spring Boot 全面使用教程

本文将从常见的 Spring Boot 项目实践出发,逐步讲解各个关键点的 注解、用法和配置,涵盖以下方面:

  • 控制器 Controller
  • Service 层
  • Mapper 与 MyBatis XML
  • 自定义配置注入
  • 依赖注入 & IoC
  • Lombok 使用
  • 日志记录(Slf4j + logback.xml)
  • 全局异常处理

1. 控制器 Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
@RequestMapping("/user")
public class UserController {

private final UserService userService;

// 构造器注入推荐(避免循环依赖 & 测试方便)
public UserController(UserService userService) {
this.userService = userService;
}

@PostMapping("/register")
public ResponseEntity<String> register(@RequestBody UserDTO userDTO) {
userService.register(userDTO);
return ResponseEntity.ok("用户注册成功!");
}
}

常用注解

  • @RestController:组合注解 = @Controller + @ResponseBody,返回 JSON。
  • @RequestMapping:定义类或方法的访问路径。
  • @PostMapping / @GetMapping:请求方法映射。
  • @RequestBody:接收 JSON 请求体并绑定到对象。

2. Service 层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
public class UserService {

private final UserMapper userMapper;

public UserService(UserMapper userMapper) {
this.userMapper = userMapper;
}

public void register(UserDTO userDTO) {
User user = new User();
user.setUsername(userDTO.getUsername());
user.setPassword(userDTO.getPassword());
userMapper.insertUser(user);
}
}

常用注解

  • @Service:标记业务逻辑层 Bean。
  • @Transactional:事务管理(可用于方法或类)。

3. Mapper 与 MyBatis XML

Mapper 接口

1
2
3
4
5
@Mapper
public interface UserMapper {
void insertUser(User user);
User findByUsername(String username);
}

Mapper XML(resources/mapper/UserMapper.xml)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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.demo.mapper.UserMapper">

<insert id="insertUser" parameterType="com.example.demo.model.User">
INSERT INTO user (username, password)
VALUES (#{username}, #{password})
</insert>

<select id="findByUsername" parameterType="String" resultType="com.example.demo.model.User">
SELECT id, username, password
FROM user
WHERE username = #{username}
</select>

</mapper>

4. 自定义配置注入

application.yml

1
2
3
app:
name: MySpringApp
version: 1.0.0

配置类

1
2
3
4
5
6
7
@Component
@ConfigurationProperties(prefix = "app")
@Data
public class AppConfig {
private String name;
private String version;
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
public class ConfigController {

private final AppConfig appConfig;

public ConfigController(AppConfig appConfig) {
this.appConfig = appConfig;
}

@GetMapping("/config")
public String getConfig() {
return appConfig.getName() + " - " + appConfig.getVersion();
}
}

5. 依赖注入 & IoC

Spring Boot 的 IoC 容器(ApplicationContext)负责管理 Bean 的生命周期。

常见注入方式

  • 构造器注入(推荐)
  • Setter 注入
  • 字段注入(@Autowired

6. Lombok 使用

pom.xml 中加入:

1
2
3
4
5
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>

常用注解

1
2
3
4
5
@Data       // 生成 getter/setter/toString/hashCode/equals
@NoArgsConstructor
@AllArgsConstructor
@Builder // 链式构建对象
@Slf4j // 日志

示例

1
2
3
4
5
6
7
8
9
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class User {
private Long id;
private String username;
private String password;
}

7. Slf4j 日志记录 & logback.xml 配置

使用日志

1
2
3
4
5
6
7
@Slf4j
@Service
public class UserService {
public void register(UserDTO userDTO) {
log.info("注册用户: {}", userDTO.getUsername());
}
}

logback-spring.xml 配置(resources/ 下)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<configuration>

<property name="LOG_PATH" value="logs"/>

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/app.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>

<root level="INFO">
<appender-ref ref="FILE"/>
</root>

</configuration>

8. 全局异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("服务器异常: " + e.getMessage());
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<String> handleValidationException(MethodArgumentNotValidException e) {
return ResponseEntity.badRequest()
.body("参数错误: " + e.getBindingResult().getFieldError().getDefaultMessage());
}
}

常用注解

  • @RestControllerAdvice:全局异常处理 + JSON 返回。
  • @ExceptionHandler:指定异常类型处理。

9. AOP 编程

AOP 概念

  • AOP (Aspect Oriented Programming,面向切面编程):通过预编译方式和运行期动态代理实现程序功能的统一维护。
  • 核心思想:把与业务逻辑无关的功能(如日志、事务、安全检查)从业务代码中分离出来,形成独立的切面。

核心术语

  • 切面 (Aspect):横切关注点的模块化,如日志切面、权限切面。
  • 连接点 (JoinPoint):程序执行的某个点,如方法调用、异常抛出。
  • 切点 (Pointcut):定义哪些连接点需要织入,如指定某个包下的所有方法。
  • 通知 (Advice):切面在连接点执行的操作,包括:
    • @Before:方法执行前
    • @After:方法执行后(无论是否异常)
    • @AfterReturning:方法返回后
    • @AfterThrowing:方法抛出异常时
    • @Around:环绕通知(可控制方法执行前后逻辑)
  • 织入 (Weaving):将切面应用到目标对象的过程。
  • 代理对象:Spring AOP 基于 JDK 动态代理CGLIB 生成代理对象。

Spring Boot 中启用 AOP

  1. 引入依赖:
1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 启用 AOP(可选,默认已启用):
1
2
3
@EnableAspectJAutoProxy
@SpringBootApplication
public class DemoApplication {}

定义切面

日志切面示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Aspect
@Component
@Slf4j
public class LoggingAspect {

// 定义切点:匹配 com.example.demo.service 包下的所有方法
@Pointcut("execution(* com.example.demo.service..*(..))")
public void serviceMethods() {}

@Before("serviceMethods()")
public void logBefore(JoinPoint joinPoint) {
log.info("调用方法: {}", joinPoint.getSignature().toShortString());
}

@AfterReturning(pointcut = "serviceMethods()", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
log.info("方法返回: {} -> {}", joinPoint.getSignature().toShortString(), result);
}

@AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {
log.error("方法异常: {} -> {}", joinPoint.getSignature().toShortString(), ex.getMessage());
}

@Around("serviceMethods()")
public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed(); // 执行目标方法
long time = System.currentTimeMillis() - start;
log.info("方法耗时: {} ms", time);
return result;
}
}

切点表达式常见写法

  • execution(* com.example..*(..)):匹配 com.example 包及子包下所有方法。
  • execution(public * *(..)):匹配所有 public 方法。
  • @annotation(org.springframework.web.bind.annotation.GetMapping):匹配带有特定注解的方法。
  • within(com.example.service..*):匹配某个包下的所有类。

常见应用场景

  1. 日志记录(方法调用、入参、返回值、耗时)
  2. 权限控制(在方法调用前检查权限)
  3. 事务管理(Spring 已内置 @Transactional 基于 AOP)
  4. 异常统一处理(捕获异常,转换成统一响应格式)
  5. 性能监控(统计方法执行时间)

高级用法

1. 自定义注解 + AOP

1
2
3
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireLogin {}

切面实现:

1
2
3
4
5
6
7
8
9
10
11
@Aspect
@Component
public class AuthAspect {
@Before("@annotation(com.example.demo.annotation.RequireLogin)")
public void checkLogin() {
// 检查是否已登录
if (!SecurityContext.isLogin()) {
throw new RuntimeException("未登录");
}
}
}

2. Order 控制切面优先级

1
2
3
4
@Aspect
@Component
@Order(1) // 数字越小优先级越高
public class FirstAspect {}

AOP vs 拦截器

特点 AOP Spring MVC 拦截器
粒度 方法级 URL 请求级
使用场景 日志、事务、权限 认证、跨域、限流
灵活性 支持注解、自定义切点 针对请求生命周期

Spring Boot 参数绑定与返回结果教程

1. 参数绑定方式

Spring Boot 提供了多种方式将请求参数绑定到方法参数上。


1.1 路径参数(PathVariable)

1
2
3
4
@GetMapping("/user/{id}")
public ResponseEntity<String> getUserById(@PathVariable Long id) {
return ResponseEntity.ok("用户ID: " + id);
}
  • @PathVariable:绑定路径中的 {} 参数。
  • 支持 可选参数(可用 required=falseOptional)。
1
2
3
4
@GetMapping("/user/{id}")
public ResponseEntity<String> getUserOptional(@PathVariable Optional<Long> id) {
return ResponseEntity.ok("用户ID: " + id.orElse(-1L));
}

1.2 查询参数(RequestParam)

1
2
3
4
@GetMapping("/search")
public ResponseEntity<String> search(@RequestParam String keyword) {
return ResponseEntity.ok("搜索关键词: " + keyword);
}
  • 默认是 必填,否则会报 400。
  • 可设置 required=false 或提供默认值:
1
2
3
4
5
@GetMapping("/search")
public ResponseEntity<String> search(
@RequestParam(required = false, defaultValue = "default") String keyword) {
return ResponseEntity.ok("搜索关键词: " + keyword);
}
  • 使用 Optional 绑定:
1
2
3
4
@GetMapping("/search2")
public ResponseEntity<String> search2(@RequestParam Optional<String> keyword) {
return ResponseEntity.ok("搜索关键词: " + keyword.orElse("未提供"));
}

1.3 请求体 JSON(RequestBody)

1
2
3
4
@PostMapping("/user")
public ResponseEntity<String> createUser(@RequestBody UserDTO user) {
return ResponseEntity.ok("创建用户: " + user.getUsername());
}
  • @RequestBody:绑定请求体 JSON → 对象。
  • 需要 spring-boot-starter-web 自动配置的 Jackson 序列化。

1.4 表单提交(application/x-www-form-urlencoded)

1
2
3
4
5
@PostMapping("/login")
public ResponseEntity<String> login(@RequestParam String username,
@RequestParam String password) {
return ResponseEntity.ok("登录用户: " + username);
}
  • @RequestParam:也可处理表单参数。

1.5 文件上传(MultipartFile)

1
2
3
4
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
return ResponseEntity.ok("文件名: " + file.getOriginalFilename() + " 大小: " + file.getSize());
}
  • 需要 spring.servlet.multipart.enabled=true(默认已启用)。
  • 多文件上传:
1
2
3
4
@PostMapping("/upload-multi")
public ResponseEntity<String> uploadMultiple(@RequestParam("files") List<MultipartFile> files) {
return ResponseEntity.ok("上传文件数: " + files.size());
}

1.6 请求头参数(RequestHeader)

1
2
3
4
@GetMapping("/header")
public ResponseEntity<String> getHeader(@RequestHeader("User-Agent") String userAgent) {
return ResponseEntity.ok("请求头 UA: " + userAgent);
}
  • 可设置 required=falseOptional
1
2
3
4
@GetMapping("/header2")
public ResponseEntity<String> getHeaderOptional(@RequestHeader(value = "X-Token", required = false) Optional<String> token) {
return ResponseEntity.ok("Token: " + token.orElse("未提供"));
}

1
2
3
4
@GetMapping("/cookie")
public ResponseEntity<String> getCookie(@CookieValue("SESSIONID") String sessionId) {
return ResponseEntity.ok("SessionID: " + sessionId);
}

2. 返回结果的常见方式


2.1 返回 JSON

1
2
3
4
@GetMapping("/json")
public UserDTO getUserJson() {
return new UserDTO("Tom", "123456");
}
  • Spring Boot 自动将对象 → JSON(Jackson)。
  • 若要自定义序列化,可用 @JsonProperty@JsonIgnore

2.2 返回 ResponseEntity(推荐)

1
2
3
4
5
6
7
@GetMapping("/entity")
public ResponseEntity<UserDTO> getEntity() {
return ResponseEntity
.status(HttpStatus.CREATED) // 设置状态码
.header("X-Custom-Header", "demo") // 设置响应头
.body(new UserDTO("Tom", "123456")); // 设置响应体
}

2.3 返回文件下载

1
2
3
4
5
6
7
8
9
@GetMapping("/download")
public ResponseEntity<byte[]> downloadFile() throws IOException {
byte[] content = Files.readAllBytes(Paths.get("example.txt"));
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData("attachment", "example.txt");
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

return new ResponseEntity<>(content, headers, HttpStatus.OK);
}

2.4 返回纯文本

1
2
3
4
5
6
@GetMapping("/text")
public ResponseEntity<String> getText() {
return ResponseEntity.ok()
.contentType(MediaType.TEXT_PLAIN)
.body("Hello Spring Boot!");
}

2.5 返回流(大文件)

1
2
3
4
5
6
7
8
9
@GetMapping("/stream")
public void streamFile(HttpServletResponse response) throws IOException {
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=data.txt");
try (OutputStream os = response.getOutputStream()) {
os.write("Stream Content".getBytes());
os.flush();
}
}

3. 小结

参数绑定

  • 路径参数@PathVariable
  • 查询参数@RequestParam
  • 请求体 JSON@RequestBody
  • 表单参数@RequestParam
  • 文件上传MultipartFile
  • 请求头@RequestHeader
  • Cookie@CookieValue
  • 可选参数Optional<T>

返回结果

  • JSON:对象 → 自动序列化
  • ResponseEntity:可设置状态码/头/体
  • 文件byte[] / 流
  • 文本MediaType.TEXT_PLAIN
  • 流式返回:写入 HttpServletResponse