跳转至

Spring Boot实战

🎯 学习目标

完成本章学习后,你将能够:

Spring Boot分层与核心能力图

1. Spring Boot核心理念

1.1 什么是Spring Boot

Spring Boot是基于Spring Framework的快速开发框架,核心理念是约定优于配置(Convention over Configuration)。它通过自动配置、起步依赖和嵌入式服务器,极大简化了Spring应用的创建和部署流程。

核心优势: - 内嵌Tomcat/Jetty,无需外部部署War包 - 自动配置(Auto-Configuration),减少XML配置 - 起步依赖(Starter),一站式引入相关Jar包 - Actuator生产级监控 - 与Spring Cloud无缝集成,便于微服务化

1.2 环境搭建

方式一:Spring Initializr(推荐)

访问 https://start.spring.io,选择: - Project: Maven - Language: Java - Spring Boot: 3.2.x - Packaging: Jar - Java: 21

勾选依赖:Spring Web、Spring Data JPA、MySQL Driver、Lombok

方式二:IDEA创建

IntelliJ IDEA → New Project → Spring Initializr → 选择依赖 → 生成

项目基本结构:

Text Only
my-app/
├── src/main/java/com/example/myapp/
│   ├── MyAppApplication.java          // 启动类
│   ├── controller/                     // 控制层
│   ├── service/                        // 业务层
│   ├── repository/                     // 持久层
│   ├── entity/                         // 实体类
│   ├── config/                         // 配置类
│   └── dto/                            // 数据传输对象
├── src/main/resources/
│   ├── application.yml                 // 主配置
│   ├── application-dev.yml             // 开发环境配置
│   ├── application-prod.yml            // 生产环境配置
│   └── static/                         // 静态资源
├── src/test/java/                      // 测试代码
└── pom.xml                             // Maven配置

1.3 第一个Spring Boot应用

Java
// pom.xml 核心依赖
// <parent>
//     <groupId>org.springframework.boot</groupId>
//     <artifactId>spring-boot-starter-parent</artifactId>
//     <version>3.2.5</version>
// </parent>
// <dependencies>
//     <dependency>
//         <groupId>org.springframework.boot</groupId>
//         <artifactId>spring-boot-starter-web</artifactId>
//     </dependency>
// </dependencies>

package com.example.myapp;

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

@SpringBootApplication  // = @Configuration + @EnableAutoConfiguration + @ComponentScan
public class MyAppApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyAppApplication.class, args);
    }
}

启动后访问 http://localhost:8080,Spring Boot应用即运行。


2. 核心注解详解

2.1 启动与配置注解

Java
// @SpringBootApplication 是组合注解,包含:
// @Configuration     — 标识当前类为配置类,可定义@Bean
// @EnableAutoConfiguration — 开启自动配置
// @ComponentScan     — 自动扫描当前包及子包下的组件

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

// @Configuration + @Bean:手动注册Bean
@Configuration
public class AppConfig {

    @Bean  // 方法返回值注册为Spring容器中的Bean,默认单例
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    @ConditionalOnProperty(name = "feature.cache.enabled", havingValue = "true")
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager("users");
    }
}

2.2 组件注解(分层架构)

Java
// @Controller / @RestController — 控制层
// @RestController = @Controller + @ResponseBody(直接返回JSON)
@RestController
@RequestMapping("/api/v1/users")
public class UserController {

    // @Autowired — 自动注入(推荐构造器注入)
    private final UserService userService;

    // Spring 4.3+,单构造器可省略@Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping  // @GetMapping 映射HTTP GET请求到此方法
    public List<UserDTO> getAllUsers() {
        return userService.findAll();
    }
}

// @Service — 业务层
@Service  // @Service 标注业务层组件,Spring自动扫描注册为Bean
public class UserService {
    private final UserRepository userRepo;

    public UserService(UserRepository userRepo) {
        this.userRepo = userRepo;
    }

    public List<UserDTO> findAll() {
        return userRepo.findAll().stream()
            .map(this::toDTO)
            .toList();
    }

    private UserDTO toDTO(User user) {
        return new UserDTO(user.getId(), user.getName(), user.getEmail());
    }
}

