引言

Spring框架是Java企业级应用开发的事实标准,它通过依赖注入(DI)和面向切面编程(AOP)等核心机制,极大地简化了企业级应用的开发。本指南将从基础概念讲起,逐步深入到高级特性,并结合实战技巧,帮助你全面掌握Spring框架。

1. Spring基础概念

1.1 什么是Spring框架?

Spring是一个开源的Java应用框架,最初由Rod Johnson在2003年发布。它的核心特性包括:

  • 控制反转(IoC):将对象的创建和依赖关系的管理交给Spring容器
  • 依赖注入(DI):通过构造器、setter方法或字段注入依赖
  • 面向切面编程(AOP):分离横切关注点,如日志、事务管理
  • 模块化设计:可以按需使用Spring的不同模块

1.2 Spring模块概览

Spring框架包含多个模块,主要分为:

  • Spring Core:提供IoC容器和DI支持
  • Spring AOP:提供面向切面编程支持
  • Spring MVC:Web应用框架
  • Spring Data:简化数据访问
  • Spring Security:安全框架
  • Spring Boot:快速构建Spring应用的工具

2. Spring核心原理详解

2.1 IoC容器与Bean生命周期

Spring IoC容器负责管理Bean的创建、配置和生命周期。主要实现类有:

  • BeanFactory:基础容器,延迟加载Bean
  • ApplicationContext:扩展BeanFactory,提供企业级功能

Bean生命周期示例

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class MyBean implements InitializingBean, DisposableBean, 
    BeanNameAware, ApplicationContextAware {
    
    private String beanName;
    private ApplicationContext applicationContext;
    
    // BeanNameAware回调
    @Override
    public void setBeanName(String name) {
        this.beanName = name;
        System.out.println("Bean名称: " + name);
    }
    
    // ApplicationContextAware回调
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        System.out.println("ApplicationContext已注入");
    }
    
    // InitializingBean回调
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("属性设置完成,执行初始化逻辑");
    }
    
    // 自定义初始化方法(通过@PostConstruct注解)
    @PostConstruct
    public void customInit() {
        System.out.println("自定义初始化方法执行");
    }
    
    // DisposableBean回调
    @Override
    public void destroy() throws Exception {
        System.out.println("Bean销毁,执行清理逻辑");
    }
    
    // 自定义销毁方法(通过@PreDestroy注解)
    @PreDestroy
    public void customDestroy() {
        System.out.println("自定义销毁方法执行");
    }
}

2.2 依赖注入(DI)详解

Spring支持多种依赖注入方式:

构造器注入(推荐)

@Service
public class OrderService {
    private final UserService userService;
    private final PaymentService paymentService;
    
    // 构造器注入
    @Autowired
    public OrderService(UserService userService, PaymentService paymentService) {
        this.userService = userService;
        this.paymentService = paymentService;
    }
    
    public void processOrder(Order order) {
        // 业务逻辑
    }
}

Setter注入

@Service
public class OrderService {
    private UserService userService;
    private PaymentService paymentService;
    
    // Setter注入
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    
    @Autowired
    public void setPaymentService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}

字段注入(不推荐)

@Service
public class OrderService {
    @Autowired
    private UserService userService;
    
    @Autowired
    private PaymentService paymentService;
}

2.3 Bean的作用域

Spring Bean支持以下作用域:

  • singleton:默认,每个容器中一个Bean实例
  • prototype:每次请求都创建新实例
  • request:每个HTTP请求一个实例(Web环境)
  • session:每个HTTP会话一个实例(Web环境)
  • global-session:全局HTTP会话(Portlet环境)
@Component
@Scope("prototype") // 每次获取都创建新实例
public class PrototypeBean {
    private final String id = UUID.randomUUID().toString();
    
    public String getId() {
        return id;
    }
}

3. Spring AOP深入解析

