引言:为什么选择Spring框架?

Spring框架是Java生态系统中最流行的企业级应用开发框架,自2003年诞生以来,已经成为Java开发者的必备技能。对于新手来说,Spring提供了全面的编程和配置模型,能够显著简化企业级应用的开发复杂度。本文将从零基础出发,系统地介绍Spring的核心概念,并通过实战技巧帮助新手快速上手。

Spring框架的核心优势在于其”非侵入式”设计,这意味着你的代码不会深度依赖于Spring框架本身。同时,Spring的依赖注入(DI)和面向切面编程(AOP)特性,使得代码更加模块化、易于测试和维护。根据2023年的开发者调查,超过80%的Java企业应用都在使用Spring框架,掌握它将为你的职业发展打开广阔的大门。

第一部分:Spring核心概念详解

1.1 控制反转(IoC)与依赖注入(DI)

控制反转(Inversion of Control, IoC)是Spring框架的设计理念核心。传统编程中,对象自己创建和管理依赖的对象;而在IoC模式下,这个过程被反转了,由外部容器(Spring IoC容器)来负责创建和管理对象及其依赖关系。

依赖注入(Dependency Injection, DI)是实现IoC的具体方式。Spring通过三种主要方式实现DI:

  • 构造器注入(推荐)
  • Setter方法注入
  • 字段注入(不推荐用于生产代码)

代码示例:构造器注入

// 定义服务接口
public interface UserService {
    void saveUser(String username);
}

// 实现类
@Service
public class UserServiceImpl implements UserService {
    private final UserRepository userRepository;
    
    // 构造器注入
    @Autowired
    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    @Override
    public void saveUser(String username) {
        userRepository.save(username);
    }
}

// Repository层
@Repository
public class UserRepository {
    public void save(String username) {
        System.out.println("保存用户: " + username);
    }
}

// 配置类
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
    // 配置Bean定义
}

// 测试代码
public class Application {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = 
            new AnnotationConfigApplicationContext(AppConfig.class);
        
        UserService userService = context.getBean(UserService.class);
        userService.saveUser("张三");
        
        context.close();
    }
}

在这个例子中,UserServiceImpl并不知道UserRepository是如何创建的,它只需要声明构造器参数,Spring容器会自动注入合适的实例。这种设计使得代码更加松耦合,便于单元测试(可以轻松注入Mock对象)。

1.2 Spring Bean的生命周期

理解Bean的生命周期对于调试和高级配置至关重要。Spring Bean从创建到销毁经历以下阶段:

  1. 实例化:容器通过构造器或工厂方法创建Bean实例
  2. 属性赋值:填充Bean的属性值(依赖注入)
  3. 初始化前处理:调用BeanPostProcessorpostProcessBeforeInitialization方法
  4. 初始化:调用InitializingBeanafterPropertiesSet方法或自定义的init-method
  5. 初始化后处理:调用BeanPostProcessorpostProcessAfterInitialization方法
  6. 使用:Bean已就绪,可以被应用程序使用
  7. 销毁:容器关闭时,调用DisposableBeandestroy方法或自定义的destroy-method

代码示例:自定义初始化和销毁逻辑

@Component
public class LifecycleBean implements InitializingBean, DisposableBean {
    
    public LifecycleBean() {
        System.out.println("1. 构造器被调用");
    }
    
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("3. InitializingBean.afterPropertiesSet() 被调用");
    }
    
    @PreDestroy
    public void customDestroy() {
        System.out.println("7. @PreDestroy 方法被调用");
    }
    
    @Override
    public void destroy() throws Exception {
        System.out.println("6. DisposableBean.destroy() 被调用");
    }
    
    @PostConstruct
    public void customInit() {
        System.out.println("2. @PostConstruct 方法被调用");
    }
    
    // XML配置方式的替代
    // <bean id="lifecycleBean" class="com.example.LifecycleBean" 
    //       init-method="customInit" destroy-method="customDestroy"/>
}

1.3 Spring Bean的作用域