// @Repository — 持久层(自动转换数据库异常为Spring DataAccessException)
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);

    @Query("SELECT u FROM User u WHERE u.status = :status")
    List<User> findByStatus(@Param("status") String status);
}

2.3 其他常用注解

Java
// @Value — 注入配置值
@Value("${app.name:DefaultApp}")
private String appName;

// @Autowired + @Qualifier — 指定注入哪个Bean(存在多个同类型Bean时)
@Autowired  // @Autowired 自动注入Spring容器中的Bean
@Qualifier("mysqlDataSource")
private DataSource dataSource;

// @PostConstruct / @PreDestroy — 生命周期回调
@Component
public class StartupRunner {
    @PostConstruct
    public void init() {
        System.out.println("应用启动后执行初始化逻辑");
    }
}

// @Scope — 控制Bean作用域
@Scope("prototype")  // 每次获取创建新实例(默认singleton)
@Component
public class PrototypeBean { }

// @Lazy — 延迟初始化
@Lazy
@Service
public class HeavyService { }

3. RESTful API开发

3.1 标准CRUD接口

Java
@RestController
@RequestMapping("/api/v1/users")
@Validated
public class UserController {

    private final UserService userService;

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

    // GET /api/v1/users — 查询全部
    @GetMapping
    public ResponseEntity<List<UserDTO>> list(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size) {
        Page<UserDTO> result = userService.findAll(PageRequest.of(page, size));
        return ResponseEntity.ok(result.getContent());
    }

    // GET /api/v1/users/{id} — 按ID查询
    @GetMapping("/{id}")
    public ResponseEntity<UserDTO> getById(@PathVariable Long id) {
        return userService.findById(id)
            .map(ResponseEntity::ok)
            .orElse(ResponseEntity.notFound().build());
    }

    // POST /api/v1/users — 新增
    @PostMapping
    public ResponseEntity<UserDTO> create(@RequestBody @Valid CreateUserRequest req) {
        UserDTO created = userService.create(req);
        URI location = URI.create("/api/v1/users/" + created.getId());
        return ResponseEntity.created(location).body(created);
    }

    // PUT /api/v1/users/{id} — 更新
    @PutMapping("/{id}")
    public ResponseEntity<UserDTO> update(
            @PathVariable Long id,
            @RequestBody @Valid UpdateUserRequest req) {
        return ResponseEntity.ok(userService.update(id, req));
    }

    // DELETE /api/v1/users/{id} — 删除
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        userService.delete(id);
        return ResponseEntity.noContent().build();
    }
}

3.2 参数校验(JSR 380 / Hibernate Validator)

Java
// DTO + 校验注解
public class CreateUserRequest {

    @NotBlank(message = "用户名不能为空")
    @Size(min = 2, max = 50, message = "用户名长度2-50")
    private String name;

    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;

    @NotNull(message = "年龄不能为空")
    @Min(value = 1, message = "年龄最小为1")
    @Max(value = 150, message = "年龄最大为150")
    private Integer age;

    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
    private String phone;

    // getter/setter 省略
}

// 全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidation(
            MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors().forEach(error ->
            errors.put(error.getField(), error.getDefaultMessage())
        );
        return ResponseEntity.badRequest().body(errors);
    }

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

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
        ErrorResponse error = new ErrorResponse(500, "服务器内部错误");
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
    }
}

3.3 统一响应格式

Java
// 统一返回结构
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiResponse<T> {  // <T> 泛型参数,使类支持任意数据类型
    private int code;
    private String message;
    private T data;

    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(200, "success", data);
    }

    public static <T> ApiResponse<T> error(int code, String message) {
        return new ApiResponse<>(code, message, null);
    }
}

// 使用示例
@GetMapping("/{id}")
public ApiResponse<UserDTO> getById(@PathVariable Long id) {
    return ApiResponse.success(userService.findById(id));
}

4. 数据持久层

4.1 Spring Data JPA

Java
// 实体类
@Entity
@Table(name = "t_user")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 50)
    private String name;

    @Column(nullable = false, unique = true, length = 100)
    private String email;

    @Enumerated(EnumType.STRING)
    private UserStatus status;

    @CreationTimestamp
    private LocalDateTime createdAt;

    @UpdateTimestamp
    private LocalDateTime updatedAt;
}