3.1 AOP核心概念

  • 切面(Aspect):横切关注点的模块化
  • 连接点(Join Point):程序执行过程中的点(如方法执行)
  • 切点(Pointcut):匹配连接点的表达式
  • 通知(Advice):在切点执行的具体逻辑
  • 目标对象(Target):被代理的对象
  • 代理(Proxy):Spring创建的代理对象

3.2 AOP实现示例

自定义切面

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Aspect
@Component
public class LoggingAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
    
    // 切点:所有Service层的方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}
    
    // 前置通知
    @Before("serviceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        logger.info("执行方法: {},参数: {}", methodName, Arrays.toString(joinPoint.getArgs()));
    }
    
    // 后置通知
    @After("serviceMethods()")
    public void logAfter(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        logger.info("方法执行完成: {}", methodName);
    }
    
    // 返回通知
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        logger.info("方法返回结果: {} -> {}", methodName, result);
    }
    
    // 异常通知
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable ex) {
        String methodName = joinPoint.getSignature().getName();
        logger.error("方法执行异常: {},异常信息: {}", methodName, ex.getMessage());
    }
    
    // 环绕通知(最强大)
    @Around("serviceMethods()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().getName();
        
        logger.info("开始执行方法: {}", methodName);
        
        try {
            // 执行目标方法
            Object result = joinPoint.proceed();
            long endTime = System.currentTimeMillis();
            logger.info("方法执行成功: {},耗时: {}ms", methodName, endTime - startTime);
            return result;
        } catch (Throwable ex) {
            long endTime = System.currentTimeMillis();
            logger.error("方法执行失败: {},耗时: {}ms,异常: {}", 
                methodName, endTime - startTime, ex.getMessage());
            throw ex;
        }
    }
}

使用注解驱动AOP

import org.springframework.stereotype.Component;
import org.springframework.core.annotation.Order;

@Aspect
@Component
@Order(1) // 定义多个切面时的执行顺序
public class TransactionAspect {
    
    @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
    public void transactionalMethods() {}
    
    @Around("transactionalMethods()")
    public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
        // 开启事务
        TransactionStatus status = beginTransaction();
        
        try {
            Object result = joinPoint.proceed();
            // 提交事务
            commitTransaction(status);
            return result;
        } catch (Exception e) {
            // 回滚事务
            rollbackTransaction(status);
            throw e;
        }
    }
    
    private TransactionStatus beginTransaction() {
        // 事务开始逻辑
        return null;
    }
    
    private void commitTransaction(TransactionStatus status) {
        // 事务提交逻辑
    }
    
    private void rollbackTransaction(TransactionStatus status) {
        // 事务回滚逻辑
    }
}

4. Spring MVC实战

4.1 Spring MVC核心组件

  • DispatcherServlet:前端控制器
  • HandlerMapping:请求映射处理器
  • HandlerAdapter:处理器适配器
    • ViewResolver:视图解析器
  • ModelAndView:模型和视图容器

4.2 RESTful API开发示例

import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;
import javax.validation.Valid;

@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    
    private final UserService userService;
    
    public UserController(UserService userService) {
        this.userService = userService;
    }
    
    // GET /api/v1/users/{id}
    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        User user = userService.findById(id);
        if (user == null) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(user);
    }
    
    // GET /api/v1/users?name=John
    @GetMapping
    public ResponseEntity<List<User>> getUsersByName(@RequestParam(required = false) String name) {
        List<User> users = name != null ? 
            userService.findByName(name) : userService.findAll();
        return ResponseEntity.ok(users);
    }
    
    // POST /api/v1/users
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public User createUser(@Valid @RequestBody User user) {
        return userService.save(user);
    }
    
    // PUT /api/v1/users/{id}
    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, 
                                          @Valid @RequestBody User user) {
        if (!userService.existsById(id)) {
            return ResponseEntity.notFound().build();
        }
        user.setId(id);
        return ResponseEntity.ok(userService.save(user));
    }
    
    // DELETE /api/v1/users/{id}
    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteUser(@PathVariable Long id) {
        userService.deleteById(id);
    }
    
    // 处理异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map<String, String> handleValidationExceptions(
            MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        return errors;
    }
}

