引言

Spring框架是Java企业级开发中最流行、最强大的开源框架之一。它提供了一个全面的编程和配置模型,为现代基于Java的企业应用程序提供了坚实的基础。无论你是刚接触Java开发的新手,还是希望提升技能的中级开发者,掌握Spring框架都是职业发展的关键一步。本指南将带你从Spring的基础概念开始,逐步深入到高级特性和实战技巧,帮助你构建健壮、可维护的企业级应用。

第一部分:Spring框架基础

1.1 Spring框架概述

Spring框架是一个分层架构,由大约20个模块组成。这些模块被组织成核心容器、数据访问/集成、Web、AOP(面向切面编程)、工具支持和测试等模块。

核心特性:

  • 依赖注入(DI):通过控制反转(IoC)容器管理对象之间的依赖关系
  • 面向切面编程(AOP):分离横切关注点,如日志、事务管理
  • 声明式事务管理:简化事务处理
  • MVC框架:提供清晰的Web层分离
  • 集成支持:与各种ORM框架、消息队列等无缝集成

1.2 为什么选择Spring?

实际案例对比: 假设我们要开发一个用户管理系统,没有Spring时,我们需要手动创建和管理对象:

// 传统方式 - 手动管理依赖
public class UserService {
    private UserRepository userRepository;
    private EmailService emailService;
    
    public UserService() {
        this.userRepository = new UserRepositoryImpl();
        this.emailService = new EmailServiceImpl();
    }
    
    // 业务方法...
}

使用Spring后:

// Spring方式 - 依赖注入
@Service
public class UserService {
    private final UserRepository userRepository;
    private final EmailService emailService;
    
    @Autowired
    public UserService(UserRepository userRepository, EmailService emailService) {
        this.userRepository = userRepository;
        this.emailService = emailService;
    }
    
    // 业务方法...
}

优势对比:

  • 可测试性:Spring允许轻松注入Mock对象进行单元测试
  • 可维护性:依赖关系清晰,修改实现不影响调用者
  • 灵活性:通过配置可以轻松切换实现

1.3 Spring核心概念详解

1.3.1 IoC容器与Bean

Spring IoC容器负责实例化、配置和组装对象。主要实现有:

  • ApplicationContext(推荐)
  • BeanFactory(较旧)

Bean定义示例:

// 使用注解定义Bean
@Component
public class MyBean {
    // 类实现
}

// 使用Java配置类
@Configuration
public class AppConfig {
    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}

Bean的作用域:

  • singleton(默认):每个容器一个实例
  • prototype:每次请求都创建新实例
  • request:每个HTTP请求一个实例
  • session:每个HTTP会话一个实例
  • global-session:全局HTTP会话

1.3.2 依赖注入(DI)

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

1. 构造器注入(推荐)

@Service
public class OrderService {
    private final PaymentService paymentService;
    private final NotificationService notificationService;
    
    @Autowired
    public OrderService(PaymentService paymentService, 
                       NotificationService notificationService) {
        this.paymentService = paymentService;
        this.notificationService = notificationService;
    }
}

2. Setter注入

@Service
public class OrderService {
    private PaymentService paymentService;
    
    @Autowired
    public void setPaymentService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}

3. 字段注入(不推荐用于生产代码)

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

第二部分:Spring Boot快速入门

2.1 Spring Boot简介

Spring Boot是Spring框架的扩展,旨在简化Spring应用的初始搭建和开发过程。它提供了:

  • 自动配置
  • 嵌入式服务器
  • 生产就绪特性
  • 简化依赖管理

2.2 创建第一个Spring Boot应用

使用Spring Initializr:

  1. 访问 start.spring.io
  2. 选择项目元数据(Group、Artifact)
  3. 添加依赖:Spring Web
  4. 生成项目并导入IDE

项目结构:

my-spring-boot-app/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/example/demo/
│   │   │       ├── DemoApplication.java
│   │   │       └── controller/
│   │   │           └── HelloController.java
│   │   └── resources/
│   │       ├── application.properties
│   │       └── static/
│   └── test/
└── pom.xml

主应用类:

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

创建第一个Controller:

@RestController
public class HelloController {
    
    @GetMapping("/hello")
    public String hello() {
        return "Hello, Spring Boot!";
    }
    
    @GetMapping("/hello/{name}")
    public String helloName(@PathVariable String name) {
        return "Hello, " + name + "!";
    }
}