// Repository
public interface UserRepository extends JpaRepository<User, Long> {

    Optional<User> findByEmail(String email);

    List<User> findByStatusOrderByCreatedAtDesc(UserStatus status);

    @Query("SELECT u FROM User u WHERE u.name LIKE %:keyword% OR u.email LIKE %:keyword%")
    Page<User> search(@Param("keyword") String keyword, Pageable pageable);

    @Modifying
    @Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
    int updateStatus(@Param("id") Long id, @Param("status") UserStatus status);
}

4.2 MyBatis整合

Java
// application.yml
// mybatis:
//   mapper-locations: classpath:mapper/*.xml
//   type-aliases-package: com.example.entity
//   configuration:
//     map-underscore-to-camel-case: true

// Mapper接口
@Mapper
public interface UserMapper {

    @Select("SELECT * FROM t_user WHERE id = #{id}")
    User findById(Long id);

    @Insert("INSERT INTO t_user(name, email, status) VALUES(#{name}, #{email}, #{status})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(User user);

    // 复杂SQL用XML
    List<User> searchByCondition(UserQueryParam param);
}

// mapper/UserMapper.xml
// <mapper namespace="com.example.mapper.UserMapper">
//   <select id="searchByCondition" resultType="User">
//     SELECT * FROM t_user
//     <where>
//       <if test="name != null and name != ''">
//         AND name LIKE CONCAT('%', #{name}, '%')
//       </if>
//       <if test="status != null">
//         AND status = #{status}
//       </if>
//     </where>
//     ORDER BY created_at DESC
//   </select>
// </mapper>

4.3 事务管理

Java
@Service
public class OrderService {

    private final OrderRepository orderRepo;
    private final InventoryService inventoryService;

    public OrderService(OrderRepository orderRepo, InventoryService inventoryService) {
        this.orderRepo = orderRepo;
        this.inventoryService = inventoryService;
    }

    // @Transactional 保证方法内所有数据库操作在同一事务中
    @Transactional(rollbackFor = Exception.class)  // 所有异常都回滚
    public Order createOrder(CreateOrderRequest req) {
        // 1. 扣减库存
        inventoryService.deductStock(req.getProductId(), req.getQuantity());
        // 2. 创建订单
        Order order = new Order();
        order.setUserId(req.getUserId());
        order.setProductId(req.getProductId());
        order.setAmount(req.getAmount());
        order.setStatus(OrderStatus.CREATED);
        return orderRepo.save(order);
        // 如果中间任何一步抛异常,整体回滚
    }

    // 事务传播行为
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logOperation(String operation) {
        // 即使外层事务回滚,日志记录也会独立提交
    }
}

@Transactional 常见坑: - 默认只回滚RuntimeException,建议加rollbackFor = Exception.class - 同类内部方法调用不走代理,事务注解无效(需注入自身或用AopContext) - private方法上的注解无效


5. AOP面向切面编程

Java
// 日志切面示例
@Aspect
@Component
@Slf4j
public class LoggingAspect {

    // 切点:匹配controller包下所有方法
    @Pointcut("execution(* com.example.controller..*.*(..))")
    public void controllerMethods() {}

    // 环绕通知
    @Around("controllerMethods()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().toShortString();
        Object[] args = joinPoint.getArgs();

        log.info(">>> 调用方法: {} | 参数: {}", methodName, Arrays.toString(args));
        long startTime = System.currentTimeMillis();

        try {
            Object result = joinPoint.proceed();
            long elapsed = System.currentTimeMillis() - startTime;
            log.info("<<< 方法返回: {} | 耗时: {}ms", methodName, elapsed);
            return result;
        } catch (Exception ex) {
            log.error("!!! 方法异常: {} | 异常: {}", methodName, ex.getMessage());
            throw ex;
        }
    }
}

// 自定义注解 + AOP实现权限校验
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireRole {
    String value();  // 所需角色
}

@Aspect
@Component
public class AuthAspect {