4.3 文件上传与下载

import org.springframework.web.multipart.MultipartFile;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

@RestController
@RequestMapping("/api/files")
public class FileController {
    
    @PostMapping("/upload")
    public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return ResponseEntity.badRequest().body("文件不能为空");
        }
        
        try {
            // 保存文件到指定目录
            String fileName = file.getOriginalFilename();
            Path path = Paths.get("uploads/" + fileName);
            Files.copy(file.getInputStream(), path, StandardCopyOption.REPLACE_EXISTING);
            
            return ResponseEntity.ok("文件上传成功: " + fileName);
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("文件上传失败: " + e.getMessage());
        }
    }
    
    @GetMapping("/download/{fileName}")
    public ResponseEntity<Resource> downloadFile(@PathVariable String fileName) {
        try {
            Path path = Paths.get("uploads/" + fileName);
            Resource resource = new UrlResource(path.toUri());
            
            if (!resource.exists() || !resource.isReadable()) {
                return ResponseEntity.notFound().build();
            }
            
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setContentDispositionFormData("attachment", fileName);
            
            return ResponseEntity.ok()
                .headers(headers)
                .body(resource);
        } catch (MalformedURLException e) {
            return ResponseEntity.badRequest().build();
        }
    }
}

5. Spring Data JPA实战

5.1 JPA基础配置

# application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/spring_demo
    username: root
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        format_sql: true
        dialect: org.hibernate.dialect.MySQL8Dialect

5.2 实体类定义

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "users")
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, unique = true, length = 50)
    private String username;
    
    @Column(nullable = false, length = 100)
    private String email;
    
    @Column(name = "created_at")
    private LocalDateTime createdAt;
    
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;
    
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Order> orders = new ArrayList<>();
    
    // 构造方法
    public User() {}
    
    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }
    
    // 预持久化回调
    @PrePersist
    protected void onCreate() {
        this.createdAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }
    
    // 预更新回调
    @PreUpdate
    protected void onUpdate() {
        this.updatedAt = LocalDateTime.now();
    }
    
    // Getter和Setter方法
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    
    public LocalDateTime getCreatedAt() { return createdAt; }
    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
    
    public LocalDateTime getUpdatedAt() { return updatedAt; }
    public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
    
    public List<Order> getOrders() { return orders; }
    public void setOrders(List<Order> orders) { this.orders = orders; }
}

5.3 Repository接口

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    
    // 方法名查询
    Optional<User> findByUsername(String username);
    List<User> findByEmailContaining(String email);
    
    // 使用JPQL查询
    @Query("SELECT u FROM User u WHERE u.username = :username AND u.email = :email")
    Optional<User> findByUsernameAndEmail(@Param("username") String username, 
                                         @Param("email") String email);
    
    // 原生SQL查询
    @Query(value = "SELECT * FROM users WHERE created_at > :date", nativeQuery = true)
    List<User> findUsersCreatedAfter(@Param("date") LocalDateTime date);
    
    // 分页查询
    @Query("SELECT u FROM User u WHERE u.username LIKE %:keyword%")
    Page<User> findByUsernameContaining(@Param("keyword") String keyword, Pageable pageable);
    
    // 自定义更新操作
    @Modifying
    @Query("UPDATE User u SET u.email = :email WHERE u.id = :id")
    int updateUserEmail(@Param("id") Long id, @Param("email") String email);
    
    // 复杂查询
    @Query("SELECT u FROM User u JOIN u.orders o WHERE o.status = 'PENDING' GROUP BY u.id")
    List<User> findUsersWithPendingOrders();
}

