第一章:Spring 核心概念深度解析

1.1 IoC 容器與 Bean 的生命週期

Spring 最核心的價值是“控制反轉(Inversion of Control)”和“依賴注入(Dependency Injection)”。

@Component
public class UserService {
    private final OrderService orderService;

    // 構造器注入(推薦方式)
    public UserService(OrderService orderService) {
        this.orderService = orderService;
    }
}

Bean 的完整生命週期:

  1. 實例化(Instantiation)
  2. 屬性填充(Populate properties)
  3. BeanNameAware、BeanFactoryAware 等回調
  4. BeanPostProcessor 前置處理
  5. InitializingBean.afterPropertiesSet()
  6. init-method
  7. BeanPostProcessor 後置處理
  8. 使用中
  9. DisposableBean.destroy()
  10. destroy-method

1.2 ApplicationContext vs BeanFactory

  • BeanFactory:懶加載,適合資源極度受限場景
  • ApplicationContext:餓加載,提供了國際化、事件發佈、資源加載等高級特性
@Bean
@Lazy
public ExpensiveObject expensiveObject() {
    return new ExpensiveObject(); // 只有第一次注入時才創建
}

1.3 Scope 作用域詳解

@Component
@Scope("prototype")
public class ProtoService { }  // 每次注入都新建

@Component
@RequestScope  // 等價於 @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestInfo { }

第二章:Spring Boot 3.x 核心特性全解析

2.1 自動配置原理深度剖析

Spring Boot 的魔法在於 @SpringBootApplication 實際上是三個註解的組合:

@SpringBootConfiguration  // 標記為配置類
@EnableAutoConfiguration // 開啓自動配置
@ComponentScan           // 組件掃描

自動配置的核心是 spring-boot-autoconfigure 包下的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件(Spring Boot 3.0+ 新機制)。

2.2 配置屬性綁定(Configuration Properties)

@ConfigurationProperties(prefix = "app.payment")
@Component
public class PaymentProperties {
    private String gatewayUrl;
    private int timeout = 30;
    private List<String> supportedTypes = new ArrayList<>();
    // getter/setter
}

配合 @EnableConfigurationProperties 或直接在類上加 @Component 即可。

2.3 條件化裝配大全

@ConditionalOnProperty(prefix = "app.feature", name = "enabled", havingValue = "true")
@ConditionalOnMissingBean(RedisTemplate.class)
@ConditionalOnClass(RedisOperations.class)
@ConditionalOnCloudPlatform(CloudPlatform.HEROKU)

2.4 Profile 與多環境配置

# application-dev.yml
server:
  port: 8080

# application-prod.yml
server:
  port: 80
spring:
  datasource:
    url: jdbc:mysql://prod-db:3306/prod

啓動時指定:--spring.profiles.active=prod

第三章:Spring MVC 深度實戰

3.1 @ControllerAdvice 全局異常處理

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BindException.class)
    public Result<?> handleBindException(BindException e) {
        // 字段校驗異常處理
    }

    @ExceptionHandler(BusinessException.class)
    public Result<?> handleBusiness(BusinessException e) {
        return Result.error(e.getCode(), e.getMessage());
    }
}

3.2 參數校驗最佳實踐

@PostMapping("/users")
public Result<?> create(@Valid @RequestBody CreateUserRequest request) {
    // 如果校驗不通過會拋出 MethodArgumentNotValidException
}

自定義校驗註解:

@Documented
@Constraint(validatedBy = IdCardValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IdCard {
    String message() default "身份證格式不正確";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

3.3 統一響應封裝

@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return !returnType.getParameterType().equals(Result.class);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
            MediaType selectedContentType, Class selectedConverterType,
            ServerHttpRequest request, ServerHttpResponse response) {
        
        if (body instanceof String) {
            // 特殊處理 String 類型
            return JSON.toJSONString(Result.success(body));
        }
        return Result.success(body);
    }
}

第四章:Spring Data JPA 生產級使用姿勢

4.1 實體設計最佳實踐

@Entity
@Table(name = "t_user", indexes = {
    @Index(name = "idx_phone", columnList = "phone"),
    @Index(name = "idx_status_created", columnList = "status,createdTime")
})
@Getter
@Setter
@ToString
@DynamicUpdate  // 只更新變動的字段
public class User extends BaseEntity {

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

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

    @Column(unique = true, length = 11)
    private String phone;

    @Enumerated(EnumType.STRING)
    private UserStatus status = UserStatus.NORMAL;

    @CreationTimestamp
    private LocalDateTime createdTime;

    @UpdateTimestamp
    private LocalDateTime updatedTime;
}

4.2 複雜查詢技巧