    @Before("@annotation(requireRole)")
    public void checkRole(JoinPoint joinPoint, RequireRole requireRole) {
        String requiredRole = requireRole.value();
        // 从SecurityContext获取当前用户角色
        String currentRole = SecurityContextHolder.getContext()
            .getAuthentication().getAuthorities().toString();
        if (!currentRole.contains(requiredRole)) {
            throw new AccessDeniedException("需要角色: " + requiredRole);
        }
    }
}

// 使用
@RequireRole("ADMIN")
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
    userService.delete(id);
    return ResponseEntity.noContent().build();
}

6. Spring Security + JWT认证

6.1 JWT工具类

Java
@Component
public class JwtUtil {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration:86400000}")  // 默认24小时
    private long expiration;

    // 生成Token
    public String generateToken(String username, List<String> roles) {
        return Jwts.builder()
            .subject(username)
            .claim("roles", roles)
            .issuedAt(new Date())
            .expiration(new Date(System.currentTimeMillis() + expiration))
            .signWith(Keys.hmacShaKeyFor(secret.getBytes()))
            .compact();
    }

    // 解析Token
    public Claims parseToken(String token) {
        return Jwts.parser()
            .verifyWith(Keys.hmacShaKeyFor(secret.getBytes()))
            .build()
            .parseSignedClaims(token)
            .getPayload();
    }

    // 验证Token
    public boolean validateToken(String token) {
        try {
            parseToken(token);
            return true;
        } catch (JwtException | IllegalArgumentException e) {
            return false;
        }
    }

    public String getUsernameFromToken(String token) {
        return parseToken(token).getSubject();
    }
}

6.2 Security配置

Java
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final JwtAuthenticationFilter jwtFilter;

    public SecurityConfig(JwtAuthenticationFilter jwtFilter) {
        this.jwtFilter = jwtFilter;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .requestMatchers("/api/**").authenticated()
                .anyRequest().permitAll()
            )
            .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

// JWT过滤器
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtUtil jwtUtil;
    private final UserDetailsService userDetailsService;

    public JwtAuthenticationFilter(JwtUtil jwtUtil, UserDetailsService userDetailsService) {
        this.jwtUtil = jwtUtil;
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
            HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        String header = request.getHeader("Authorization");
        if (header != null && header.startsWith("Bearer ")) {
            String token = header.substring(7);
            if (jwtUtil.validateToken(token)) {
                String username = jwtUtil.getUsernameFromToken(token);
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                UsernamePasswordAuthenticationToken auth =
                    new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(auth);
            }
        }
        chain.doFilter(request, response);
    }
}

7. 配置管理

7.1 多环境配置

YAML
# application.yml — 公共配置
spring:
  profiles:
    active: dev  # 默认开发环境
  application:
    name: my-app

server:
  port: 8080

# application-dev.yml — 开发环境
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/myapp_dev
    username: root
    password: root123
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update
logging:
  level:
    com.example: DEBUG

# application-prod.yml — 生产环境
spring:
  datasource:
    url: jdbc:mysql://prod-db:3306/myapp
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
  jpa:
    show-sql: false
    hibernate:
      ddl-auto: none
logging:
  level:
    com.example: WARN

7.2 类型安全配置

Java
@Component
@ConfigurationProperties(prefix = "app")
@Data
public class AppProperties {

    private String name;
    private String version;
    private Security security = new Security();
    private List<String> allowedOrigins = new ArrayList<>();

    @Data
    public static class Security {
        private String jwtSecret;
        private long jwtExpiration = 86400000;
        private int maxLoginAttempts = 5;
    }
}

// application.yml
// app:
//   name: MyApplication
//   version: 1.0.0
//   security:
//     jwt-secret: my-secret-key-at-least-32-characters
//     jwt-expiration: 86400000
//   allowed-origins:
//     - http://localhost:3000
//     - https://example.com

// 使用
@RestController
public class InfoController {
    private final AppProperties appProperties;

    public InfoController(AppProperties appProperties) {
        this.appProperties = appProperties;
    }

    @GetMapping("/info")
    public Map<String, String> info() {
        return Map.of(
            "name", appProperties.getName(),
            "version", appProperties.getVersion()
        );
    }
}