2.3 Spring Boot自动配置原理

Spring Boot通过@SpringBootApplication注解启用自动配置:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    // ...
}

自动配置工作原理:

  1. Spring Boot启动时扫描META-INF/spring.factories文件
  2. 加载EnableAutoConfiguration键下的配置类
  3. 根据条件(@Conditional注解)决定是否启用配置

自定义自动配置示例:

@Configuration
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public DataSource dataSource(DataSourceProperties properties) {
        return properties.initializeDataSourceBuilder().build();
    }
}

第三部分:Spring核心特性深入

3.1 Spring AOP(面向切面编程)

AOP允许将横切关注点(如日志、事务、安全)从业务逻辑中分离。

AOP术语:

  • 切面(Aspect):横切关注点的模块化
  • 连接点(Join Point):程序执行过程中的点
  • 通知(Advice):在特定连接点执行的动作
  • 切点(Pointcut):匹配连接点的表达式

AOP示例 - 日志记录:

@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("Executing method: {}", methodName);
    }
    
    // 后置通知
    @After("serviceMethods()")
    public void logAfter(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        logger.info("Finished executing method: {}", methodName);
    }
    
    // 异常通知
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
    public void logException(JoinPoint joinPoint, Exception ex) {
        String methodName = joinPoint.getSignature().getName();
        logger.error("Exception in method {}: {}", methodName, ex.getMessage());
    }
    
    // 环绕通知
    @Around("serviceMethods()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            Object result = joinPoint.proceed();
            long end = System.currentTimeMillis();
            logger.info("Method {} executed in {} ms", 
                       joinPoint.getSignature().getName(), 
                       end - start);
            return result;
        } catch (Exception e) {
            logger.error("Exception in method {}: {}", 
                        joinPoint.getSignature().getName(), 
                        e.getMessage());
            throw e;
        }
    }
}

启用AOP:

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
    // 配置类
}

3.2 Spring事务管理

Spring提供了声明式事务管理,通过@Transactional注解实现。

事务传播行为:

  • REQUIRED(默认):如果存在事务则加入,否则新建
  • REQUIRES_NEW:总是新建事务,挂起当前事务
  • MANDATORY:必须在事务中执行
  • SUPPORTS:支持事务,但不强制
  • NOT_SUPPORTED:不支持事务
  • NEVER:绝不能在事务中执行

事务示例:

@Service
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private PaymentService paymentService;
    
    @Transactional(propagation = Propagation.REQUIRED, 
                  rollbackFor = Exception.class)
    public Order createOrder(Order order) {
        // 1. 保存订单
        orderRepository.save(order);
        
        // 2. 处理支付
        paymentService.processPayment(order);
        
        // 3. 发送通知
        sendNotification(order);
        
        return order;
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void sendNotification(Order order) {
        // 独立事务的通知发送
        // ...
    }
}

事务配置:

@Configuration
@EnableTransactionManagement
public class TransactionConfig {
    
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

3.3 Spring事件机制

Spring提供了强大的事件发布/订阅机制。

自定义事件:

// 自定义事件类
public class OrderCreatedEvent extends ApplicationEvent {
    private final Order order;
    
    public OrderCreatedEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }
    
    public Order getOrder() {
        return order;
    }
}

事件发布者:

@Service
public class OrderService {
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    public void createOrder(Order order) {
        // 业务逻辑
        orderRepository.save(order);
        
        // 发布事件
        OrderCreatedEvent event = new OrderCreatedEvent(this, order);
        eventPublisher.publishEvent(event);
    }
}

事件监听器:

@Component
public class OrderEventListener {
    
    @EventListener
    @Async
    public void handleOrderCreated(OrderCreatedEvent event) {
        // 异步处理事件
        Order order = event.getOrder();
        // 发送邮件、更新库存等
    }
    
    @EventListener(condition = "#event.order.amount > 1000")
    public void handleLargeOrder(OrderCreatedEvent event) {
        // 只处理大额订单
        // 发送特殊通知
    }
}

第四部分:Spring Web开发

4.1 Spring MVC架构

Spring MVC遵循Model-View-Controller模式,提供清晰的Web层分离。

核心组件:

  • DispatcherServlet:前端控制器
  • HandlerMapping:请求映射到处理器
  • HandlerAdapter:执行处理器
  • ViewResolver:解析视图
  • ModelAndView:模型和视图的组合