Spring支持多种Bean作用域,理解它们对于内存管理和并发控制很重要:

  • singleton(默认):每个Spring容器中一个Bean定义,一个实例
  • prototype:每次请求都创建一个新实例
  • request:每个HTTP请求一个实例(Web环境)
  • session:每个HTTP会话一个实例(Web环境)
  • global-session:全局HTTP会话(Portlet环境)

代码示例:作用域配置

@Configuration
public class ScopeConfig {
    
    @Bean
    @Scope("singleton")
    public SingletonBean singletonBean() {
        return new SingletonBean();
    }
    
    @Bean
    @Scope("prototype")
    public PrototypeBean prototypeBean() {
        return new PrototypeBean();
    }
    
    // Web环境下request作用域
    @Bean
    @RequestScope
    public RequestScopedBean requestBean() {
        return new RequestScopedBean();
    }
}

第二部分:Spring Boot快速入门

2.1 Spring Boot的核心理念

Spring Boot是Spring框架的”约定优于配置”理念的完美体现。它通过自动配置和起步依赖,极大地简化了Spring应用的搭建过程。对于新手来说,Spring Boot是学习Spring的最佳起点。

Spring Boot的主要特性:

  • 内嵌Tomcat/Jetty服务器,无需部署WAR文件
  • 自动配置大多数常见场景
  • 提供生产级监控和健康检查
  • 彻底告别繁琐的XML配置

2.2 创建第一个Spring Boot项目

方法一:使用Spring Initializr(推荐) 访问 start.spring.io,选择:

  • Project: Maven Project
  • Language: Java
  • Spring Boot: 3.x版本
  • Dependencies: Spring Web

方法二:使用IDE 在IntelliJ IDEA或Eclipse中选择”New Project” → “Spring Initializr”

方法三:命令行(使用cURL)

curl https://start.spring.io/starter.zip \
  -d dependencies=web \
  -d groupId=com.example \
  -d artifactId=demo \
  -o demo.zip

2.3 项目结构解析

标准的Spring Boot项目结构:

demo/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/example/demo/
│   │   │       ├── DemoApplication.java    // 主启动类
│   │   │       ├── controller/              // 控制器
│   │   │       ├── service/                 // 服务层
│   │   │       ├── repository/              // 数据访问层
│   │   │       └── config/                  // 配置类
│   │   └── resources/
│   │       ├── static/                      // 静态资源
│   │       ├── templates/                   // 模板文件
│   │       └── application.properties       // 配置文件
│   └── test/                                // 测试代码
├── pom.xml                                  // Maven配置
└── target/                                  // 编译输出

2.4 编写第一个REST API

代码示例:完整的Spring Boot应用

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

// 实体类
public class User {
    private Long id;
    private String name;
    private String email;
    
    // 构造器、getter/setter省略
}

// Controller层
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    private final UserService userService;
    
    public UserController(UserService userService) {
        this.userService = userService;
    }
    
    // GET /api/users
    @GetMapping
    public List<User> getAllUsers() {
        return userService.findAll();
    }
    
    // GET /api/users/{id}
    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        return userService.findById(id)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }
    
    // POST /api/users
    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.save(user);
    }
    
    // PUT /api/users/{id}
    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
        try {
            User updated = userService.update(id, user);
            return ResponseEntity.ok(updated);
        } catch (RuntimeException e) {
            return ResponseEntity.notFound().build();
        }
    }
    
    // DELETE /api/users/{id}
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.delete(id);
        return ResponseEntity.noContent().build();
    }
}

// Service层
@Service
public class UserService {
    
    private final Map<Long, User> userStore = new ConcurrentHashMap<>();
    private AtomicLong idGenerator = new AtomicLong(1);
    
    public List<User> findAll() {
        return new ArrayList<>(userStore.values());
    }
    
    public Optional<User> findById(Long id) {
        return Optional.ofNullable(userStore.get(id));
    }
    