8. 缓存集成(Redis)

Java
// 引入依赖: spring-boot-starter-data-redis
// application.yml:
// spring:
//   data:
//     redis:
//       host: localhost
//       port: 6379
//       password:
//       lettuce:
//         pool:
//           max-active: 8

@Configuration
@EnableCaching
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }
}

@Service
public class UserService {

    private final UserRepository userRepo;

    public UserService(UserRepository userRepo) {
        this.userRepo = userRepo;
    }

    // 查询时缓存,key为 "user::1"
    @Cacheable(value = "user", key = "#id")
    public UserDTO findById(Long id) {
        return userRepo.findById(id)
            .map(this::toDTO)
            .orElseThrow(() -> new ResourceNotFoundException("用户不存在: " + id));
    }

    // 更新时清除缓存
    @CacheEvict(value = "user", key = "#id")
    @Transactional
    public UserDTO update(Long id, UpdateUserRequest req) {
        User user = userRepo.findById(id)
            .orElseThrow(() -> new ResourceNotFoundException("用户不存在: " + id));
        user.setName(req.getName());
        user.setEmail(req.getEmail());
        return toDTO(userRepo.save(user));
    }

    // 更新时同时更新缓存
    @CachePut(value = "user", key = "#result.id")
    @Transactional
    public UserDTO create(CreateUserRequest req) {
        User user = new User();
        user.setName(req.getName());
        user.setEmail(req.getEmail());
        return toDTO(userRepo.save(user));
    }

    private UserDTO toDTO(User user) {
        return new UserDTO(user.getId(), user.getName(), user.getEmail());
    }
}

9. 消息队列集成

9.1 RabbitMQ

Java
// 引入: spring-boot-starter-amqp
// application.yml:
// spring:
//   rabbitmq:
//     host: localhost
//     port: 5672
//     username: guest
//     password: guest

@Configuration
public class RabbitMQConfig {

    public static final String ORDER_QUEUE = "order.queue";
    public static final String ORDER_EXCHANGE = "order.exchange";
    public static final String ORDER_ROUTING_KEY = "order.create";

    @Bean
    public Queue orderQueue() {
        return QueueBuilder.durable(ORDER_QUEUE).build();
    }

    @Bean
    public DirectExchange orderExchange() {
        return new DirectExchange(ORDER_EXCHANGE);
    }

    @Bean
    public Binding binding(Queue orderQueue, DirectExchange orderExchange) {
        return BindingBuilder.bind(orderQueue).to(orderExchange).with(ORDER_ROUTING_KEY);
    }
}

// 生产者
@Service
public class OrderProducer {

    private final RabbitTemplate rabbitTemplate;

    public OrderProducer(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    public void sendOrderMessage(OrderMessage message) {
        rabbitTemplate.convertAndSend(
            RabbitMQConfig.ORDER_EXCHANGE,
            RabbitMQConfig.ORDER_ROUTING_KEY,
            message
        );
    }
}

// 消费者
@Component
@Slf4j
public class OrderConsumer {

    @RabbitListener(queues = RabbitMQConfig.ORDER_QUEUE)
    public void handleOrder(OrderMessage message) {
        log.info("收到订单消息: {}", message);
        // 处理订单逻辑...
    }
}

9.2 Kafka

Java
// 引入: spring-kafka
// application.yml:
// spring:
//   kafka:
//     bootstrap-servers: localhost:9092
//     consumer:
//       group-id: my-group
//       auto-offset-reset: earliest

// 生产者
@Service
public class KafkaProducerService {

    private final KafkaTemplate<String, String> kafkaTemplate;

    public KafkaProducerService(KafkaTemplate<String, String> kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }

    public void send(String topic, String key, String message) {
        kafkaTemplate.send(topic, key, message)
            .whenComplete((result, ex) -> {
                if (ex == null) {
                    System.out.println("发送成功: offset=" + result.getRecordMetadata().offset());
                } else {
                    System.err.println("发送失败: " + ex.getMessage());
                }
            });
    }
}

// 消费者
@Component
@Slf4j
public class KafkaConsumerService {