public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {

    // 方法名查詢
    List<User> findByPhoneAndStatus(String phone, UserStatus status);

    // @Query 原生查詢
    @Query(value = "SELECT * FROM t_user WHERE phone like %?1", nativeQuery = true)
    List<User> findByPhoneLike(String phone);

    // Specification 動態查詢
    default List<User> search(UserSearchRequest req) {
        return findAll((root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (StringUtils.hasText(req.getName())) {
                predicates.add(cb.like(root.get("name"), "%" + req.getName() + "%"));
            }
            if (req.getStatus() != null) {
                predicates.add(cb.equal(root.get("status"), req.getStatus()));
            }
            return cb.and(predicates.toArray(new Predicate[0]));
        });
    }
}

4.3 分頁 + 排序 + 投影

@RestController
public class UserController {

    @GetMapping("/users")
    public Page<UserDTO> list(UserSearchRequest req, Pageable pageable) {
        Page<User> page = userRepository.findAll(buildSpec(req), pageable);
        return page.map(user -> userMapper.toDto(user));
    }

    // 投影接口
    interface UserSimpleView {
        Long getId();
        String getName();
        String getPhone();
    }

    @Query("SELECT u.id as id, u.name as name, u.phone as phone FROM User u WHERE u.status = ?1")
    Page<UserSimpleView> findSimpleByStatus(UserStatus status, Pageable pageable);
}

第五章:Spring Security 6.x 完全攻略

5.1 基於 JWT + Redis 的無狀態認證方案

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/login", "/register").permitAll()
                .anyRequest().authenticated()
            )
            .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

JWT 工具類:

@Component
public class JwtTokenProvider {

    private final String secret = "your-256-bit-secret-your-256-bit-secret"; // 至少256位
    private final long validityInMilliseconds = 3600000; // 1h

    public String createToken(String username, List<String> roles) {
        Claims claims = Jwts.claims().setSubject(username);
        claims.put("roles", roles);

        Date now = new Date();
        Date validity = new Date(now.getTime() + validityInMilliseconds);

        return Jwts.builder()
            .setClaims(claims)
            .setIssuedAt(now)
            .setExpiration(validity)
            .signWith(Keys.hmacShaKeyFor(secret.getBytes()), SignatureAlgorithm.HS256)
            .compact();
    }

    public Authentication getAuthentication(String token) {
        String username = Jwts.parserBuilder()
            .setSigningKey(secret.getBytes())
            .build()
            .parseClaimsJws(token)
            .getBody()
            .getSubject();

        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }
}

5.2 權限控制的三種方式對比

  1. @PreAuthorize("hasRole('ADMIN')")
  2. @Secured("ROLE_ADMIN")
  3. 方法級 Security + AOP

推薦使用 @PreAuthorize,支持 SpEL 表達式:

@PreAuthorize("#userId == authentication.principal.id or hasRole('ADMIN')")
@PatchMapping("/users/{userId}")
public Result<?> update(@PathVariable Long userId, @RequestBody UpdateRequest req) {
    // 只能修改自己的信息,或管理員可以修改任意
}

第六章:Spring Cache 抽象深度使用

6.1 多級緩存(Caffeine + Redis)實戰

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        // 本地緩存
        Caffeine<Object, Object> caffeine = Caffeine.newBuilder()
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .maximumSize(1000);

        CaffeineCacheManager localCacheManager = new CaffeineCacheManager();
        localCacheManager.setCaffeine(caffeine);

        // Redis 緩存
        RedisCacheConfiguration redisConfig = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofHours(24))
            .serializeValuesWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new GenericJackson2JsonRedisSerializer()));

        RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisConnectionFactory)
            .cacheDefaults(redisConfig)
            .build();

        // 組合緩存
        return new CompositeCacheManager(localCacheManager, redisCacheManager);
    }
}

使用:

@Cacheable(value = "user", key = "#id")
public User getById(Long id) { ... }

@CacheEvict(value = "user", key = "#user.id")
public void update(User user) { ... }

第七章:異步處理與定時任務

7.1 @Async 異步方法最佳實踐

@Service
public class MailService {

    @Async("taskExecutor")
    public CompletableFuture<Void> sendRegisterMail(User user) {
        // 模擬耗時
        Thread.sleep(3000);
        log.info("發送註冊郵件給: {}", user.getEmail());
        return CompletableFuture.completedFuture(null);
    }
}

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setQueueCapacity(1000);
        executor.setThreadNamePrefix("async-");
        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (throwable, method, objects) -> {
            log.error("異步任務異常: {}", method, throwable);
        };
    }
}

7.2 動態定時任務(基於數據庫)

@Component
public class DynamicScheduler {

    @Autowired
    private TaskMapper taskMapper;

    private final Map<Long, ScheduledFuture<?>> futures = new ConcurrentHashMap<>();

    @EventListener(ApplicationReadyEvent.class)
    public void init() {
        List<Task> tasks = taskMapper.findByEnabled(true);
        tasks.forEach(this::scheduleTask);
    }

    private void scheduleTask(Task task) {
        ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(
            () -> runTask(task), 
            Duration.ofSeconds(task.getInterval())
        );
        futures.put(task.getId(), future);
    }
}