    public User save(User user) {
        Long newId = idGenerator.getAndIncrement();
        user.setId(newId);
        userStore.put(newId, user);
        return user;
    }
    
    public User update(Long id, User user) {
        if (!userStore.containsKey(id)) {
            throw new RuntimeException("User not found");
        }
        user.setId(id);
        userStore.put(id, user);
        return user;
    }
    
    public void delete(Long id) {
        userStore.remove(id);
    }
}

测试API:

# 创建用户
curl -X POST http://localhost:8080/api/users \
  -H "Content-Type: application/json" \
  -d '{"name":"张三","email":"zhangsan@example.com"}'

# 获取所有用户
curl http://localhost:8080/api/users

# 获取特定用户
curl http://localhost:8080/api/users/1

# 更新用户
curl -X PUT http://localhost:8080/api/users/1 \
  -H "Content-Type: application/json" \
  -d '{"name":"张三丰","email":"zhangsanfeng@example.com"}'

# 删除用户
curl -X DELETE http://localhost:8080/api/users/1

第三部分:Spring Data JPA与数据库集成

3.1 什么是JPA?

JPA(Java Persistence API)是Java持久化规范,它定义了对象关系映射(ORM)的标准API。Spring Data JPA在此基础上提供了更加简洁的Repository抽象,大幅减少了样板代码。

3.2 配置数据源和JPA

添加依赖(pom.xml)

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

配置application.properties

# 数据源配置
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# JPA配置
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

# H2控制台(开发环境)
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

3.3 实体映射与Repository

代码示例:完整JPA应用

// 实体类
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, length = 100)
    private String name;
    
    @Column(nullable = false, unique = true, length = 255)
    private String email;
    
    @Column(name = "created_at")
    @CreationTimestamp
    private LocalDateTime createdAt;
    
    // JPA要求必须有无参构造器
    public User() {}
    
    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }
    
    // Getter和Setter省略
}

// Repository接口
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    
    // Spring Data JPA会根据方法名自动生成查询
    List<User> findByName(String name);
    
    List<User> findByEmailContaining(String email);
    
    // 使用@Query注解自定义查询
    @Query("SELECT u FROM User u WHERE u.email = :email")
    Optional<User> findByEmail(@Param("email") String email);
    
    // 原生SQL查询
    @Query(value = "SELECT * FROM users WHERE created_at > :date", nativeQuery = true)
    List<User> findUsersCreatedAfter(@Param("date") LocalDateTime date);
    
    // 分页查询
    Page<User> findAll(Pageable pageable);
    
    // 自定义更新操作
    @Modifying
    @Query("UPDATE User u SET u.name = :name WHERE u.id = :id")
    int updateName(@Param("id") Long id, @Param("name") String name);
}

// Service层使用Repository
@Service
@Transactional
public class UserService {
    
    private final UserRepository userRepository;
    
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    public User createUser(String name, String email) {
        // 检查邮箱是否已存在
        if (userRepository.findByEmail(email).isPresent()) {
            throw new RuntimeException("邮箱已存在: " + email);
        }
        
        User user = new User(name, email);
        return userRepository.save(user);
    }
    
    public Optional<User> getUserById(Long id) {
        return userRepository.findById(id);
    }
    
    public List<User> searchUsers(String keyword) {
        return userRepository.findByEmailContaining(keyword);
    }
    
    public Page<User> getUsers(int page, int size) {
        return userRepository.findAll(PageRequest.of(page, size, Sort.by("createdAt").descending()));
    }
    
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}

3.4 事务管理

Spring的声明式事务管理是其强大功能之一。通过@Transactional注解,可以轻松管理事务边界。

代码示例:事务传播行为

@Service
public class BankService {
    
    private final AccountRepository accountRepository;
    
    public BankService(AccountRepository accountRepository) {
        this.accountRepository = accountRepository;
    }
    