请求处理流程:

客户端请求 → DispatcherServlet → HandlerMapping → HandlerAdapter → Controller → 
ModelAndView → ViewResolver → View → 响应

4.2 RESTful API开发

Controller示例:

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    // 获取用户列表
    @GetMapping
    public ResponseEntity<List<User>> getUsers(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size) {
        List<User> users = userService.getUsers(page, size);
        return ResponseEntity.ok(users);
    }
    
    // 获取单个用户
    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        User user = userService.getUserById(id);
        if (user == null) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(user);
    }
    
    // 创建用户
    @PostMapping
    public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
        User created = userService.createUser(user);
        return ResponseEntity.status(HttpStatus.CREATED).body(created);
    }
    
    // 更新用户
    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, 
                                          @Valid @RequestBody User user) {
        User updated = userService.updateUser(id, user);
        if (updated == null) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(updated);
    }
    
    // 删除用户
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        boolean deleted = userService.deleteUser(id);
        if (!deleted) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.noContent().build();
    }
}

请求参数验证:

// 实体类
public class User {
    @NotNull(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
    private String username;
    
    @Email(message = "邮箱格式不正确")
    @NotNull(message = "邮箱不能为空")
    private String email;
    
    @Min(value = 18, message = "年龄必须大于18岁")
    @Max(value = 100, message = "年龄不能超过100岁")
    private Integer age;
    
    // getters and setters
}

// 全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<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 ResponseEntity.badRequest().body(errors);
    }
}

4.3 文件上传与下载

文件上传:

@RestController
@RequestMapping("/api/files")
public class FileController {
    
    @Value("${file.upload-dir}")
    private String uploadDir;
    
    @PostMapping("/upload")
    public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return ResponseEntity.badRequest().body("文件不能为空");
        }
        
        try {
            // 创建上传目录
            Path uploadPath = Paths.get(uploadDir);
            if (!Files.exists(uploadPath)) {
                Files.createDirectories(uploadPath);
            }
            
            // 生成唯一文件名
            String fileName = UUID.randomUUID().toString() + "_" + file.getOriginalFilename();
            Path filePath = uploadPath.resolve(fileName);
            
            // 保存文件
            Files.copy(file.getInputStream(), filePath, 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 filePath = Paths.get(uploadDir).resolve(fileName);
            if (!Files.exists(filePath)) {
                return ResponseEntity.notFound().build();
            }
            
            Resource resource = new UrlResource(filePath.toUri());
            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, 
                           "attachment; filename=\"" + fileName + "\"")
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(resource);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(null);
        }
    }
}

第五部分:Spring数据访问

5.1 Spring Data JPA

Spring Data JPA简化了JPA的使用,提供了Repository接口。

实体类:

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, unique = true)
    private String username;
    
    @Column(nullable = false)
    private String email;
    
    @Column
    private Integer age;
    
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Order> orders = new ArrayList<>();
    
    // 构造函数、getter/setter
}

Repository接口:

public interface UserRepository extends JpaRepository<User, Long> {
    
    // 自定义查询方法
    Optional<User> findByUsername(String username);
    
    List<User> findByAgeGreaterThan(Integer age);
    
    // JPQL查询
    @Query("SELECT u FROM User u WHERE u.email LIKE %:email%")
    List<User> findByEmailContaining(@Param("email") String email);
    
    // 原生SQL查询
    @Query(value = "SELECT * FROM users WHERE age > :age", nativeQuery = true)
    List<User> findUsersByAgeGreaterThan(@Param("age") Integer age);
    
    // 分页查询
    Page<User> findByAgeGreaterThan(Integer age, Pageable pageable);
    
    // 自定义更新
    @Modifying
    @Query("UPDATE User u SET u.age = :age WHERE u.id = :id")
    int updateUserAge(@Param("id") Long id, @Param("age") Integer age);
}

Service层使用:

@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    public User createUser(User user) {
        // 检查用户名是否已存在
        if (userRepository.findByUsername(user.getUsername()).isPresent()) {
            throw new RuntimeException("用户名已存在");
        }
        return userRepository.save(user);
    }
    
    public List<User> getUsersByAge(Integer age) {
        return userRepository.findByAgeGreaterThan(age);
    }
    
    public Page<User> getUsersPage(int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("id").descending());
        return userRepository.findAll(pageable);
    }
}