    @KafkaListener(topics = "order-topic", groupId = "order-group")
    public void listen(ConsumerRecord<String, String> record) {
        log.info("Kafka消费: topic={}, key={}, value={}, offset={}",
            record.topic(), record.key(), record.value(), record.offset());
    }
}

10. 微服务入门(Spring Cloud)

10.1 核心组件概览

组件 功能 主流方案
服务注册发现 服务地址管理 Nacos / Consul / Eureka
负载均衡 请求分发 Spring Cloud LoadBalancer
API网关 统一入口 Spring Cloud Gateway
配置中心 集中配置管理 Nacos Config / Spring Cloud Config
服务熔断 容错保护 Sentinel / Resilience4j
分布式链路追踪 调用链跟踪 Micrometer Tracing / Zipkin

10.2 Nacos服务注册发现

Java
// 引入: spring-cloud-starter-alibaba-nacos-discovery
// application.yml
// spring:
//   cloud:
//     nacos:
//       discovery:
//         server-addr: localhost:8848

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

// 服务调用(使用OpenFeign)
@FeignClient(name = "order-service")
public interface OrderClient {

    @GetMapping("/api/orders/user/{userId}")
    List<OrderDTO> getOrdersByUserId(@PathVariable("userId") Long userId);
}

10.3 Spring Cloud Gateway

YAML
# gateway-service application.yml
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service     # lb表示负载均衡
          predicates:
            - Path=/api/users/**
          filters:
            - StripPrefix=0
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/orders/**

11. Docker容器化部署

11.1 Dockerfile

Docker
# 多阶段构建
FROM maven:3.9-eclipse-temurin-21 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn clean package -DskipTests

FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar

# JVM参数优化
ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC"
EXPOSE 8080

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

11.2 Docker Compose

YAML
version: '3.8'
services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - DB_USERNAME=root
      - DB_PASSWORD=secret
    depends_on:
      - mysql
      - redis

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: myapp
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  mysql-data:

构建运行:

Bash
# 构建并启动所有服务
docker-compose up -d --build

# 查看日志
docker-compose logs -f app

# 停止
docker-compose down

✏️ 练习

  1. 初级:创建一个Spring Boot项目,实现对Book实体的完整CRUD RESTful API,使用Spring Data JPA + H2内存数据库
  2. 中级:在上述项目中加入参数校验、全局异常处理、统一响应格式;集成Spring Security + JWT认证
  3. 高级:将应用拆分为user-service和book-service两个微服务,使用Nacos注册发现 + OpenFeign调用 + Spring Cloud Gateway网关;编写Dockerfile并使用Docker Compose编排部署

📋 面试高频考点

Spring Boot基础

  1. Spring Boot自动配置原理?@EnableAutoConfigurationspring.factories / AutoConfiguration.imports → 条件注解(@ConditionalOnClass等)决定是否生效
  2. Spring Boot启动流程?SpringApplication.run() → 创建ApplicationContext → 刷新容器(解析配置类 → 注册Bean → 实例化) → 执行Runner
  3. @SpringBootApplication包含什么?@Configuration + @EnableAutoConfiguration + @ComponentScan

Spring核心

  1. Spring IOC和DI的区别? — IOC是控制反转思想(容器管理对象生命周期),DI是依赖注入实现方式
  2. Bean的作用域? — singleton(默认)、prototype、request、session、application
  3. Bean的生命周期? — 实例化 → 属性填充 → Aware接口 → BeanPostProcessor前 → InitializingBean/init-method → BeanPostProcessor后 → 使用 → DisposableBean/destroy-method
  4. @Transactional失效场景? — 同类方法内部调用、非public方法、异常被吞、使用错误的传播行为

安全与架构

  1. JWT vs Session认证? — JWT无状态、适合分布式;Session有状态、服务端存储
  2. Spring Cloud核心组件? — 注册中心、配置中心、网关、负载均衡、熔断器、链路追踪
  3. 微服务拆分原则? — 单一职责、高内聚低耦合、按业务域拆分(DDD)

📌 下一步学习13-JVM深入理解 — 掌握Java虚拟机底层原理