    // REQUIRED: 如果当前存在事务,则加入;否则新建事务(默认)
    @Transactional(propagation = Propagation.REQUIRED)
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        Account from = accountRepository.findById(fromId)
            .orElseThrow(() -> new RuntimeException("转出账户不存在"));
        Account to = accountRepository.findById(toId)
            .orElseThrow(() -> new RuntimeException("转入账户不存在"));
        
        if (from.getBalance().compareTo(amount) < 0) {
            throw new RuntimeException("余额不足");
        }
        
        from.setBalance(from.getBalance().subtract(amount));
        to.setBalance(to.getBalance().add(amount));
        
        accountRepository.save(from);
        accountRepository.save(to);
    }
    
    // REQUIRES_NEW: 挂起当前事务,创建新事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logTransaction(Long accountId, BigDecimal amount) {
        // 记录交易日志,独立事务
    }
    
    // NESTED: 嵌套事务(如果底层数据库支持保存点)
    @Transactional(propagation = Propagation.NESTED)
    public void nestedOperation() {
        // 嵌套事务操作
    }
}

第四部分:Spring AOP面向切面编程

4.1 AOP核心概念

AOP(Aspect-Oriented Programming)允许开发者在不修改业务代码的情况下,为程序添加横切关注点(如日志、安全、事务等)。

核心术语:

  • 切面(Aspect):封装横切关注点的模块
  • 连接点(Join Point):程序执行过程中的某个点(如方法执行)
  • 通知(Advice):切面在连接点执行的具体操作
  • 切入点(Pointcut):匹配连接点的表达式
  • 目标对象(Target):被通知的对象
  • 代理(Proxy):Spring创建的对象,包含目标对象和通知

4.2 自定义切面示例

代码示例:日志切面

// 切面类
@Aspect
@Component
public class LoggingAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
    
    // 定义切入点:所有public方法
    @Pointcut("execution(public * com.example.demo.service.*.*(..))")
    public void serviceMethods() {}
    
    // 前置通知
    @Before("serviceMethods()")
    public void logMethodStart(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        logger.info("执行方法: {}.{},参数: {}", 
            joinPoint.getTarget().getClass().getSimpleName(),
            methodName, 
            Arrays.toString(args));
    }
    
    // 返回后通知
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void logMethodReturn(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        logger.info("方法 {}.{} 执行成功,返回: {}", 
            joinPoint.getTarget().getClass().getSimpleName(),
            methodName, 
            result);
    }
    
    // 异常通知
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
    public void logMethodException(JoinPoint joinPoint, Exception ex) {
        String methodName = joinPoint.getSignature().getName();
        logger.error("方法 {}.{} 执行异常: {}", 
            joinPoint.getTarget().getClass().getSimpleName(),
            methodName, 
            ex.getMessage(), ex);
    }
    
    // 环绕通知(最强大,可以控制方法执行)
    @Around("serviceMethods()")
    public Object measureExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            Object result = pjp.proceed(); // 执行目标方法
            long duration = System.currentTimeMillis() - start;
            logger.info("方法执行耗时: {} ms", duration);
            return result;
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - start;
            logger.error("方法执行失败,耗时: {} ms", duration);
            throw e;
        }
    }
}

// 启用AOP
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}

代码示例:性能监控切面

@Aspect
@Component
public class PerformanceAspect {
    
    // 使用注解作为切入点
    @Pointcut("@annotation(com.example.demo.annotation.Timed)")
    public void annotatedMethods() {}
    
    @Around("annotatedMethods()")
    public Object timeMethod(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        Timed timed = method.getAnnotation(Timed.class);
        
        String metricName = timed.value().isEmpty() 
            ? method.getName() 
            : timed.value();
        
        long start = System.currentTimeMillis();
        try {
            return pjp.proceed();
        } finally {
            long duration = System.currentTimeMillis() - start;
            // 发送到监控系统
            Metrics.counter("method.execution.time", "metric", metricName)
                  .increment(duration);
        }
    }
}

// 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Timed {
    String value() default "";
}

第五部分:Spring Security安全集成

5.1 安全基础配置

Spring Security是Spring生态系统中的安全框架,提供认证和授权功能。