5.2 Spring Data MongoDB

对于NoSQL数据库,Spring Data MongoDB提供了类似的支持。

配置:

@Configuration
@EnableMongoRepositories(basePackages = "com.example.repository")
public class MongoConfig {
    
    @Bean
    public MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDbFactory) {
        return new MongoTemplate(mongoDbFactory);
    }
}

文档实体:

@Document(collection = "products")
public class Product {
    @Id
    private String id;
    
    @Field("name")
    private String name;
    
    @Field("price")
    private BigDecimal price;
    
    @Field("category")
    private String category;
    
    @Field("tags")
    private List<String> tags;
    
    // getters and setters
}

MongoDB Repository:

public interface ProductRepository extends MongoRepository<Product, String> {
    
    List<Product> findByCategory(String category);
    
    List<Product> findByPriceBetween(BigDecimal min, BigDecimal max);
    
    @Query("{ 'tags': { $in: ?0 } }")
    List<Product> findByTagsIn(List<String> tags);
}

第六部分:Spring Security安全框架

6.1 Spring Security基础

Spring Security提供了全面的安全解决方案,包括认证、授权、防护等。

基本配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable() // 禁用CSRF(对于REST API)
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public/**").permitAll()
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults()) // 基本认证
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态
            );
        
        return http.build();
    }
    
    @Bean
    public UserDetailsService userDetailsService() {
        // 内存用户存储(生产环境应使用数据库)
        UserDetails user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();
        
        UserDetails admin = User.withDefaultPasswordEncoder()
            .username("admin")
            .password("admin123")
            .roles("ADMIN", "USER")
            .build();
        
        return new InMemoryUserDetailsManager(user, admin);
    }
}

6.2 JWT认证

JWT工具类:

@Component
public class JwtTokenUtil {
    
    @Value("${jwt.secret}")
    private String secret;
    
    @Value("${jwt.expiration}")
    private Long expiration;
    
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("roles", userDetails.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toList()));
        
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
    
    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
    
    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }
    
    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }
    
    private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }
    
    private Claims extractAllClaims(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(secret)
                .build()
                .parseClaimsJws(token)
                .getBody();
    }
    
    private Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }
}

JWT认证过滤器:

@Component
public class JwtRequestFilter extends OncePerRequestFilter {
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                   HttpServletResponse response, 
                                   FilterChain filterChain) throws ServletException, IOException {
        
        final String authorizationHeader = request.getHeader("Authorization");
        
        String username = null;
        String jwt = null;
        
        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            jwt = authorizationHeader.substring(7);
            username = jwtTokenUtil.extractUsername(jwt);
        }
        
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
            
            if (jwtTokenUtil.validateToken(jwt, userDetails)) {
                UsernamePasswordAuthenticationToken authentication = 
                    new UsernamePasswordAuthenticationToken(
                        userDetails, 
                        null, 
                        userDetails.getAuthorities()
                    );
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        
        filterChain.doFilter(request, response);
    }
}

认证控制器:

@RestController
@RequestMapping("/api/auth")
public class AuthController {
    
    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @PostMapping("/login")
    public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthRequest authRequest) {
        try {
            authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                    authRequest.getUsername(), 
                    authRequest.getPassword()
                )
            );
        } catch (BadCredentialsException e) {
            throw new RuntimeException("用户名或密码错误");
        }
        
        final UserDetails userDetails = userDetailsService
                .loadUserByUsername(authRequest.getUsername());
        
        final String jwt = jwtTokenUtil.generateToken(userDetails);
        
        return ResponseEntity.ok(new AuthResponse(jwt));
    }
}

第七部分:Spring测试

7.1 单元测试

使用JUnit 5和Mockito:

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

7.2 集成测试

使用Spring Boot Test:

@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Transactional
class UserServiceIntegrationTest {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    void shouldCreateUserInDatabase() {
        User user = new User();
        user.setUsername("integrationtest");
        user.setEmail("integration@test.com");
        
        User created = userService.createUser(user);
        
        assertNotNull(created.getId());
        
        // 验证数据库中的数据
        Optional<User> found = userRepository.findById(created.getId());
        assertTrue(found.isPresent());
        assertEquals("integrationtest", found.get().getUsername());
    }
}

7.3 Web层测试

使用MockMvc:

@WebMvcTest(UserController.class)
class UserControllerTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @MockBean
    private UserService userService;
    
    @Test
    void shouldReturnUserWhenExists() throws Exception {
        User user = new User();
        user.setId(1L);
        user.setUsername("testuser");
        
        when(userService.getUserById(1L)).thenReturn(user);
        
        mockMvc.perform(get("/api/users/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id").value(1))
                .andExpect(jsonPath("$.username").value("testuser"));
    }
    
    @Test
    void shouldReturn404WhenUserNotFound() throws Exception {
        when(userService.getUserById(999L)).thenReturn(null);
        
        mockMvc.perform(get("/api/users/999"))
                .andExpect(status().isNotFound());
    }
}

第八部分:Spring高级特性

8.1 Spring Cloud微服务

Spring Cloud为构建分布式系统提供了工具。

服务注册与发现(Eureka):

// Eureka Server
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

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

配置中心(Config Server):

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

API网关(Spring Cloud Gateway):

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

// 路由配置
@Configuration
public class GatewayConfig {
    
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("user-service", r -> r.path("/api/users/**")
                .uri("lb://user-service"))
            .route("order-service", r -> r.path("/api/orders/**")
                .uri("lb://order-service"))
            .build();
    }
}

8.2 Spring Reactive编程

Spring WebFlux提供了响应式编程支持。

响应式Controller:

@RestController
@RequestMapping("/api/reactive")
public class ReactiveController {
    
    @Autowired
    private ReactiveUserRepository userRepository;
    
    @GetMapping("/users")
    public Flux<User> getAllUsers() {
        return userRepository.findAll();
    }
    
    @GetMapping("/users/{id}")
    public Mono<User> getUser(@PathVariable String id) {
        return userRepository.findById(id)
                .switchIfEmpty(Mono.error(new UserNotFoundException(id)));
    }
    
    @PostMapping("/users")
    public Mono<User> createUser(@RequestBody User user) {
        return userRepository.save(user);
    }
    
    @GetMapping("/users/stream")
    public Flux<User> streamUsers() {
        return userRepository.findAll()
                .delayElements(Duration.ofMillis(100));
    }
}

响应式Repository:

public interface ReactiveUserRepository extends ReactiveMongoRepository<User, String> {
    
    Flux<User> findByAgeGreaterThan(Integer age);
    
    @Query("{ 'username': ?0 }")
    Mono<User> findByUsername(String username);
}

8.3 Spring批处理

Spring Batch提供了批量处理框架。

批处理配置:

@Configuration
@EnableBatchProcessing
public class BatchConfig {
    
    @Autowired
    private JobBuilderFactory jobBuilderFactory;
    
    @Autowired
    private StepBuilderFactory stepBuilderFactory;
    
    @Bean
    public Job importUserJob() {
        return jobBuilderFactory.get("importUserJob")
                .start(step1())
                .build();
    }
    
    @Bean
    public Step step1() {
        return stepBuilderFactory.get("step1")
                .<User, User>chunk(10)
                .reader(reader())
                .processor(processor())
                .writer(writer())
                .build();
    }
    
    @Bean
    public FlatFileItemReader<User> reader() {
        FlatFileItemReader<User> reader = new FlatFileItemReader<>();
        reader.setResource(new ClassPathResource("users.csv"));
        reader.setLineMapper(new DefaultLineMapper<User>() {{
            setLineTokenizer(new DelimitedLineTokenizer() {{
                setNames(new String[]{"username", "email", "age"});
            }});
            setFieldSetMapper(new BeanWrapperFieldSetMapper<User>() {{
                setTargetType(User.class);
            }});
        }});
        return reader;
    }
    
    @Bean
    public ItemProcessor<User, User> processor() {
        return user -> {
            user.setEmail(user.getEmail().toLowerCase());
            return user;
        };
    }
    
    @Bean
    public ItemWriter<User> writer() {
        return users -> {
            // 批量写入数据库
            // ...
        };
    }
}

第九部分:Spring性能优化

9.1 连接池优化

HikariCP配置:

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

自定义连接池:

@Configuration
public class DataSourceConfig {
    
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    public HikariDataSource dataSource() {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
        dataSource.setUsername("root");
        dataSource.setPassword("password");
        dataSource.setMaximumPoolSize(20);
        dataSource.setMinimumIdle(5);
        dataSource.setConnectionTimeout(30000);
        dataSource.setIdleTimeout(600000);
        dataSource.setMaxLifetime(1800000);
        dataSource.setLeakDetectionThreshold(60000);
        return dataSource;
    }
}

9.2 缓存优化

Spring Cache配置:

@Configuration
@EnableCaching
public class CacheConfig {
    
    @Bean
    public CacheManager cacheManager() {
        RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory())
                .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
                        .entryTtl(Duration.ofMinutes(10))
                        .disableCachingNullValues())
                .build();
        return cacheManager;
    }
    
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName("localhost");
        config.setPort(6379);
        return new JedisConnectionFactory(config);
    }
}

缓存使用:

@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Cacheable(value = "users", key = "#id")
    public User getUserById(Long id) {
        // 这个方法只在缓存未命中时执行
        return userRepository.findById(id).orElse(null);
    }
    
    @CachePut(value = "users", key = "#user.id")
    public User updateUser(User user) {
        return userRepository.save(user);
    }
    
    @CacheEvict(value = "users", key = "#id")
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}

9.3 异步处理

启用异步支持:

@Configuration
@EnableAsync
public class AsyncConfig {
    
    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.setThreadNamePrefix("Async-");
        executor.initialize();
        return executor;
    }
}

异步方法:

@Service
public class NotificationService {
    
    @Async
    public void sendEmail(String to, String subject, String body) {
        // 模拟耗时操作
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("Email sent to: " + to);
    }
    
    @Async
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public CompletableFuture<Void> sendNotificationAsync(User user) {
        // 异步且独立事务
        return CompletableFuture.runAsync(() -> {
            // 发送通知逻辑
        });
    }
}

第十部分:Spring项目实战

10.1 项目架构设计

分层架构:

Controller层(Web层)
    ↓
Service层(业务逻辑层)
    ↓
Repository层(数据访问层)
    ↓
Domain层(领域模型)

项目结构示例:

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

10.2 电商系统实战

用户模块:

// DTO
public class UserRegistrationDTO {
    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 20)
    private String username;
    
    @NotBlank(message = "密码不能为空")
    @Size(min = 6)
    private String password;
    
    @NotBlank(message = "邮箱不能为空")
    @Email
    private String email;
    
    // getters and setters
}

// Controller
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @PostMapping("/register")
    public ResponseEntity<?> register(@Valid @RequestBody UserRegistrationDTO dto) {
        User user = userService.register(dto);
        return ResponseEntity.status(HttpStatus.CREATED).body(user);
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        User user = userService.getUser(id);
        return ResponseEntity.ok(user);
    }
}

// Service
@Service
@Transactional
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    public User register(UserRegistrationDTO dto) {
        if (userRepository.findByUsername(dto.getUsername()).isPresent()) {
            throw new BusinessException("用户名已存在");
        }
        
        User user = new User();
        user.setUsername(dto.getUsername());
        user.setPassword(passwordEncoder.encode(dto.getPassword()));
        user.setEmail(dto.getEmail());
        user.setRole(Role.USER);
        
        return userRepository.save(user);
    }
    
    public User getUser(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new UserNotFoundException(id));
    }
}

商品模块:

// 实体类
@Entity
@Table(name = "products")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false)
    private String name;
    
    @Column(nullable = false)
    private BigDecimal price;
    
    @Column
    private String description;
    
    @Column
    private Integer stock;
    
    @Column
    private String category;
    
    @Column
    private Boolean active = true;
    
    // getters and setters
}

// Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
    
    List<Product> findByCategoryAndActiveTrue(String category);
    
    Page<Product> findByPriceBetweenAndActiveTrue(BigDecimal min, BigDecimal max, Pageable pageable);
    
    @Query("SELECT p FROM Product p WHERE p.name LIKE %:keyword% AND p.active = true")
    List<Product> searchProducts(@Param("keyword") String keyword);
}

订单模块:

@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;
    
    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> items = new ArrayList<>();
    
    @Column(nullable = false)
    private BigDecimal totalAmount;
    
    @Enumerated(EnumType.STRING)
    private OrderStatus status;
    
    @Column
    private String shippingAddress;
    
    @Column
    private LocalDateTime orderDate;
    
    // getters and setters
}

@Service
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private ProductRepository productRepository;
    
    @Autowired
    private UserService userService;
    
    @Transactional
    public Order createOrder(Long userId, List<OrderItemDTO> items) {
        User user = userService.getUser(userId);
        
        Order order = new Order();
        order.setUser(user);
        order.setOrderDate(LocalDateTime.now());
        order.setStatus(OrderStatus.PENDING);
        
        BigDecimal total = BigDecimal.ZERO;
        
        for (OrderItemDTO itemDTO : items) {
            Product product = productRepository.findById(itemDTO.getProductId())
                    .orElseThrow(() -> new ProductNotFoundException(itemDTO.getProductId()));
            
            if (product.getStock() < itemDTO.getQuantity()) {
                throw new InsufficientStockException(product.getName());
            }
            
            OrderItem item = new OrderItem();
            item.setOrder(order);
            item.setProduct(product);
            item.setQuantity(itemDTO.getQuantity());
            item.setUnitPrice(product.getPrice());
            
            order.getItems().add(item);
            
            // 更新库存
            product.setStock(product.getStock() - itemDTO.getQuantity());
            productRepository.save(product);
            
            total = total.add(product.getPrice().multiply(new BigDecimal(itemDTO.getQuantity())));
        }
        
        order.setTotalAmount(total);
        return orderRepository.save(order);
    }
}

10.3 部署与监控

Docker部署:

# Dockerfile
FROM openjdk:17-jdk-slim

WORKDIR /app

# 复制JAR文件
COPY target/ecommerce-app.jar app.jar

# 暴露端口
EXPOSE 8080

# 运行应用
ENTRYPOINT ["java", "-jar", "app.jar"]

Spring Boot Actuator监控:

# application.properties
management.endpoints.web.exposure.include=health,info,metrics
management.endpoint.health.show-details=always
management.metrics.export.prometheus.enabled=true

自定义健康检查:

@Component
public class DatabaseHealthIndicator implements HealthIndicator {
    
    @Autowired
    private DataSource dataSource;
    
    @Override
    public Health health() {
        try (Connection connection = dataSource.getConnection()) {
            return Health.up()
                    .withDetail("database", "MySQL")
                    .withDetail("version", connection.getMetaData().getDatabaseProductVersion())
                    .build();
        } catch (Exception e) {
            return Health.down()
                    .withDetail("error", e.getMessage())
                    .build();
        }
    }
}

第十一部分:最佳实践与常见问题

11.1 代码组织最佳实践

1. 遵循单一职责原则

// 不好的做法 - 一个类做太多事情
@Service
public class OrderService {
    public void createOrder() { /* ... */ }
    public void sendEmail() { /* ... */ }
    public void updateInventory() { /* ... */ }
}