5.4 Service层实现

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class UserService {
    
    private final UserRepository userRepository;
    
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    // 创建用户
    public User createUser(User user) {
        // 业务验证
        if (userRepository.findByUsername(user.getUsername()).isPresent()) {
            throw new RuntimeException("用户名已存在");
        }
        
        // 密码加密(示例)
        // user.setPassword(passwordEncoder.encode(user.getPassword()));
        
        return userRepository.save(user);
    }
    
    // 获取用户(带分页)
    public Page<User> getUsers(int page, int size, String sortBy) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy));
        return userRepository.findAll(pageable);
    }
    
    // 根据用户名查询
    public Optional<User> findByUsername(String username) {
        return userRepository.findByUsername(username);
    }
    
    // 更新用户信息
    public User updateUser(Long id, User userUpdates) {
        User user = userRepository.findById(id)
            .orElseThrow(() -> new RuntimeException("用户不存在"));
        
        // 只更新允许的字段
        if (userUpdates.getEmail() != null) {
            user.setEmail(userUpdates.getEmail());
        }
        
        return userRepository.save(user);
    }
    
    // 删除用户
    @Transactional
    public void deleteUser(Long id) {
        if (!userRepository.existsById(id)) {
            throw new RuntimeException("用户不存在");
        }
        userRepository.deleteById(id);
    }
    
    // 复杂业务逻辑:获取用户订单统计
    public Map<String, Object> getUserOrderStats(Long userId) {
        User user = userRepository.findById(userId)
            .orElseThrow(() -> new RuntimeException("用户不存在"));
        
        Map<String, Object> stats = new HashMap<>();
        stats.put("totalOrders", user.getOrders().size());
        stats.put("pendingOrders", 
            user.getOrders().stream()
                .filter(order -> "PENDING".equals(order.getStatus()))
                .count());
        stats.put("totalSpent", 
            user.getOrders().stream()
                .mapToDouble(Order::getAmount)
                .sum());
        
        return stats;
    }
}

6. Spring Boot进阶特性

6.1 自动配置原理

Spring Boot通过@SpringBootApplication注解实现自动配置:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication
@EnableConfigurationProperties(AppProperties.class)
@EnableJpaRepositories(basePackages = "com.example.repository")
@EntityScan(basePackages = "com.example.entity")
@ComponentScan(basePackages = "com.example")
public class Application {
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

6.2 自定义Starter开发

创建自定义Starter的步骤:

  1. 创建Starter模块my-spring-boot-starter
  2. 定义自动配置类
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

@AutoConfiguration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyServiceAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public MyService myService(MyProperties properties) {
        return new MyService(properties);
    }
}
  1. 配置自动配置文件:在META-INF/spring.factories中添加:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyServiceAutoConfiguration

6.3 配置文件管理

# application.yml
spring:
  profiles:
    active: dev
  config:
    import: optional:configserver:http://localhost:8888

---
# 开发环境配置
spring:
  config:
    activate:
      on-profile: dev
  datasource:
    url: jdbc:h2:mem:testdb
    username: sa
    password: 
  jpa:
    hibernate:
      ddl-auto: create-drop
    show-sql: true

---
# 生产环境配置
spring:
  config:
    activate:
      on-profile: prod
  datasource:
    url: jdbc:mysql://prod-db:3306/app
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false

7. Spring Security实战