添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

基础配置

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable() // 对于REST API,通常禁用CSRF
            .authorizeHttpRequests(authz -> authz
                .requestMatchers("/api/public/**").permitAll()
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .requestMatchers("/api/users/**").authenticated()
                .anyRequest().denyAll()
            )
            .httpBasic(Customizer.withDefaults()) // 使用HTTP Basic认证
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态
            );
        
        return http.build();
    }
    
    // 内存用户存储(开发环境)
    @Bean
    public UserDetailsService users() {
        UserDetails user = User.builder()
            .username("user")
            .password("{bcrypt}$2a$10$GRLdN1uZ5vK1vQ5vK1vQ5uO5vK1vQ5vK1vQ5vK1vQ5vK1vQ5vK1vQ5vK1v")
            .roles("USER")
            .build();
        
        UserDetails admin = User.builder()
            .username("admin")
            .password("{bcrypt}$2a$10$GRLdN1uZ5vK1vQ5vK1vQ5uO5vK1vQ5vK1vQ5vK1vQ5vK1vQ5vK1vQ5vK1v")
            .roles("USER", "ADMIN")
            .build();
        
        return new InMemoryUserDetailsManager(user, admin);
    }
    
    // 密码编码器
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

5.2 JWT认证实现

代码示例:JWT工具类

@Component
public class JwtUtil {
    
    private static final String SECRET_KEY = "your-secret-key-min-256-bits";
    private static final long EXPIRATION_TIME = 86400000; // 24小时
    
    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_TIME))
            .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
            .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 getClaimsFromToken(token).getSubject();
    }
    
    public Boolean isTokenExpired(String token) {
        return getClaimsFromToken(token).getExpiration().before(new Date());
    }
    
    private Claims getClaimsFromToken(String token) {
        return Jwts.parser()
            .setSigningKey(SECRET_KEY)
            .parseClaimsJws(token)
            .getBody();
    }
}

// JWT认证过滤器
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    
    @Autowired
    private JwtUtil jwtUtil;
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                   HttpServletResponse response, 
                                   FilterChain filterChain) throws ServletException, IOException {
        
        final String authHeader = request.getHeader("Authorization");
        
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            filterChain.doFilter(request, response);
            return;
        }
        
        final String jwt = authHeader.substring(7);
        final String username = jwtUtil.extractUsername(jwt);
        
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            
            if (jwtUtil.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);
    }
}

// 更新Security配置
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Autowired
    private JwtAuthenticationFilter jwtFilter;
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeHttpRequests(authz -> authz
                .requestMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
            )
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
        
        return http.build();
    }
}

第六部分:测试策略与最佳实践

6.1 单元测试

代码示例:使用JUnit 5和Mockito

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    
    @Mock
    private UserRepository userRepository;
    
    @InjectMocks
    private UserService userService;
    
    @Test
    void shouldCreateUserSuccessfully() {
        // 准备数据
        String name = "张三";
        String email = "zhangsan@example.com";
        
        // 模拟行为
        when(userRepository.findByEmail(email)).thenReturn(Optional.empty());
        when(userRepository.save(any(User.class))).thenAnswer(invocation -> {
            User user = invocation.getArgument(0);
            user.setId(1L);
            return user;
        });
        
        // 执行测试
        User result = userService.createUser(name, email);
        
        // 验证结果
        assertNotNull(result.getId());
        assertEquals(name, result.getName());
        assertEquals(email, result.getEmail());
        
        // 验证交互
        verify(userRepository).findByEmail(email);
        verify(userRepository).save(any(User.class));
    }
    
    @Test
    void shouldThrowExceptionWhenEmailExists() {
        String email = "existing@example.com";
        when(userRepository.findByEmail(email)).thenReturn(Optional.of(new User()));
        
        assertThrows(RuntimeException.class, () -> {
            userService.createUser("测试", email);
        });
        
        verify(userRepository, never()).save(any());
    }
}

6.2 集成测试

代码示例:Spring Boot集成测试

