强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

22 - Spring Boot:自动配置、Web 开发、DI/IoC

22 - Spring Boot:自动配置、Web 开发、DI/IoC

快速开始

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

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@SpringBootApplication 等价于:

@SpringBootConfiguration
@EnableAutoConfiguration   // 自动配置
@ComponentScan             // 包扫描

依赖注入(DI / IoC)

import org.springframework.stereotype.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;

// 1. 定义 Bean
@Service
public class UserService {
    private final UserRepository userRepository;
    private final EmailService emailService;

    // 构造器注入(推荐)
    public UserService(UserRepository userRepository, EmailService emailService) {
        this.userRepository = userRepository;
        this.emailService = emailService;
    }

    public User register(String name, String email) {
        User user = new User(name, email);
        userRepository.save(user);
        emailService.sendWelcome(email);
        return user;
    }
}

// 2. Repository
@Repository
public class UserRepository {
    private final Map<Long, User> db = new ConcurrentHashMap<>();

    public void save(User user) {
        db.put(user.getId(), user);
    }

    public Optional<User> findById(Long id) {
        return Optional.ofNullable(db.get(id));
    }
}

// 3. 配置类 Bean
@Configuration
public class AppConfig {
    @Bean
    @ConditionalOnMissingBean  // 没有时才创建
    public EmailService emailService() {
        return new SmtpEmailService();
    }
}

// 4. 注入方式
@RestController
public class UserController {
    // 字段注入(不推荐)
    // @Autowired private UserService userService;

    // 构造器注入(推荐)
    private final UserService userService;
    public UserController(UserService userService) {
        this.userService = userService;
    }
}

Bean 注解

注解说明使用场景
@Component通用组件工具类
@Service业务层Service 类
@Repository数据层DAO 类
@Controller控制层MVC 控制器
@RestControllerREST 控制器REST API
@Configuration配置类定义 Bean
@Bean工厂方法第三方库集成

RESTful Web 开发

import org.springframework.web.bind.annotation.*;
import org.springframework.http.*;

@RestController
@RequestMapping("/api/users")
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    // GET /api/users
    @GetMapping
    public List<User> list() {
        return userService.findAll();
    }

    // GET /api/users/{id}
    @GetMapping("/{id}")
    public ResponseEntity<User> getById(@PathVariable Long id) {
        return userService.findById(id)
            .map(ResponseEntity::ok)
            .orElse(ResponseEntity.notFound().build());
    }

    // POST /api/users
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public User create(@Valid @RequestBody CreateUserRequest request) {
        return userService.create(request);
    }

    // PUT /api/users/{id}
    @PutMapping("/{id}")
    public User update(@PathVariable Long id,
                       @Valid @RequestBody UpdateUserRequest request) {
        return userService.update(id, request);
    }

    // DELETE /api/users/{id}
    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void delete(@PathVariable Long id) {
        userService.delete(id);
    }
}

请求参数处理

@RestController
@RequestMapping("/api/demo")
public class ParamController {
    // 路径变量
    @GetMapping("/users/{id}")
    public String pathVar(@PathVariable Long id) { return "ID: " + id; }

    // 查询参数
    @GetMapping("/search")
    public String queryParam(@RequestParam String keyword,
                             @RequestParam(defaultValue = "1") int page) {
        return keyword + ", page " + page;
    }

    // 请求体
    @PostMapping("/body")
    public String body(@Valid @RequestBody UserDTO dto) { return dto.toString(); }

    // 请求头
    @GetMapping("/header")
    public String header(@RequestHeader("Authorization") String token) {
        return token;
    }
}

全局异常处理

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
            .body(new ErrorResponse("NOT_FOUND", ex.getMessage()));
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
        String msg = ex.getBindingResult().getFieldErrors().stream()
            .map(e -> e.getField() + ": " + e.getDefaultMessage())
            .collect(Collectors.joining(", "));
        return ResponseEntity.badRequest()
            .body(new ErrorResponse("VALIDATION_ERROR", msg));
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
        return ResponseEntity.internalServerError()
            .body(new ErrorResponse("INTERNAL_ERROR", "服务器内部错误"));
    }
}

record ErrorResponse(String code, String message) {}

配置文件

# application.yml
server:
  port: 8080
  servlet:
    context-path: /

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: ${DB_PASSWORD:default}
    hikari:
      maximum-pool-size: 10
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true

app:
  name: My Application
  jwt:
    secret: ${JWT_SECRET}
    expiration: 3600000
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
    private String name;
    private Jwt jwt = new Jwt();

    // getters/setters

    public static class Jwt {
        private String secret;
        private long expiration;
        // getters/setters
    }
}

⚠️ 注意事项

  1. 构造器注入优于字段注入 — 更容易测试,强制依赖不可为 null。
  2. 不要在 Service 层 catch 所有异常 — 使用全局异常处理器。
  3. @Transactional 只在 public 方法生效 — 自调用不会触发事务。
  4. 配置敏感信息使用环境变量 — 不要硬编码密码。

💡 技巧

  1. Profile 切换环境

    java -jar app.jar --spring.profiles.active=prod
    
  2. Actuator 健康检查

    management:
      endpoints:
        web:
          exposure:
            include: health,info,metrics
    
  3. 优雅关机

    server:
      shutdown: graceful
    spring:
      lifecycle:
        timeout-per-shutdown-phase: 30s
    

🏢 业务场景

  • REST API 服务: 电商、社交、内容管理系统的后端。
  • 微服务: Spring Cloud 生态中的独立服务。
  • 后台管理: 前后端分离架构的后端部分。

📖 扩展阅读