7.1 基础安全配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        UserDetails user = User.withUsername("user")
            .password(passwordEncoder().encode("password"))
            .roles("USER")
            .build();
        
        UserDetails admin = User.withUsername("admin")
            .password(passwordEncoder().encode("admin"))
            .roles("ADMIN", "USER")
            .build();
        
        return new InMemoryUserDetailsManager(user, admin);
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/api/public/**").permitAll()
                .antMatchers("/api/admin/**").hasRole("ADMIN")
                .antMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/home")
                .permitAll()
            .and()
            .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login?logout")
                .permitAll()
            .and()
            .csrf().disable(); // 对于REST API,通常需要禁用CSRF
    }
}

7.2 JWT认证实现

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

@Component
public class JwtTokenProvider {
    
    private static final String SECRET_KEY = "your-secret-key-change-in-production";
    private static final long EXPIRATION_TIME = 86400000; // 24小时
    
    public String generateToken(Authentication authentication) {
        UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();
        
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + EXPIRATION_TIME);
        
        return Jwts.builder()
            .setSubject(userPrincipal.getUsername())
            .setIssuedAt(now)
            .setExpiration(expiryDate)
            .claim("roles", userPrincipal.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toList()))
            .signWith(SignatureAlgorithm.HS512, SECRET_KEY)
            .compact();
    }
    
    public Authentication getAuthentication(String token) {
        Claims claims = Jwts.parser()
            .setSigningKey(SECRET_KEY)
            .parseClaimsJws(token)
            .getBody();
        
        String username = claims.getSubject();
        List<String> roles = claims.get("roles", List.class);
        
        Collection<GrantedAuthority> authorities = roles.stream()
            .map(role -> new SimpleGrantedAuthority(role))
            .collect(Collectors.toList());
        
        return new UsernamePasswordAuthenticationToken(username, null, authorities);
    }
    
    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

8. Spring性能优化技巧

8.1 连接池优化

# application.yml
spring:
  datasource:
    hikari:
      connection-timeout: 30000
      maximum-pool-size: 20
      minimum-idle: 5
      idle-timeout: 600000
      max-lifetime: 1800000
      leak-detection-threshold: 60000
      pool-name: MyHikariCP

8.2 缓存配置

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;

@Configuration
@EnableCaching
public class CacheConfig {
    
    // 使用Redis作为缓存
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .serializeValuesWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new GenericJackson2JsonRedisSerializer()))
            .entryTtl(Duration.ofMinutes(10));
        
        return RedisCacheManager.builder(connectionFactory)
            .cacheDefaults(config)
            .build();
    }
    
    // 使用ConcurrentMap作为缓存(开发环境)
    @Bean
    public CacheManager concurrentMapCacheManager() {
        return new ConcurrentMapCacheManager("users", "products", "orders");
    }
}

8.3 异步处理

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;

@Service
@EnableAsync
public class AsyncService {
    
    @Async("taskExecutor")
    public CompletableFuture<String> processAsync(String data) {
        // 模拟耗时操作
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return CompletableFuture.completedFuture("Processed: " + data);
    }
    
    @Async
    public void sendEmail(String to, String subject, String body) {
        // 发送邮件的耗时操作
        System.out.println("Sending email to " + to);
    }
}

// 配置线程池
@Configuration
public class AsyncConfig {
    
    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("Async-");
        executor.initialize();
        return executor;
    }
}

9. 测试策略

9.1 单元测试

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    
    @Mock
    private UserRepository userRepository;
    
    @InjectMocks
    private UserService userService;
    
    @Test
    void shouldCreateUserSuccessfully() {
        // 准备测试数据
        User user = new User("testuser", "test@example.com");
        when(userRepository.findByUsername("testuser")).thenReturn(Optional.empty());
        when(userRepository.save(any(User.class))).thenReturn(user);
        
        // 执行测试
        User result = userService.createUser(user);
        
        // 验证结果
        assertNotNull(result);
        assertEquals("testuser", result.getUsername());
        verify(userRepository, times(1)).save(user);
    }
    
    @Test
    void shouldThrowExceptionWhenUsernameExists() {
        User existingUser = new User("existing", "existing@example.com");
        when(userRepository.findByUsername("existing"))
            .thenReturn(Optional.of(existingUser));
        
        assertThrows(RuntimeException.class, () -> {
            userService.createUser(new User("existing", "new@example.com"));
        });
    }
}

9.2 集成测试

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
class UserControllerIntegrationTest {
    
    @Autowired
    private TestRestTemplate restTemplate;
    
    @Test
    void shouldCreateUserAndReturn201() {
        User user = new User("integration", "integration@example.com");
        
        ResponseEntity<User> response = restTemplate.postForEntity(
            "/api/v1/users", user, User.class);
        
        assertEquals(HttpStatus.CREATED, response.getStatusCode());
        assertNotNull(response.getBody());
        assertEquals("integration", response.getBody().getUsername());
    }
    
    @Test
    void shouldReturn404ForNonExistentUser() {
        ResponseEntity<User> response = restTemplate.getForEntity(
            "/api/v1/users/99999", User.class);
        
        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
    }
}

10. 实战项目示例:电商系统

10.1 项目架构设计

src/main/java/com/example/ecommerce/
├── config/           # 配置类
├── controller/       # 控制器层
├── service/         # 服务层
├── repository/      # 数据访问层
├── entity/          # 实体类
├── dto/             # 数据传输对象
├── exception/       # 异常处理
└── security/        # 安全配置

10.2 核心实体关系

// 用户实体
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(unique = true)
    private String username;
    
    private String password;
    
    @Enumerated(EnumType.STRING)
    private UserRole role;
    
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<Order> orders;
}

// 商品实体
@Entity
@Table(name = "products")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    private String description;
    private BigDecimal price;
    private Integer stock;
    
    @OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems;
}

// 订单实体
@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;
    
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> items;
    
    private BigDecimal totalAmount;
    
    @Enumerated(EnumType.STRING)
    private OrderStatus status;
    
    private LocalDateTime orderDate;
}

// 订单项实体
@Entity
@Table(name = "order_items")
public class OrderItem {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @ManyToOne
    @JoinColumn(name = "order_id")
    private Order order;
    
    @ManyToOne
    @JoinColumn(name = "product_id")
    private Product product;
    
    private Integer quantity;
    private BigDecimal price;
}

10.3 服务层实现

@Service
@Transactional
public class OrderService {
    
    private final OrderRepository orderRepository;
    private final ProductRepository productRepository;
    private final UserRepository userRepository;
    private final PaymentService paymentService;
    
    public OrderService(OrderRepository orderRepository, 
                       ProductRepository productRepository,
                       UserRepository userRepository,
                       PaymentService paymentService) {
        this.orderRepository = orderRepository;
        this.productRepository = productRepository;
        this.userRepository = userRepository;
        this.paymentService = paymentService;
    }
    
    public Order createOrder(Long userId, List<OrderItemDTO> items) {
        User user = userRepository.findById(userId)
            .orElseThrow(() -> new RuntimeException("用户不存在"));
        
        Order order = new Order();
        order.setUser(user);
        order.setOrderDate(LocalDateTime.now());
        order.setStatus(OrderStatus.PENDING);
        
        BigDecimal totalAmount = BigDecimal.ZERO;
        List<OrderItem> orderItems = new ArrayList<>();
        
        for (OrderItemDTO itemDTO : items) {
            Product product = productRepository.findById(itemDTO.getProductId())
                .orElseThrow(() -> new RuntimeException("商品不存在"));
            
            // 检查库存
            if (product.getStock() < itemDTO.getQuantity()) {
                throw new RuntimeException("库存不足");
            }
            
            // 扣减库存
            product.setStock(product.getStock() - itemDTO.getQuantity());
            productRepository.save(product);
            
            OrderItem orderItem = new OrderItem();
            orderItem.setOrder(order);
            orderItem.setProduct(product);
            orderItem.setQuantity(itemDTO.getQuantity());
            orderItem.setPrice(product.getPrice());
            
            orderItems.add(orderItem);
            totalAmount = totalAmount.add(
                product.getPrice().multiply(BigDecimal.valueOf(itemDTO.getQuantity())));
        }
        
        order.setItems(orderItems);
        order.setTotalAmount(totalAmount);
        
        // 处理支付
        paymentService.processPayment(order);
        
        return orderRepository.save(order);
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void cancelOrder(Long orderId) {
        Order order = orderRepository.findById(orderId)
            .orElseThrow(() -> new RuntimeException("订单不存在"));
        
        if (order.getStatus() != OrderStatus.PENDING) {
            throw new RuntimeException("只能取消待处理订单");
        }
        
        // 恢复库存
        for (OrderItem item : order.getItems()) {
            Product product = item.getProduct();
            product.setStock(product.getStock() + item.getQuantity());
            productRepository.save(product);
        }
        
        order.setStatus(OrderStatus.CANCELLED);
        orderRepository.save(order);
    }
}

11. 常见问题与解决方案

11.1 循环依赖问题

问题:A依赖B,B依赖A,导致Bean创建失败。

解决方案

  1. 使用构造器注入(Spring会尝试解决循环依赖)
  2. 重构代码,引入第三方组件
  3. 使用@Lazy延迟初始化
@Service
public class ServiceA {
    private final ServiceB serviceB;
    
    @Autowired
    public ServiceA(@Lazy ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

11.2 事务失效场景

常见原因

  1. 方法不是public
  2. 自调用问题(this.method())
  3. 异常类型不匹配
  4. 数据库不支持事务

解决方案

@Service
public class MyService {
    
    // 正确:通过代理调用
    @Transactional
    public void method1() {
        // 业务逻辑
    }
    
    // 错误:自调用,事务失效
    public void method2() {
        method1(); // 这里不会触发事务
    }
    
    // 正确:通过注入自身调用
    @Autowired
    private MyService self;
    
    public void method3() {
        self.method1(); // 这里会触发事务
    }
}

11.3 N+1查询问题

问题:查询主实体时,关联实体被延迟加载,导致多次查询。

解决方案

  1. 使用@EntityGraph注解
  2. 使用JOIN FETCH
  3. 使用DTO投影
// 使用@EntityGraph
@EntityGraph(attributePaths = {"orders"})
@Query("SELECT u FROM User u WHERE u.id = :id")
Optional<User> findByIdWithOrders(@Param("id") Long id);

// 使用JOIN FETCH
@Query("SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :id")
Optional<User> findByIdWithOrders(@Param("id") Long id);

// 使用DTO投影
public interface UserOrderSummary {
    Long getId();
    String getUsername();
    List<OrderSummary> getOrders();
    
    interface OrderSummary {
        Long getId();
        BigDecimal getTotalAmount();
    }
}

@Query("SELECT u.id as id, u.username as username, o as orders " +
       "FROM User u JOIN u.orders o WHERE u.id = :id")
UserOrderSummary findUserOrderSummary(@Param("id") Long id);

12. 学习路径建议

12.1 初级阶段(1-2个月)

  1. 掌握Spring Core基础(IoC、DI、Bean生命周期)
  2. 学习Spring MVC基础
  3. 理解AOP概念和基本使用
  4. 完成简单CRUD项目

12.2 中级阶段(2-3个月)

  1. 深入学习Spring Data JPA
  2. 掌握Spring Security基础
  3. 学习Spring Boot自动配置原理
  4. 完成带用户认证的Web应用

12.3 高级阶段(3-6个月)

  1. 深入理解Spring源码
  2. 掌握微服务架构(Spring Cloud)
  3. 学习性能优化和调优
  4. 完成分布式系统项目

12.4 持续学习

  1. 关注Spring官方博客和文档
  2. 参与开源项目贡献
  3. 学习相关技术栈(Docker、Kubernetes、消息队列等)
  4. 参加技术社区和会议

13. 推荐资源

13.1 官方文档

13.2 书籍推荐

  • 《Spring实战》(Craig Walls著)
  • 《Spring Boot编程思想》(小马哥著)
  • 《Spring源码深度解析》(郝佳著)

13.3 在线课程

  • Spring官方培训课程
  • Udemy上的Spring Boot课程
  • 极客时间Spring专栏

13.4 开源项目参考

  • Spring PetClinic示例项目
  • Spring Boot Admin
  • Spring Cloud微服务示例

结语

Spring框架是一个功能强大且不断发展的生态系统。通过本指南的学习,你应该已经掌握了从基础到进阶的核心知识。记住,理论学习必须结合实践,建议你从一个简单的项目开始,逐步增加复杂度,不断挑战自己。

Spring的学习是一个持续的过程,随着版本的更新和新技术的出现,你需要保持学习的热情和好奇心。祝你在Spring开发的道路上取得成功!