@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource(locations = "classpath:application-test.properties")
class UserControllerIntegrationTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @Autowired
    private UserRepository userRepository;
    
    @BeforeEach
    void setUp() {
        userRepository.deleteAll();
    }
    
    @Test
    void shouldCreateAndRetrieveUser() throws Exception {
        // 创建用户
        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"name\":\"李四\",\"email\":\"lisi@example.com\"}"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id").exists())
                .andExpect(jsonPath("$.name").value("李四"));
        
        // 验证数据库
        assertEquals(1, userRepository.count());
        
        // 查询用户
        mockMvc.perform(get("/api/users/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.email").value("lisi@example.com"));
    }
    
    @Test
    void shouldReturn404ForNonExistentUser() throws Exception {
        mockMvc.perform(get("/api/users/999"))
                .andExpect(status().isNotFound());
    }
}

6.3 测试配置

application-test.properties

spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=false

第七部分:高级主题与性能优化

7.1 缓存配置

代码示例:Spring Cache

@Configuration
@EnableCaching
public class CacheConfig {
    
    @Bean
    public CacheManager cacheManager() {
        // 使用ConcurrentMapCacheManager作为简单实现
        return new ConcurrentMapCacheManager("users", "products");
    }
}

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

7.2 异步处理

代码示例:@Async