// 好的做法 - 拆分职责
@Service
public class OrderService {
    @Autowired
    private EmailService emailService;
    
    @Autowired
    private InventoryService inventoryService;
    
    public void createOrder() {
        // 只负责订单创建
    }
}

2. 使用DTO避免暴露实体

// 实体类
@Entity
public class User {
    @Id
    private Long id;
    private String username;
    private String password; // 不应该暴露
    private String email;
    private String phone;
    private String address;
}

// DTO类
public class UserDTO {
    private Long id;
    private String username;
    private String email;
    private String phone;
    private String address;
    
    // 不包含password字段
}

11.2 常见问题与解决方案

问题1:循环依赖

// 错误示例
@Service
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}

@Service
public class ServiceB {
    @Autowired
    private ServiceA serviceA;
}

// 解决方案1:使用Setter注入
@Service
public class ServiceA {
    private ServiceB serviceB;
    
    @Autowired
    public void setServiceB(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

// 解决方案2:重构设计
@Service
public class ServiceA {
    @Autowired
    private CommonService commonService;
}

@Service
public class ServiceB {
    @Autowired
    private CommonService commonService;
}

问题2:事务失效

// 错误示例 - 自调用导致事务失效
@Service
public class OrderService {
    
    @Transactional
    public void createOrder() {
        // 事务开始
        saveOrder();
        // 事务结束
    }
    
    // 自调用,事务不生效
    private void saveOrder() {
        // 数据库操作
    }
}

// 解决方案1:提取到另一个Bean
@Service
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Transactional
    public void createOrder() {
        orderRepository.save(new Order());
    }
}

// 解决方案2:使用AopContext.currentProxy()
@Service
public class OrderService {
    
