第一章: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 的完整生命週期:
- 實例化(Instantiation)
- 屬性填充(Populate properties)
- BeanNameAware、BeanFactoryAware 等回調
- BeanPostProcessor 前置處理
- InitializingBean.afterPropertiesSet()
- init-method
- BeanPostProcessor 後置處理
- 使用中
- DisposableBean.destroy()
- 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 權限控制的三種方式對比
@PreAuthorize("hasRole('ADMIN')")@Secured("ROLE_ADMIN")- 方法級 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);
}
}