@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 EmailService {
    
    @Async
    public void sendWelcomeEmail(String email) {
        // 模拟耗时操作
        try {
            Thread.sleep(2000);
            System.out.println("发送欢迎邮件到: " + email);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    @Async
    public CompletableFuture<String> processBatch(List<String> items) {
        // 异步批量处理
        List<String> results = items.stream()
            .map(item -> "Processed: " + item)
            .collect(Collectors.toList());
        return CompletableFuture.completedFuture(results.toString());
    }
}

7.3 配置外部化

代码示例:多环境配置

# application.yml
spring:
  profiles:
    active: dev

---
# 开发环境
spring:
  config:
    activate:
      on-profile: dev
  datasource:
    url: jdbc:h2:mem:devdb
    username: sa
    password:
  jpa:
    show-sql: true

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

使用@Value和@ConfigurationProperties

@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
    private String name;
    private String version;
    private final Email email = new Email();
    
    public static class Email {
        private String from;
        private String host;
        // getters/setters
    }
    // getters/setters
}

@Service
public class ConfigService {
    @Value("${app.name}")
    private String appName;
    
    @Value("${app.email.from}")
    private String fromEmail;
    
    @Value("${app.feature.enabled:false}")
    private boolean featureEnabled;
}

第八部分:实战项目结构与最佳实践

8.1 分层架构最佳实践

src/main/java/com/example/demo/
├── DemoApplication.java
├── config/                    # 配置类
│   ├── WebConfig.java
│   ├── SecurityConfig.java
│   └── CacheConfig.java
├── controller/                # 控制器层
│   ├── UserController.java
│   └── dto/                   # DTO类
│       ├── UserDTO.java
│       └── UserCreateRequest.java
├── service/                   # 服务层
│   ├── UserService.java
│   └── impl/
│       └── UserServiceImpl.java
├── repository/                # 数据访问层
│   ├── UserRepository.java
│   └── custom/                # 自定义Repository
│       └── UserRepositoryCustom.java
├── entity/                    # 实体类
│   ├── User.java
│   └── AuditEntity.java      # 基础审计实体
├── exception/                 # 异常处理
│   ├── GlobalExceptionHandler.java
│   ├── ResourceNotFoundException.java
│   └── BadRequestException.java
├── dto/                       # 数据传输对象
│   ├── response/
│   └── request/
├── util/                      # 工具类
│   └── JwtUtil.java
└── aspect/                    # 切面
    └── LoggingAspect.java

8.2 全局异常处理

代码示例:统一异常处理

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    
    @ExceptionHandler(ResourceNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ErrorResponse handleResourceNotFound(ResourceNotFoundException ex) {
        logger.warn("资源未找到: {}", ex.getMessage());
        return new ErrorResponse("RESOURCE_NOT_FOUND", ex.getMessage());
    }
    
    @ExceptionHandler(BadRequestException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorResponse handleBadRequest(BadRequestException ex) {
        logger.warn("请求错误: {}", ex.getMessage());
        return new ErrorResponse("BAD_REQUEST", ex.getMessage());
    }
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorResponse handleValidationException(MethodArgumentNotValidException ex) {
        List<String> errors = ex.getBindingResult().getFieldErrors().stream()
            .map(error -> error.getField() + ": " + error.getDefaultMessage())
            .collect(Collectors.toList());
        return new ErrorResponse("VALIDATION_FAILED", String.join(", ", errors));
    }
    
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ErrorResponse handleGenericException(Exception ex) {
        logger.error("系统异常: ", ex);
        return new ErrorResponse("INTERNAL_ERROR", "系统内部错误,请稍后重试");
    }
}

// 自定义异常
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

// 错误响应DTO
public class ErrorResponse {
    private String code;
    private String message;
    private LocalDateTime timestamp;
    
    public ErrorResponse(String code, String message) {
        this.code = code;
        this.message = message;
        this.timestamp = LocalDateTime.now();
    }
    // getters
}

8.3 DTO模式与数据验证

代码示例:DTO与验证

// 创建请求DTO
public class UserCreateRequest {
    @NotBlank(message = "用户名不能为空")
    @Size(min = 2, max = 50, message = "用户名长度必须在2-50之间")
    private String name;
    
    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @Pattern(regexp = "^(?=.*[A-Z])(?=.*\\d).{8,}$", message = "密码必须包含大写字母和数字,至少8位")
    private String password;
    
    // getters/setters
}

// 响应DTO
public class UserResponse {
    private Long id;
    private String name;
    private String email;
    private LocalDateTime createdAt;
    
    // 构造函数从实体转换
    public static UserResponse fromEntity(User user) {
        UserResponse response = new UserResponse();
        response.setId(user.getId());
        response.setName(user.getName());
        response.setEmail(user.getEmail());
        response.setCreatedAt(user.getCreatedAt());
        return response;
    }
    // getters
}

// Controller中使用
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @PostMapping
    public ResponseEntity<UserResponse> createUser(
            @Valid @RequestBody UserCreateRequest request) {
        User user = userService.createUser(request.getName(), request.getEmail(), request.getPassword());
        return ResponseEntity.ok(UserResponse.fromEntity(user));
    }
}

8.4 日志配置

logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/application.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/application.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </root>
    
    <!-- 特定包的日志级别 -->
    <logger name="com.example.demo" level="DEBUG"/>
    <logger name="org.springframework.security" level="DEBUG"/>
</configuration>

第九部分:部署与监控

9.1 打包与运行

Maven打包

# 生产环境打包(跳过测试)
mvn clean package -DskipTests

# 运行
java -jar target/demo-1.0.0.jar

# 指定环境
java -jar demo-1.0.0.jar --spring.profiles.active=prod

# 内存配置
java -Xms512m -Xmx1024m -jar demo-1.0.0.jar

Docker部署

# Dockerfile
FROM openjdk:17-jdk-slim

WORKDIR /app

# 复制jar包
COPY target/demo-1.0.0.jar app.jar

# 暴露端口
EXPOSE 8080

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

构建和运行

docker build -t demo-app:1.0 .
docker run -d -p 8080:8080 --name demo-container demo-app:1.0

9.2 Spring Boot Actuator监控

添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

配置actuator

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

自定义健康检查

@Component
public class DatabaseHealthIndicator implements HealthIndicator {
    
    @Autowired
    private DataSource dataSource;
    
    @Override
    public Health health() {
        try (Connection connection = dataSource.getConnection()) {
            connection.createStatement().execute("SELECT 1");
            return Health.up()
                .withDetail("database", "H2")
                .withDetail("status", "connected")
                .build();
        } catch (Exception e) {
            return Health.down()
                .withDetail("error", e.getMessage())
                .build();
        }
    }
}

9.3 性能监控与调优

代码示例:自定义性能监控

@Component
public class PerformanceMonitor {
    
    private final MeterRegistry meterRegistry;
    
    public PerformanceMonitor(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    
    public void recordExecutionTime(String metricName, long duration) {
        meterRegistry.timer("execution.time", "method", metricName)
                     .record(duration, TimeUnit.MILLISECONDS);
    }
    
    public void incrementCounter(String counterName) {
        meterRegistry.counter(counterName).increment();
    }
    
    public void registerGauge(String metricName, double value) {
        meterRegistry.gauge(metricName, value);
    }
}

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

10.1 推荐学习资源

官方文档

书籍推荐

  • 《Spring实战》(Craig Walls著)- 经典入门书籍
  • 《Spring Boot编程思想》- 深入理解Spring Boot
  • 《Spring Cloud微服务实战》- 进阶微服务

在线课程

10.2 进阶学习路径

第一阶段:基础巩固(1-2个月)

  • 深入理解IoC容器和Bean生命周期
  • 掌握Spring Boot自动配置原理
  • 熟练使用Spring Data JPA
  • 学习REST API设计和开发

第二阶段:高级特性(2-3个月)

  • Spring AOP原理和应用
  • Spring Security深度集成
  • 事务管理和传播行为
  • 缓存和异步处理

第三阶段:微服务架构(3-6个月)

  • Spring Cloud基础组件
  • 服务注册与发现(Eureka/Nacos)
  • 配置中心(Config Server/Apollo)
  • 熔断器(Hystrix/Resilience4j)
  • API网关(Gateway)

第四阶段:性能优化与架构设计(持续学习)

  • JVM调优
  • 数据库优化
  • 分布式系统设计
  • 云原生部署(Kubernetes)

10.3 常见问题与解决方案

问题1:循环依赖

// 避免循环依赖的设计
@Service
public class ServiceA {
    @Lazy
    @Autowired
    private ServiceB serviceB;
}

// 或者使用事件驱动解耦
@Service
public class ServiceA {
    @Autowired
    private ApplicationEventPublisher publisher;
    
    public void doSomething() {
        // 业务逻辑
        publisher.publishEvent(new CustomEvent(this));
    }
}

问题2:N+1查询问题

// 错误的写法
@EntityGraph(attributePaths = {"orders"})
List<User> findAll();

// 或者使用JOIN FETCH
@Query("SELECT DISTINCT u FROM User u JOIN FETCH u.orders")
List<User> findAllWithOrders();

问题3:事务失效

// 确保代理创建成功
@Service
public class UserService { // 必须是public类
    @Transactional
    public void updateUser(Long id, User user) { // 必须是public方法
        // ...
    }
    
    // 避免自调用
    public void methodA() {
        // this.methodB() 会导致事务失效
        // 应该注入自己或使用AopContext.currentProxy()
    }
}

总结

Spring框架的学习是一个循序渐进的过程。作为新手,建议按照以下步骤进行:

  1. 先掌握核心概念:IoC、DI、AOP是Spring的基石,必须深入理解
  2. 从Spring Boot开始:利用其简化配置的优势快速上手
  3. 实践驱动学习:通过实际项目巩固知识,不要只停留在理论
  4. 深入理解自动配置:这是Spring Boot的魔法所在
  5. 关注最佳实践:学习分层架构、异常处理、日志规范等

记住,Spring的强大之处在于其生态系统和社区支持。遇到问题时,多查阅官方文档,善用Spring Boot的Actuator进行调试,利用好IDE的智能提示功能。

随着经验的积累,你会逐渐发现Spring不仅仅是一个框架,更是一种企业级应用开发的最佳实践集合。保持学习的热情,关注Spring生态的最新发展(如Spring Native、Spring Cloud Function等),你将在这个领域不断成长。

祝你Spring学习之旅顺利!