    @Transactional
    public void createOrder() {
        saveOrder();
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveOrder() {
        // 独立事务
    }
}

问题3:N+1查询问题

// 错误示例 - 导致N+1查询
@Entity
public class User {
    @OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
    private List<Order> orders;
}

// 查询用户时,每个用户的订单都会单独查询
List<User> users = userRepository.findAll();
for (User user : users) {
    user.getOrders().size(); // 触发N+1查询
}

// 解决方案1:使用JOIN FETCH
@Query("SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :id")
User findUserWithOrders(@Param("id") Long id);

// 解决方案2:使用EntityGraph
@EntityGraph(attributePaths = {"orders"})
User findById(Long id);

// 解决方案3:批量查询
List<User> users = userRepository.findAll();
List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
List<Order> orders = orderRepository.findByUserIdIn(userIds);
Map<Long, List<Order>> ordersByUserId = orders.stream()
        .collect(Collectors.groupingBy(Order::getUserId));
users.forEach(user -> user.setOrders(ordersByUserId.getOrDefault(user.getId(), Collections.emptyList())));

11.3 性能调优技巧

1. 数据库查询优化

// 使用索引
@Entity
@Table(indexes = {
    @Index(name = "idx_username", columnList = "username"),
    @Index(name = "idx_email", columnList = "email")
})
public class User {
    // ...
}

// 使用分页
Page<User> users = userRepository.findAll(PageRequest.of(0, 20));

// 使用投影
public interface UserProjection {
    String getUsername();
    String getEmail();
}

@Query("SELECT u.username as username, u.email as email FROM User u")
List<UserProjection> findUserProjections();

2. 缓存策略

// 多级缓存
@Service
public class ProductService {
    
    @Autowired
    private ProductRepository productRepository;
    
    @Autowired
    private RedisTemplate<String, Product> redisTemplate;
    
    @Cacheable(value = "products", key = "#id")
    public Product getProduct(Long id) {
        // 先查Redis缓存
        String key = "product:" + id;
        Product product = redisTemplate.opsForValue().get(key);
        if (product != null) {
            return product;
        }
        
        // 再查数据库
        product = productRepository.findById(id).orElse(null);
        if (product != null) {
            // 写入Redis
            redisTemplate.opsForValue().set(key, product, Duration.ofMinutes(10));
        }
        
        return product;
    }
}

第十二部分:学习资源与进阶路线

12.1 推荐学习资源

官方文档:

书籍推荐:

  1. 《Spring实战》(Craig Walls著)
  2. 《Spring Boot实战》(Craig Walls著)
  3. 《Spring Cloud微服务实战》(翟永超著)
  4. 《Spring Data JPA实战》

在线课程:

  • Spring官方教程
  • Udemy Spring Boot课程
  • Coursera Spring框架课程

12.2 进阶学习路线

阶段1:基础掌握(1-2个月)

  • Spring Core(IoC、DI、AOP)
  • Spring Boot基础
  • Spring MVC
  • Spring Data JPA

阶段2:进阶应用(2-3个月)

  • Spring Security
  • Spring事务管理
  • Spring测试
  • RESTful API设计

阶段3:高级特性(3-4个月)

  • Spring Cloud微服务
  • Spring WebFlux响应式编程
  • Spring Batch批处理
  • Spring集成

阶段4:实战项目(持续)

  • 参与开源项目
  • 构建个人项目
  • 学习性能优化
  • 掌握部署运维

12.3 社区与交流

活跃社区:

  • Spring官方论坛
  • Stack Overflow Spring标签
  • GitHub Spring项目
  • Reddit r/springframework

技术博客:

  • Spring官方博客
  • Baeldung Spring教程
  • Spring Boot中文社区
  • 各大技术平台Spring专栏

结语

Spring框架的学习是一个循序渐进的过程,从基础概念到高级特性,再到实战应用,每一步都需要扎实的理论基础和大量的实践。本指南涵盖了Spring开发的各个方面,但真正的精通来自于不断的实践和项目经验。

建议的学习路径:

  1. 从Spring Boot开始,快速搭建第一个应用
  2. 深入理解Spring Core的核心概念
  3. 逐步学习Spring Data、Spring Security等模块
  4. 通过实际项目巩固知识
  5. 持续关注Spring生态的发展

记住,框架只是工具,真正的价值在于你如何使用它来解决实际问题。保持好奇心,持续学习,你一定能成为Spring领域的专家!

最后,祝你学习顺利,编码愉快!