本章目标

通过本章学习,你将掌握: - Servlet和JSP基础开发 - Spring框架核心概念和应用 - Spring Boot快速开发 - RESTful Web服务设计与实现 - Web安全和认证机制 - 前后端分离架构设计 - Web应用性能优化

1. Servlet和JSP基础

1.1 Servlet基础

// 基础Servlet演示
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.*;
import java.util.*;

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        // 设置响应内容类型
        response.setContentType("text/html;charset=UTF-8");
        
        // 获取请求参数
        String name = request.getParameter("name");
        if (name == null || name.trim().isEmpty()) {
            name = "World";
        }
        
        // 生成响应
        PrintWriter out = response.getWriter();
        out.println("<!DOCTYPE html>");
        out.println("<html>");
        out.println("<head><title>Hello Servlet</title></head>");
        out.println("<body>");
        out.println("<h1>Hello, " + escapeHtml(name) + "!</h1>");
        out.println("<p>当前时间: " + new Date() + "</p>");
        out.println("</body>");
        out.println("</html>");
    }
    
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        doGet(request, response);
    }
    
    // HTML转义,防止XSS攻击
    private String escapeHtml(String input) {
        if (input == null) return "";
        return input.replace("&", "&amp;")
                   .replace("<", "&lt;")
                   .replace(">", "&gt;")
                   .replace("\"", "&quot;")
                   .replace("'", "&#x27;");
    }
}

2.2 Spring MVC

// 用户控制器
@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "*")
public class UserController {
    
    private final UserService userService;
    
    public UserController(UserService userService) {
        this.userService = userService;
    }
    
    @GetMapping
    public ResponseEntity<List<User>> getAllUsers() {
        List<User> users = userService.findAll();
        return ResponseEntity.ok(users);
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        try {
            User user = userService.findById(id);
            return ResponseEntity.ok(user);
        } catch (UserNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }
    
    @PostMapping
    public ResponseEntity<?> createUser(@Valid @RequestBody CreateUserRequest request) {
        try {
            User user = new User(null, request.getName(), request.getEmail());
            User savedUser = userService.save(user);
            return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
        } catch (DuplicateEmailException e) {
            return ResponseEntity.badRequest()
                    .body(new ErrorResponse("EMAIL_EXISTS", e.getMessage()));
        } catch (IllegalArgumentException e) {
            return ResponseEntity.badRequest()
                    .body(new ErrorResponse("INVALID_INPUT", e.getMessage()));
        }
    }
    
    @PutMapping("/{id}")
    public ResponseEntity<?> updateUser(@PathVariable Long id, 
                                       @Valid @RequestBody UpdateUserRequest request) {
        try {
            User existingUser = userService.findById(id);
            existingUser.setName(request.getName());
            existingUser.setEmail(request.getEmail());
            
            User updatedUser = userService.save(existingUser);
            return ResponseEntity.ok(updatedUser);
        } catch (UserNotFoundException e) {
            return ResponseEntity.notFound().build();
        } catch (DuplicateEmailException e) {
            return ResponseEntity.badRequest()
                    .body(new ErrorResponse("EMAIL_EXISTS", e.getMessage()));
        } catch (IllegalArgumentException e) {
            return ResponseEntity.badRequest()
                    .body(new ErrorResponse("INVALID_INPUT", e.getMessage()));
        }
    }
    
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        try {
            userService.deleteById(id);
            return ResponseEntity.noContent().build();
        } catch (UserNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }
    
    @GetMapping("/search")
    public ResponseEntity<User> getUserByEmail(@RequestParam String email) {
        try {
            User user = userService.findByEmail(email);
            return ResponseEntity.ok(user);
        } catch (UserNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }
}

// 请求DTO类
public class CreateUserRequest {
    
    @NotBlank(message = "姓名不能为空")
    @Size(min = 2, max = 50, message = "姓名长度必须在2-50个字符之间")
    private String name;
    
    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式无效")
    private String email;
    
    // 构造函数
    public CreateUserRequest() {}
    
    public CreateUserRequest(String name, String email) {
        this.name = name;
        this.email = email;
    }
    
    // Getters and Setters
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

public class UpdateUserRequest {
    
    @NotBlank(message = "姓名不能为空")
    @Size(min = 2, max = 50, message = "姓名长度必须在2-50个字符之间")
    private String name;
    
    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式无效")
    private String email;
    
    // 构造函数
    public UpdateUserRequest() {}
    
    public UpdateUserRequest(String name, String email) {
        this.name = name;
        this.email = email;
    }
    
    // Getters and Setters
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

// 错误响应类
public class ErrorResponse {
    private String code;
    private String message;
    private long timestamp;
    
    public ErrorResponse(String code, String message) {
        this.code = code;
        this.message = message;
        this.timestamp = System.currentTimeMillis();
    }
    
    // Getters and Setters
    public String getCode() { return code; }
    public void setCode(String code) { this.code = code; }
    
    public String getMessage() { return message; }
    public void setMessage(String message) { this.message = message; }
    
    public long getTimestamp() { return timestamp; }
    public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
}

// 全局异常处理器
@ControllerAdvice
public class GlobalExceptionHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidationException(
            MethodArgumentNotValidException ex) {
        
        StringBuilder message = new StringBuilder("验证失败: ");
        ex.getBindingResult().getFieldErrors().forEach(error -> {
            message.append(error.getField()).append(" ").append(error.getDefaultMessage()).append("; ");
        });
        
        ErrorResponse error = new ErrorResponse("VALIDATION_ERROR", message.toString());
        return ResponseEntity.badRequest().body(error);
    }
    
    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleUserNotFoundException(UserNotFoundException ex) {
        ErrorResponse error = new ErrorResponse("USER_NOT_FOUND", ex.getMessage());
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }
    
    @ExceptionHandler(DuplicateEmailException.class)
    public ResponseEntity<ErrorResponse> handleDuplicateEmailException(DuplicateEmailException ex) {
        ErrorResponse error = new ErrorResponse("EMAIL_EXISTS", ex.getMessage());
        return ResponseEntity.badRequest().body(error);
    }
    
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<ErrorResponse> handleIllegalArgumentException(IllegalArgumentException ex) {
        ErrorResponse error = new ErrorResponse("INVALID_INPUT", ex.getMessage());
        return ResponseEntity.badRequest().body(error);
    }
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
        logger.error("未处理的异常", ex);
        ErrorResponse error = new ErrorResponse("INTERNAL_ERROR", "服务器内部错误");
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
    }
}

// Web配置
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("*")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .maxAge(3600);
    }
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoggingInterceptor())
                .addPathPatterns("/api/**");
        
        registry.addInterceptor(new AuthenticationInterceptor())
                .addPathPatterns("/api/admin/**")
                .excludePathPatterns("/api/auth/**");
    }
    
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 配置JSON消息转换器
        MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
        jsonConverter.setObjectMapper(objectMapper);
        converters.add(jsonConverter);
    }
}

// 日志拦截器
public class LoggingInterceptor implements HandlerInterceptor {
    
    private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class);
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
                           Object handler) throws Exception {
        
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String queryString = request.getQueryString();
        
        logger.info("请求开始: {} {}{}", method, uri, 
                   queryString != null ? "?" + queryString : "");
        
        request.setAttribute("startTime", System.currentTimeMillis());
        return true;
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                              Object handler, Exception ex) throws Exception {
        
        Long startTime = (Long) request.getAttribute("startTime");
        long duration = System.currentTimeMillis() - startTime;
        
        String method = request.getMethod();
        String uri = request.getRequestURI();
        int status = response.getStatus();
        
        logger.info("请求完成: {} {} -> {} ({} ms)", method, uri, status, duration);
        
        if (ex != null) {
            logger.error("请求异常: " + ex.getMessage(), ex);
        }
    }
}

// 认证拦截器
public class AuthenticationInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
                           Object handler) throws Exception {
        
        String authHeader = request.getHeader("Authorization");
        
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("{\"error\": \"未授权访问\"}");
            return false;
        }
        
        String token = authHeader.substring(7);
        
        // 简单的token验证(实际项目中应使用JWT等)
        if (!isValidToken(token)) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("{\"error\": \"无效的访问令牌\"}");
            return false;
        }
        
        return true;
    }
    
    private boolean isValidToken(String token) {
        // 简单的token验证逻辑
        return "valid-token-123".equals(token);
    }
}

3. Spring Boot快速开发

3.1 Spring Boot应用

// Spring Boot主应用类
@SpringBootApplication
@EnableJpaRepositories
@EnableScheduling
public class WebApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(WebApplication.class, args);
    }
    
    @Bean
    public CommandLineRunner initData(UserRepository userRepository) {
        return args -> {
            if (userRepository.count() == 0) {
                userRepository.save(new User(null, "管理员", "admin@example.com", "ADMIN"));
                userRepository.save(new User(null, "普通用户", "user@example.com", "USER"));
                System.out.println("初始数据已创建");
            }
        };
    }
}

// 用户实体(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 = 150)
    private String email;
    
    @Column(length = 20)
    @Enumerated(EnumType.STRING)
    private UserRole role = UserRole.USER;
    
    @Column(name = "created_at")
    @CreationTimestamp
    private LocalDateTime createdAt;
    
    @Column(name = "updated_at")
    @UpdateTimestamp
    private LocalDateTime updatedAt;
    
    // 构造函数
    public User() {}
    
    public User(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }
    
    public User(Long id, String name, String email, String role) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.role = UserRole.valueOf(role);
    }
    
    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    
    public UserRole getRole() { return role; }
    public void setRole(UserRole role) { this.role = role; }
    
    public String getRoleString() { return role != null ? role.name() : null; }
    
    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; }
    
    @Override
    public String toString() {
        return "User{id=" + id + ", name='" + name + "', email='" + email + 
               "', role=" + role + ", createdAt=" + createdAt + "}";
    }
}

// 用户角色枚举
public enum UserRole {
    USER("普通用户"),
    ADMIN("管理员"),
    MODERATOR("版主");
    
    private final String displayName;
    
    UserRole(String displayName) {
        this.displayName = displayName;
    }
    
    public String getDisplayName() {
        return displayName;
    }
}

// JPA用户仓库
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    
    Optional<User> findByEmail(String email);
    
    boolean existsByEmail(String email);
    
    List<User> findByRole(UserRole role);
    
    @Query("SELECT u FROM User u WHERE u.name LIKE %:name%")
    List<User> findByNameContaining(@Param("name") String name);
    
    @Query("SELECT u FROM User u WHERE u.createdAt >= :startDate")
    List<User> findUsersCreatedAfter(@Param("startDate") LocalDateTime startDate);
    
    @Modifying
    @Query("UPDATE User u SET u.role = :role WHERE u.id = :id")
    int updateUserRole(@Param("id") Long id, @Param("role") UserRole role);
}

// 增强的用户服务
@Service
@Transactional
public class EnhancedUserService {
    
    private final UserRepository userRepository;
    private final EmailService emailService;
    private final ApplicationEventPublisher eventPublisher;
    
    public EnhancedUserService(UserRepository userRepository, 
                             EmailService emailService,
                             ApplicationEventPublisher eventPublisher) {
        this.userRepository = userRepository;
        this.emailService = emailService;
        this.eventPublisher = eventPublisher;
    }
    
    @Transactional(readOnly = true)
    public Page<User> findUsers(Pageable pageable) {
        return userRepository.findAll(pageable);
    }
    
    @Transactional(readOnly = true)
    public List<User> searchUsers(String name) {
        return userRepository.findByNameContaining(name);
    }
    
    @Transactional(readOnly = true)
    public List<User> findUsersByRole(UserRole role) {
        return userRepository.findByRole(role);
    }
    
    @Transactional(readOnly = true)
    public List<User> findRecentUsers(int days) {
        LocalDateTime startDate = LocalDateTime.now().minusDays(days);
        return userRepository.findUsersCreatedAfter(startDate);
    }
    
    public User createUser(CreateUserRequest request) {
        // 检查邮箱是否已存在
        if (userRepository.existsByEmail(request.getEmail())) {
            throw new DuplicateEmailException("邮箱已存在: " + request.getEmail());
        }
        
        User user = new User(null, request.getName(), request.getEmail());
        User savedUser = userRepository.save(user);
        
        // 发布用户创建事件
        eventPublisher.publishEvent(new UserCreatedEvent(savedUser));
        
        return savedUser;
    }
    
    public User updateUserRole(Long userId, UserRole newRole) {
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new UserNotFoundException("用户不存在: " + userId));
        
        UserRole oldRole = user.getRole();
        user.setRole(newRole);
        User updatedUser = userRepository.save(user);
        
        // 发布角色变更事件
        eventPublisher.publishEvent(new UserRoleChangedEvent(updatedUser, oldRole, newRole));
        
        return updatedUser;
    }
    
    @Cacheable(value = "users", key = "#id")
    @Transactional(readOnly = true)
    public User findById(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new UserNotFoundException("用户不存在: " + id));
    }
    
    @CacheEvict(value = "users", key = "#user.id")
    public User save(User user) {
        return userRepository.save(user);
    }
    
    @CacheEvict(value = "users", key = "#id")
    public void deleteById(Long id) {
        if (!userRepository.existsById(id)) {
            throw new UserNotFoundException("用户不存在: " + id);
        }
        userRepository.deleteById(id);
        
        // 发布用户删除事件
        eventPublisher.publishEvent(new UserDeletedEvent(id));
    }
}

// 用户事件类
public class UserCreatedEvent {
    private final User user;
    private final LocalDateTime timestamp;
    
    public UserCreatedEvent(User user) {
        this.user = user;
        this.timestamp = LocalDateTime.now();
    }
    
    public User getUser() { return user; }
    public LocalDateTime getTimestamp() { return timestamp; }
}

public class UserRoleChangedEvent {
    private final User user;
    private final UserRole oldRole;
    private final UserRole newRole;
    private final LocalDateTime timestamp;
    
    public UserRoleChangedEvent(User user, UserRole oldRole, UserRole newRole) {
        this.user = user;
        this.oldRole = oldRole;
        this.newRole = newRole;
        this.timestamp = LocalDateTime.now();
    }
    
    public User getUser() { return user; }
    public UserRole getOldRole() { return oldRole; }
    public UserRole getNewRole() { return newRole; }
    public LocalDateTime getTimestamp() { return timestamp; }
}

public class UserDeletedEvent {
    private final Long userId;
    private final LocalDateTime timestamp;
    
    public UserDeletedEvent(Long userId) {
        this.userId = userId;
        this.timestamp = LocalDateTime.now();
    }
    
    public Long getUserId() { return userId; }
    public LocalDateTime getTimestamp() { return timestamp; }
}

// 事件监听器
@Component
public class UserEventListener {
    
    private static final Logger logger = LoggerFactory.getLogger(UserEventListener.class);
    
    private final EmailService emailService;
    
    public UserEventListener(EmailService emailService) {
        this.emailService = emailService;
    }
    
    @EventListener
    @Async
    public void handleUserCreated(UserCreatedEvent event) {
        logger.info("处理用户创建事件: {}", event.getUser().getEmail());
        
        try {
            emailService.sendWelcomeEmail(event.getUser());
        } catch (Exception e) {
            logger.error("发送欢迎邮件失败: " + e.getMessage(), e);
        }
    }
    
    @EventListener
    public void handleUserRoleChanged(UserRoleChangedEvent event) {
        logger.info("用户角色变更: {} 从 {} 变更为 {}", 
                   event.getUser().getEmail(), 
                   event.getOldRole(), 
                   event.getNewRole());
        
        // 可以在这里添加角色变更的业务逻辑
        // 例如:更新权限缓存、发送通知等
    }
    
    @EventListener
    public void handleUserDeleted(UserDeletedEvent event) {
        logger.info("用户删除事件: 用户ID {}", event.getUserId());
        
        // 可以在这里添加用户删除后的清理逻辑
        // 例如:清理相关数据、发送通知等
    }
}

// 缓存配置
@Configuration
@EnableCaching
public class CacheConfig {
    
    @Bean
    public CacheManager cacheManager() {
        ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
        cacheManager.setCacheNames(Arrays.asList("users", "userStats"));
        return cacheManager;
    }
}

// 异步配置
@Configuration
@EnableAsync
public class AsyncConfig {
    
    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("async-");
        executor.initialize();
        return executor;
    }
}

3.2 配置文件

# application.properties

# 服务器配置
server.port=8080
server.servlet.context-path=/api

# 数据库配置
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

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

# H2控制台
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

# 日志配置
logging.level.com.example=DEBUG
logging.level.org.springframework.web=DEBUG
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} - %msg%n

# 应用配置
app.email.enabled=false
app.email.from=noreply@example.com

# 缓存配置
spring.cache.type=simple

# JSON配置
spring.jackson.property-naming-strategy=SNAKE_CASE
spring.jackson.default-property-inclusion=NON_NULL

# 分页配置
spring.data.web.pageable.default-page-size=20
spring.data.web.pageable.max-page-size=100
# application.yml (YAML格式)
server:
  port: 8080
  servlet:
    context-path: /api

spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
    username: sa
    password: 
  
  jpa:
    hibernate:
      ddl-auto: create-drop
    show-sql: true
    properties:
      hibernate:
        format_sql: true
    database-platform: org.hibernate.dialect.H2Dialect
  
  h2:
    console:
      enabled: true
      path: /h2-console
  
  jackson:
    property-naming-strategy: SNAKE_CASE
    default-property-inclusion: NON_NULL
  
  cache:
    type: simple
  
  data:
    web:
      pageable:
        default-page-size: 20
        max-page-size: 100

logging:
  level:
    com.example: DEBUG
    org.springframework.web: DEBUG
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n"

app:
  email:
    enabled: false
    from: noreply@example.com

---
# 开发环境配置
spring:
  config:
    activate:
      on-profile: dev
  
  datasource:
    url: jdbc:h2:file:./data/devdb
  
  jpa:
    hibernate:
      ddl-auto: update

logging:
  level:
    root: INFO
    com.example: DEBUG

---
# 生产环境配置
spring:
  config:
    activate:
      on-profile: prod
  
  datasource:
    url: jdbc:mysql://localhost:3306/webapp
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
    driver-class-name: com.mysql.cj.jdbc.Driver
  
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false

logging:
  level:
    root: WARN
    com.example: INFO
  file:
    name: logs/webapp.log

app:
  email:
    enabled: true

4. RESTful API设计

4.1 RESTful API最佳实践

// API版本控制
@RestController
@RequestMapping("/api/v1/users")
public class UserApiV1Controller {
    
    private final EnhancedUserService userService;
    
    public UserApiV1Controller(EnhancedUserService userService) {
        this.userService = userService;
    }
    
    @GetMapping
    public ResponseEntity<ApiResponse<Page<UserDto>>> getUsers(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size,
            @RequestParam(defaultValue = "id") String sort,
            @RequestParam(defaultValue = "asc") String direction,
            @RequestParam(required = false) String search,
            @RequestParam(required = false) UserRole role) {
        
        Sort.Direction sortDirection = "desc".equalsIgnoreCase(direction) ? 
                Sort.Direction.DESC : Sort.Direction.ASC;
        Pageable pageable = PageRequest.of(page, size, Sort.by(sortDirection, sort));
        
        Page<User> users;
        if (search != null && !search.trim().isEmpty()) {
            users = userService.searchUsers(search).stream()
                    .collect(Collectors.collectingAndThen(
                            Collectors.toList(),
                            list -> new PageImpl<>(list, pageable, list.size())));
        } else if (role != null) {
            users = userService.findUsersByRole(role).stream()
                    .collect(Collectors.collectingAndThen(
                            Collectors.toList(),
                            list -> new PageImpl<>(list, pageable, list.size())));
        } else {
            users = userService.findUsers(pageable);
        }
        
        Page<UserDto> userDtos = users.map(this::convertToDto);
        
        ApiResponse<Page<UserDto>> response = ApiResponse.success(userDtos);
        return ResponseEntity.ok(response);
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<ApiResponse<UserDto>> getUserById(@PathVariable Long id) {
        try {
            User user = userService.findById(id);
            UserDto userDto = convertToDto(user);
            return ResponseEntity.ok(ApiResponse.success(userDto));
        } catch (UserNotFoundException e) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND)
                    .body(ApiResponse.error("USER_NOT_FOUND", e.getMessage()));
        }
    }
    
    @PostMapping
    public ResponseEntity<ApiResponse<UserDto>> createUser(
            @Valid @RequestBody CreateUserRequest request,
            HttpServletRequest httpRequest) {
        
        try {
            User user = userService.createUser(request);
            UserDto userDto = convertToDto(user);
            
            // 构建资源URI
            URI location = ServletUriComponentsBuilder
                    .fromCurrentRequest()
                    .path("/{id}")
                    .buildAndExpand(user.getId())
                    .toUri();
            
            return ResponseEntity.created(location)
                    .body(ApiResponse.success(userDto, "用户创建成功"));
        } catch (DuplicateEmailException e) {
            return ResponseEntity.status(HttpStatus.CONFLICT)
                    .body(ApiResponse.error("EMAIL_EXISTS", e.getMessage()));
        } catch (IllegalArgumentException e) {
            return ResponseEntity.badRequest()
                    .body(ApiResponse.error("INVALID_INPUT", e.getMessage()));
        }
    }
    
    @PutMapping("/{id}")
    public ResponseEntity<ApiResponse<UserDto>> updateUser(
            @PathVariable Long id,
            @Valid @RequestBody UpdateUserRequest request) {
        
        try {
            User existingUser = userService.findById(id);
            existingUser.setName(request.getName());
            existingUser.setEmail(request.getEmail());
            
            User updatedUser = userService.save(existingUser);
            UserDto userDto = convertToDto(updatedUser);
            
            return ResponseEntity.ok(ApiResponse.success(userDto, "用户更新成功"));
        } catch (UserNotFoundException e) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND)
                    .body(ApiResponse.error("USER_NOT_FOUND", e.getMessage()));
        } catch (DuplicateEmailException e) {
            return ResponseEntity.status(HttpStatus.CONFLICT)
                    .body(ApiResponse.error("EMAIL_EXISTS", e.getMessage()));
        }
    }
    
    @PatchMapping("/{id}/role")
    public ResponseEntity<ApiResponse<UserDto>> updateUserRole(
            @PathVariable Long id,
            @RequestBody Map<String, String> request) {
        
        try {
            String roleStr = request.get("role");
            if (roleStr == null) {
                return ResponseEntity.badRequest()
                        .body(ApiResponse.error("MISSING_ROLE", "角色参数不能为空"));
            }
            
            UserRole newRole = UserRole.valueOf(roleStr.toUpperCase());
            User updatedUser = userService.updateUserRole(id, newRole);
            UserDto userDto = convertToDto(updatedUser);
            
            return ResponseEntity.ok(ApiResponse.success(userDto, "用户角色更新成功"));
        } catch (UserNotFoundException e) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND)
                    .body(ApiResponse.error("USER_NOT_FOUND", e.getMessage()));
        } catch (IllegalArgumentException e) {
            return ResponseEntity.badRequest()
                    .body(ApiResponse.error("INVALID_ROLE", "无效的角色: " + request.get("role")));
        }
    }
    
    @DeleteMapping("/{id}")
    public ResponseEntity<ApiResponse<Void>> deleteUser(@PathVariable Long id) {
        try {
            userService.deleteById(id);
            return ResponseEntity.ok(ApiResponse.success(null, "用户删除成功"));
        } catch (UserNotFoundException e) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND)
                    .body(ApiResponse.error("USER_NOT_FOUND", e.getMessage()));
        }
    }
    
    @GetMapping("/stats")
    public ResponseEntity<ApiResponse<UserStatsDto>> getUserStats() {
        UserStatsDto stats = new UserStatsDto();
        stats.setTotalUsers(userService.findUsers(Pageable.unpaged()).getTotalElements());
        stats.setAdminUsers(userService.findUsersByRole(UserRole.ADMIN).size());
        stats.setRegularUsers(userService.findUsersByRole(UserRole.USER).size());
        stats.setRecentUsers(userService.findRecentUsers(7).size());
        
        return ResponseEntity.ok(ApiResponse.success(stats));
    }
    
    private UserDto convertToDto(User user) {
        UserDto dto = new UserDto();
        dto.setId(user.getId());
        dto.setName(user.getName());
        dto.setEmail(user.getEmail());
        dto.setRole(user.getRole());
        dto.setRoleDisplayName(user.getRole().getDisplayName());
        dto.setCreatedAt(user.getCreatedAt());
        dto.setUpdatedAt(user.getUpdatedAt());
        return dto;
    }
}

// 统一API响应格式
public class ApiResponse<T> {
    private boolean success;
    private String message;
    private T data;
    private String errorCode;
    private long timestamp;
    
    public ApiResponse() {
        this.timestamp = System.currentTimeMillis();
    }
    
    public static <T> ApiResponse<T> success(T data) {
        return success(data, "操作成功");
    }
    
    public static <T> ApiResponse<T> success(T data, String message) {
        ApiResponse<T> response = new ApiResponse<>();
        response.success = true;
        response.message = message;
        response.data = data;
        return response;
    }
    
    public static <T> ApiResponse<T> error(String errorCode, String message) {
        ApiResponse<T> response = new ApiResponse<>();
        response.success = false;
        response.errorCode = errorCode;
        response.message = message;
        return response;
    }
    
    // Getters and Setters
    public boolean isSuccess() { return success; }
    public void setSuccess(boolean success) { this.success = success; }
    
    public String getMessage() { return message; }
    public void setMessage(String message) { this.message = message; }
    
    public T getData() { return data; }
    public void setData(T data) { this.data = data; }
    
    public String getErrorCode() { return errorCode; }
    public void setErrorCode(String errorCode) { this.errorCode = errorCode; }
    
    public long getTimestamp() { return timestamp; }
    public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
}

// 用户DTO
public class UserDto {
    private Long id;
    private String name;
    private String email;
    private UserRole role;
    private String roleDisplayName;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    
    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    
    public UserRole getRole() { return role; }
    public void setRole(UserRole role) { this.role = role; }
    
    public String getRoleDisplayName() { return roleDisplayName; }
    public void setRoleDisplayName(String roleDisplayName) { this.roleDisplayName = roleDisplayName; }
    
    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; }
}

// 用户统计DTO
public class UserStatsDto {
    private long totalUsers;
    private long adminUsers;
    private long regularUsers;
    private long recentUsers;
    
    // Getters and Setters
    public long getTotalUsers() { return totalUsers; }
    public void setTotalUsers(long totalUsers) { this.totalUsers = totalUsers; }
    
    public long getAdminUsers() { return adminUsers; }
    public void setAdminUsers(long adminUsers) { this.adminUsers = adminUsers; }
    
    public long getRegularUsers() { return regularUsers; }
    public void setRegularUsers(long regularUsers) { this.regularUsers = regularUsers; }
    
    public long getRecentUsers() { return recentUsers; }
    public void setRecentUsers(long recentUsers) { this.recentUsers = recentUsers; }
}

4.2 API文档和测试

// Swagger配置
@Configuration
@EnableOpenApi
public class SwaggerConfig {
    
    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
                .info(new Info()
                        .title("用户管理API")
                        .version("1.0")
                        .description("用户管理系统的RESTful API文档")
                        .contact(new Contact()
                                .name("开发团队")
                                .email("dev@example.com"))
                        .license(new License()
                                .name("MIT License")
                                .url("https://opensource.org/licenses/MIT")))
                .servers(Arrays.asList(
                        new Server().url("http://localhost:8080").description("开发环境"),
                        new Server().url("https://api.example.com").description("生产环境")
                ))
                .components(new Components()
                        .addSecuritySchemes("bearerAuth",
                                new SecurityScheme()
                                        .type(SecurityScheme.Type.HTTP)
                                        .scheme("bearer")
                                        .bearerFormat("JWT")));
    }
}

// API测试控制器
@RestController
@RequestMapping("/api/test")
public class ApiTestController {
    
    @GetMapping("/health")
    public ResponseEntity<Map<String, Object>> healthCheck() {
        Map<String, Object> health = new HashMap<>();
        health.put("status", "UP");
        health.put("timestamp", LocalDateTime.now());
        health.put("version", "1.0.0");
        health.put("environment", System.getProperty("spring.profiles.active", "default"));
        
        return ResponseEntity.ok(health);
    }
    
    @GetMapping("/info")
    public ResponseEntity<Map<String, Object>> appInfo() {
        Map<String, Object> info = new HashMap<>();
        info.put("name", "用户管理系统");
        info.put("description", "基于Spring Boot的用户管理RESTful API");
        info.put("version", "1.0.0");
        info.put("java.version", System.getProperty("java.version"));
        info.put("spring.version", SpringVersion.getVersion());
        
        return ResponseEntity.ok(info);
    }
    
    @PostMapping("/echo")
    public ResponseEntity<Map<String, Object>> echo(@RequestBody Map<String, Object> request) {
        Map<String, Object> response = new HashMap<>();
        response.put("received", request);
        response.put("timestamp", LocalDateTime.now());
        response.put("method", "POST");
        
        return ResponseEntity.ok(response);
    }
}

5. 前端集成

5.1 JavaScript客户端

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>用户管理系统</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            margin: 0;
            padding: 20px;
            background-color: #f5f5f5;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        .header {
            text-align: center;
            margin-bottom: 30px;
            color: #333;
        }
        .form-group {
            margin-bottom: 15px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
            color: #555;
        }
        input, select {
            width: 100%;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 14px;
        }
        button {
            background-color: #007bff;
            color: white;
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            margin-right: 10px;
            font-size: 14px;
        }
        button:hover {
            background-color: #0056b3;
        }
        .btn-danger {
            background-color: #dc3545;
        }
        .btn-danger:hover {
            background-color: #c82333;
        }
        .user-list {
            margin-top: 30px;
        }
        .user-item {
            background: #f8f9fa;
            padding: 15px;
            margin-bottom: 10px;
            border-radius: 4px;
            border-left: 4px solid #007bff;
        }
        .user-actions {
            margin-top: 10px;
        }
        .message {
            padding: 10px;
            margin-bottom: 15px;
            border-radius: 4px;
        }
        .message.success {
            background-color: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }
        .message.error {
            background-color: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }
        .stats {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 15px;
            margin-bottom: 30px;
        }
        .stat-card {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 20px;
            border-radius: 8px;
            text-align: center;
        }
        .stat-number {
            font-size: 2em;
            font-weight: bold;
            margin-bottom: 5px;
        }
        .stat-label {
            font-size: 0.9em;
            opacity: 0.9;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>用户管理系统</h1>
            <p>基于Spring Boot的RESTful API演示</p>
        </div>
        
        <div id="message"></div>
        
        <div class="stats" id="stats"></div>
        
        <form id="userForm">
            <div class="form-group">
                <label for="name">姓名:</label>
                <input type="text" id="name" name="name" required>
            </div>
            <div class="form-group">
                <label for="email">邮箱:</label>
                <input type="email" id="email" name="email" required>
            </div>
            <div class="form-group">
                <label for="role">角色:</label>
                <select id="role" name="role">
                    <option value="USER">普通用户</option>
                    <option value="ADMIN">管理员</option>
                    <option value="MODERATOR">版主</option>
                </select>
            </div>
            <button type="submit">创建用户</button>
            <button type="button" onclick="clearForm()">清空表单</button>
            <button type="button" onclick="loadUsers()">刷新列表</button>
        </form>
        
        <div class="user-list">
            <h3>用户列表</h3>
            <div id="userList"></div>
        </div>
    </div>

    <script>
        const API_BASE = '/api/v1';
        let editingUserId = null;

        // 页面加载时初始化
        document.addEventListener('DOMContentLoaded', function() {
            loadStats();
            loadUsers();
            
            document.getElementById('userForm').addEventListener('submit', handleSubmit);
        });

        // 加载统计信息
        async function loadStats() {
            try {
                const response = await fetch(`${API_BASE}/users/stats`);
                const result = await response.json();
                
                if (result.success) {
                    displayStats(result.data);
                }
            } catch (error) {
                console.error('加载统计信息失败:', error);
            }
        }

        // 显示统计信息
        function displayStats(stats) {
            const statsContainer = document.getElementById('stats');
            statsContainer.innerHTML = `
                <div class="stat-card">
                    <div class="stat-number">${stats.totalUsers}</div>
                    <div class="stat-label">总用户数</div>
                </div>
                <div class="stat-card">
                    <div class="stat-number">${stats.adminUsers}</div>
                    <div class="stat-label">管理员</div>
                </div>
                <div class="stat-card">
                    <div class="stat-number">${stats.regularUsers}</div>
                    <div class="stat-label">普通用户</div>
                </div>
                <div class="stat-card">
                    <div class="stat-number">${stats.recentUsers}</div>
                    <div class="stat-label">近7天新增</div>
                </div>
            `;
        }

        // 处理表单提交
        async function handleSubmit(event) {
            event.preventDefault();
            
            const formData = new FormData(event.target);
            const userData = {
                name: formData.get('name'),
                email: formData.get('email')
            };
            
            try {
                let response;
                if (editingUserId) {
                    // 更新用户
                    response = await fetch(`${API_BASE}/users/${editingUserId}`, {
                        method: 'PUT',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify(userData)
                    });
                } else {
                    // 创建用户
                    response = await fetch(`${API_BASE}/users`, {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify(userData)
                    });
                }
                
                const result = await response.json();
                
                if (result.success) {
                    showMessage(result.message, 'success');
                    clearForm();
                    loadUsers();
                    loadStats();
                } else {
                    showMessage(result.message, 'error');
                }
            } catch (error) {
                showMessage('操作失败: ' + error.message, 'error');
            }
        }

        // 加载用户列表
        async function loadUsers() {
            try {
                const response = await fetch(`${API_BASE}/users?size=50`);
                const result = await response.json();
                
                if (result.success) {
                    displayUsers(result.data.content);
                } else {
                    showMessage('加载用户列表失败', 'error');
                }
            } catch (error) {
                showMessage('加载用户列表失败: ' + error.message, 'error');
            }
        }

        // 显示用户列表
        function displayUsers(users) {
            const userList = document.getElementById('userList');
            
            if (users.length === 0) {
                userList.innerHTML = '<p>暂无用户数据</p>';
                return;
            }
            
            userList.innerHTML = users.map(user => `
                <div class="user-item">
                    <strong>${user.name}</strong> (${user.email})
                    <br>
                    <small>角色: ${user.roleDisplayName} | 创建时间: ${formatDate(user.createdAt)}</small>
                    <div class="user-actions">
                        <button onclick="editUser(${user.id})">编辑</button>
                        <button onclick="updateUserRole(${user.id})" class="btn-warning">更改角色</button>
                        <button onclick="deleteUser(${user.id})" class="btn-danger">删除</button>
                    </div>
                </div>
            `).join('');
        }

        // 编辑用户
        async function editUser(userId) {
            try {
                const response = await fetch(`${API_BASE}/users/${userId}`);
                const result = await response.json();
                
                if (result.success) {
                    const user = result.data;
                    document.getElementById('name').value = user.name;
                    document.getElementById('email').value = user.email;
                    document.getElementById('role').value = user.role;
                    editingUserId = userId;
                    
                    document.querySelector('button[type="submit"]').textContent = '更新用户';
                    showMessage('正在编辑用户: ' + user.name, 'success');
                }
            } catch (error) {
                showMessage('加载用户信息失败: ' + error.message, 'error');
            }
        }

        // 更新用户角色
        async function updateUserRole(userId) {
            const newRole = prompt('请输入新角色 (USER/ADMIN/MODERATOR):');
            if (!newRole) return;
            
            try {
                const response = await fetch(`${API_BASE}/users/${userId}/role`, {
                    method: 'PATCH',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ role: newRole.toUpperCase() })
                });
                
                const result = await response.json();
                
                if (result.success) {
                    showMessage(result.message, 'success');
                    loadUsers();
                    loadStats();
                } else {
                    showMessage(result.message, 'error');
                }
            } catch (error) {
                showMessage('更新角色失败: ' + error.message, 'error');
            }
        }

        // 删除用户
        async function deleteUser(userId) {
            if (!confirm('确定要删除这个用户吗?')) return;
            
            try {
                const response = await fetch(`${API_BASE}/users/${userId}`, {
                    method: 'DELETE'
                });
                
                const result = await response.json();
                
                if (result.success) {
                    showMessage(result.message, 'success');
                    loadUsers();
                    loadStats();
                } else {
                    showMessage(result.message, 'error');
                }
            } catch (error) {
                showMessage('删除用户失败: ' + error.message, 'error');
            }
        }

        // 清空表单
        function clearForm() {
            document.getElementById('userForm').reset();
            editingUserId = null;
            document.querySelector('button[type="submit"]').textContent = '创建用户';
        }

        // 显示消息
        function showMessage(message, type) {
            const messageDiv = document.getElementById('message');
            messageDiv.innerHTML = `<div class="message ${type}">${message}</div>`;
            
            setTimeout(() => {
                messageDiv.innerHTML = '';
            }, 5000);
        }

        // 格式化日期
        function formatDate(dateString) {
            const date = new Date(dateString);
            return date.toLocaleString('zh-CN');
        }
    </script>
</body>
</html>

6. Web安全

6.1 Spring Security集成

// Security配置
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
    
    private final UserDetailsService userDetailsService;
    private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    private final JwtRequestFilter jwtRequestFilter;
    
    public SecurityConfig(UserDetailsService userDetailsService,
                         JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint,
                         JwtRequestFilter jwtRequestFilter) {
        this.userDetailsService = userDetailsService;
        this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;
        this.jwtRequestFilter = jwtRequestFilter;
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    public AuthenticationManager authenticationManager(
            AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(authz -> authz
                .requestMatchers("/api/auth/**").permitAll()
                .requestMatchers("/api/test/**").permitAll()
                .requestMatchers("/h2-console/**").permitAll()
                .requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
                .requestMatchers(HttpMethod.GET, "/api/v1/users").hasAnyRole("USER", "ADMIN")
                .requestMatchers(HttpMethod.POST, "/api/v1/users").hasRole("ADMIN")
                .requestMatchers(HttpMethod.PUT, "/api/v1/users/**").hasRole("ADMIN")
                .requestMatchers(HttpMethod.DELETE, "/api/v1/users/**").hasRole("ADMIN")
                .requestMatchers("/api/v1/users/stats").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .exceptionHandling(ex -> ex.authenticationEntryPoint(jwtAuthenticationEntryPoint))
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .headers(headers -> headers.frameOptions().disable()); // 为H2控制台
        
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
        
        return http.build();
    }
}

// JWT工具类
@Component
public class JwtTokenUtil {
    
    private static final String SECRET = "mySecretKey";
    private static final int JWT_TOKEN_VALIDITY = 5 * 60 * 60; // 5小时
    
    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token, Claims::getSubject);
    }
    
    public Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }
    
    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }
    
    private Claims getAllClaimsFromToken(String token) {
        return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
    }
    
    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }
    
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, userDetails.getUsername());
    }
    
    private String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000))
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();
    }
    
    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
}

// JWT认证入口点
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                        AuthenticationException authException) throws IOException {
        
        response.setContentType("application/json");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        
        String jsonResponse = "{\"error\": \"未授权访问\", \"message\": \"" + 
                             authException.getMessage() + "\"}";
        response.getWriter().write(jsonResponse);
    }
}

// JWT请求过滤器
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
    
    private final UserDetailsService userDetailsService;
    private final JwtTokenUtil jwtTokenUtil;
    
    public JwtRequestFilter(UserDetailsService userDetailsService, JwtTokenUtil jwtTokenUtil) {
        this.userDetailsService = userDetailsService;
        this.jwtTokenUtil = jwtTokenUtil;
    }
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                  FilterChain chain) throws ServletException, IOException {
        
        final String requestTokenHeader = request.getHeader("Authorization");
        
        String username = null;
        String jwtToken = null;
        
        if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
            jwtToken = requestTokenHeader.substring(7);
            try {
                username = jwtTokenUtil.getUsernameFromToken(jwtToken);
            } catch (IllegalArgumentException e) {
                logger.error("无法获取JWT Token", e);
            } catch (ExpiredJwtException e) {
                logger.error("JWT Token已过期", e);
            }
        }
        
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
            
            if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
                UsernamePasswordAuthenticationToken authToken = 
                    new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }
        
        chain.doFilter(request, response);
    }
}

// 认证控制器
@RestController
@RequestMapping("/api/auth")
public class AuthController {
    
    private final AuthenticationManager authenticationManager;
    private final JwtTokenUtil jwtTokenUtil;
    private final UserDetailsService userDetailsService;
    
    public AuthController(AuthenticationManager authenticationManager,
                         JwtTokenUtil jwtTokenUtil,
                         UserDetailsService userDetailsService) {
        this.authenticationManager = authenticationManager;
        this.jwtTokenUtil = jwtTokenUtil;
        this.userDetailsService = userDetailsService;
    }
    
    @PostMapping("/login")
    public ResponseEntity<ApiResponse<JwtResponse>> login(@RequestBody JwtRequest authRequest) {
        try {
            authenticate(authRequest.getUsername(), authRequest.getPassword());
            
            final UserDetails userDetails = userDetailsService
                    .loadUserByUsername(authRequest.getUsername());
            final String token = jwtTokenUtil.generateToken(userDetails);
            
            JwtResponse response = new JwtResponse(token, userDetails.getUsername(), 
                    userDetails.getAuthorities().stream()
                            .map(GrantedAuthority::getAuthority)
                            .collect(Collectors.toList()));
            
            return ResponseEntity.ok(ApiResponse.success(response, "登录成功"));
        } catch (BadCredentialsException e) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                    .body(ApiResponse.error("INVALID_CREDENTIALS", "用户名或密码错误"));
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(ApiResponse.error("LOGIN_ERROR", "登录失败: " + e.getMessage()));
        }
    }
    
    private void authenticate(String username, String password) throws Exception {
        try {
            authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(username, password));
        } catch (DisabledException e) {
            throw new Exception("用户已被禁用", e);
        } catch (BadCredentialsException e) {
            throw new Exception("用户名或密码错误", e);
        }
    }
}

// JWT请求和响应类
public class JwtRequest {
    private String username;
    private String password;
    
    public JwtRequest() {}
    
    public JwtRequest(String username, String password) {
        this.username = username;
        this.password = password;
    }
    
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
}

public class JwtResponse {
    private String token;
    private String username;
    private List<String> roles;
    
    public JwtResponse(String token, String username, List<String> roles) {
        this.token = token;
        this.username = username;
        this.roles = roles;
    }
    
    public String getToken() { return token; }
    public void setToken(String token) { this.token = token; }
    
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    
    public List<String> getRoles() { return roles; }
    public void setRoles(List<String> roles) { this.roles = roles; }
}

6.2 安全最佳实践

// 输入验证和清理
@Component
public class InputValidator {
    
    private static final Pattern EMAIL_PATTERN = 
            Pattern.compile("^[A-Za-z0-9+_.-]+@([A-Za-z0-9.-]+\\.[A-Za-z]{2,})$");
    
    private static final Pattern NAME_PATTERN = 
            Pattern.compile("^[\\p{L}\\p{M}\\s'-]{2,50}$");
    
    public boolean isValidEmail(String email) {
        return email != null && EMAIL_PATTERN.matcher(email).matches();
    }
    
    public boolean isValidName(String name) {
        return name != null && NAME_PATTERN.matcher(name).matches();
    }
    
    public String sanitizeInput(String input) {
        if (input == null) return null;
        
        // 移除潜在的恶意字符
        return input.replaceAll("[<>\"'&]", "")
                   .trim();
    }
    
    public String escapeHtml(String input) {
        if (input == null) return null;
        
        return input.replace("&", "&amp;")
                   .replace("<", "&lt;")
                   .replace(">", "&gt;")
                   .replace("\"", "&quot;")
                   .replace("'", "&#x27;");
    }
}

// CSRF保护
@Configuration
public class CsrfConfig {
    
    @Bean
    public CsrfTokenRepository csrfTokenRepository() {
        HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
        repository.setHeaderName("X-XSRF-TOKEN");
        return repository;
    }
}

// 速率限制
@Component
public class RateLimitingFilter implements Filter {
    
    private final Map<String, List<Long>> requestCounts = new ConcurrentHashMap<>();
    private final int maxRequests = 100; // 每分钟最大请求数
    private final long timeWindow = 60000; // 1分钟
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        String clientIp = getClientIp(httpRequest);
        
        if (isRateLimited(clientIp)) {
            httpResponse.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
            httpResponse.setContentType("application/json");
            httpResponse.getWriter().write(
                "{\"error\": \"请求过于频繁\", \"message\": \"请稍后再试\"}");
            return;
        }
        
        chain.doFilter(request, response);
    }
    
    private boolean isRateLimited(String clientIp) {
        long currentTime = System.currentTimeMillis();
        
        requestCounts.compute(clientIp, (key, timestamps) -> {
            if (timestamps == null) {
                timestamps = new ArrayList<>();
            }
            
            // 移除过期的时间戳
            timestamps.removeIf(timestamp -> currentTime - timestamp > timeWindow);
            
            // 添加当前时间戳
            timestamps.add(currentTime);
            
            return timestamps;
        });
        
        return requestCounts.get(clientIp).size() > maxRequests;
    }
    
    private String getClientIp(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIp = request.getHeader("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty()) {
            return xRealIp;
        }
        
        return request.getRemoteAddr();
    }
}

// 安全头配置
@Configuration
public class SecurityHeadersConfig {
    
    @Bean
    public FilterRegistrationBean<SecurityHeadersFilter> securityHeadersFilter() {
        FilterRegistrationBean<SecurityHeadersFilter> registrationBean = 
                new FilterRegistrationBean<>();
        registrationBean.setFilter(new SecurityHeadersFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setOrder(1);
        return registrationBean;
    }
    
    public static class SecurityHeadersFilter implements Filter {
        
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, 
                            FilterChain chain) throws IOException, ServletException {
            
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            
            // 防止点击劫持
            httpResponse.setHeader("X-Frame-Options", "DENY");
            
            // 防止MIME类型嗅探
            httpResponse.setHeader("X-Content-Type-Options", "nosniff");
            
            // XSS保护
            httpResponse.setHeader("X-XSS-Protection", "1; mode=block");
            
            // 强制HTTPS
            httpResponse.setHeader("Strict-Transport-Security", 
                    "max-age=31536000; includeSubDomains");
            
            // 内容安全策略
            httpResponse.setHeader("Content-Security-Policy", 
                    "default-src 'self'; script-src 'self' 'unsafe-inline'; " +
                    "style-src 'self' 'unsafe-inline'; img-src 'self' data:");
            
            // 引用者策略
            httpResponse.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
            
            chain.doFilter(request, response);
        }
    }
}

7. Web开发最佳实践

7.1 性能优化

// 缓存配置
@Configuration
@EnableCaching
public class CacheConfig {
    
    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(Caffeine.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(Duration.ofMinutes(10))
                .recordStats());
        return cacheManager;
    }
    
    @Bean
    public CacheMetricsRegistrar cacheMetricsRegistrar(MeterRegistry meterRegistry) {
        return new CacheMetricsRegistrar(meterRegistry);
    }
}

// 异步处理
@Service
public class AsyncUserService {
    
    private final UserRepository userRepository;
    private final EmailService emailService;
    
    public AsyncUserService(UserRepository userRepository, EmailService emailService) {
        this.userRepository = userRepository;
        this.emailService = emailService;
    }
    
    @Async
    public CompletableFuture<Void> sendWelcomeEmailAsync(User user) {
        try {
            emailService.sendWelcomeEmail(user.getEmail(), user.getName());
            return CompletableFuture.completedFuture(null);
        } catch (Exception e) {
            return CompletableFuture.failedFuture(e);
        }
    }
    
    @Async
    public CompletableFuture<List<User>> findUsersAsync(Pageable pageable) {
        return CompletableFuture.supplyAsync(() -> 
                userRepository.findAll(pageable).getContent());
    }
    
    @Async
    public CompletableFuture<Void> updateUserStatisticsAsync() {
        return CompletableFuture.runAsync(() -> {
            // 更新用户统计信息
            long totalUsers = userRepository.count();
            // 缓存统计信息
            // ...
        });
    }
}

// 数据库优化
@Repository
public interface OptimizedUserRepository extends JpaRepository<User, Long> {
    
    @Query("SELECT u FROM User u WHERE u.email = :email")
    @QueryHints(@QueryHint(name = "org.hibernate.cacheable", value = "true"))
    Optional<User> findByEmailCached(@Param("email") String email);
    
    @Query("SELECT u FROM User u WHERE u.role = :role")
    @QueryHints(@QueryHint(name = "org.hibernate.fetchSize", value = "50"))
    List<User> findByRoleOptimized(@Param("role") UserRole role);
    
    @Modifying
    @Query("UPDATE User u SET u.lastLoginAt = :loginTime WHERE u.id = :userId")
    void updateLastLoginTime(@Param("userId") Long userId, 
                           @Param("loginTime") LocalDateTime loginTime);
    
    @Query(value = "SELECT * FROM users WHERE created_at >= :since ORDER BY created_at DESC", 
           nativeQuery = true)
    List<User> findRecentUsersNative(@Param("since") LocalDateTime since);
}

// 响应压缩
@Configuration
public class CompressionConfig {
    
    @Bean
    public FilterRegistrationBean<CompressionFilter> compressionFilter() {
        FilterRegistrationBean<CompressionFilter> registrationBean = 
                new FilterRegistrationBean<>();
        registrationBean.setFilter(new CompressionFilter());
        registrationBean.addUrlPatterns("/api/*");
        return registrationBean;
    }
    
    public static class CompressionFilter implements Filter {
        
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, 
                            FilterChain chain) throws IOException, ServletException {
            
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            
            String acceptEncoding = httpRequest.getHeader("Accept-Encoding");
            
            if (acceptEncoding != null && acceptEncoding.contains("gzip")) {
                httpResponse.setHeader("Content-Encoding", "gzip");
                // 实现GZIP压缩逻辑
            }
            
            chain.doFilter(request, response);
        }
    }
}

7.2 监控和日志

// 应用监控
@Component
public class ApplicationMetrics {
    
    private final MeterRegistry meterRegistry;
    private final Counter userCreationCounter;
    private final Timer userQueryTimer;
    private final Gauge activeUsersGauge;
    
    public ApplicationMetrics(MeterRegistry meterRegistry, UserService userService) {
        this.meterRegistry = meterRegistry;
        this.userCreationCounter = Counter.builder("users.created")
                .description("用户创建计数")
                .register(meterRegistry);
        this.userQueryTimer = Timer.builder("users.query.time")
                .description("用户查询耗时")
                .register(meterRegistry);
        this.activeUsersGauge = Gauge.builder("users.active")
                .description("活跃用户数")
                .register(meterRegistry, userService, UserService::getActiveUserCount);
    }
    
    public void incrementUserCreation() {
        userCreationCounter.increment();
    }
    
    public Timer.Sample startUserQueryTimer() {
        return Timer.start(meterRegistry);
    }
    
    public void recordUserQueryTime(Timer.Sample sample) {
        sample.stop(userQueryTimer);
    }
}

// 自定义日志
@Component
public class AuditLogger {
    
    private static final Logger auditLog = LoggerFactory.getLogger("AUDIT");
    
    public void logUserCreation(String username, String createdBy) {
        auditLog.info("用户创建 - 用户名: {}, 创建者: {}, 时间: {}", 
                username, createdBy, LocalDateTime.now());
    }
    
    public void logUserDeletion(String username, String deletedBy) {
        auditLog.warn("用户删除 - 用户名: {}, 删除者: {}, 时间: {}", 
                username, deletedBy, LocalDateTime.now());
    }
    
    public void logLoginAttempt(String username, String ip, boolean success) {
        if (success) {
            auditLog.info("登录成功 - 用户名: {}, IP: {}, 时间: {}", 
                    username, ip, LocalDateTime.now());
        } else {
            auditLog.warn("登录失败 - 用户名: {}, IP: {}, 时间: {}", 
                    username, ip, LocalDateTime.now());
        }
    }
    
    public void logSecurityEvent(String event, String details) {
        auditLog.error("安全事件 - 事件: {}, 详情: {}, 时间: {}", 
                event, details, LocalDateTime.now());
    }
}

// 健康检查
@Component
public class CustomHealthIndicator implements HealthIndicator {
    
    private final UserRepository userRepository;
    private final EmailService emailService;
    
    public CustomHealthIndicator(UserRepository userRepository, EmailService emailService) {
        this.userRepository = userRepository;
        this.emailService = emailService;
    }
    
    @Override
    public Health health() {
        try {
            // 检查数据库连接
            long userCount = userRepository.count();
            
            // 检查邮件服务
            boolean emailServiceUp = emailService.isServiceAvailable();
            
            if (emailServiceUp) {
                return Health.up()
                        .withDetail("database", "可用")
                        .withDetail("userCount", userCount)
                        .withDetail("emailService", "可用")
                        .build();
            } else {
                return Health.down()
                        .withDetail("database", "可用")
                        .withDetail("userCount", userCount)
                        .withDetail("emailService", "不可用")
                        .build();
            }
        } catch (Exception e) {
            return Health.down()
                    .withDetail("error", e.getMessage())
                    .build();
        }
    }
}

7.3 错误处理和异常管理

// 全局异常处理增强
@ControllerAdvice
public class EnhancedGlobalExceptionHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(EnhancedGlobalExceptionHandler.class);
    private final AuditLogger auditLogger;
    
    public EnhancedGlobalExceptionHandler(AuditLogger auditLogger) {
        this.auditLogger = auditLogger;
    }
    
    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ApiResponse<Void>> handleUserNotFound(UserNotFoundException e) {
        logger.warn("用户未找到: {}", e.getMessage());
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
                .body(ApiResponse.error("USER_NOT_FOUND", e.getMessage()));
    }
    
    @ExceptionHandler(DuplicateEmailException.class)
    public ResponseEntity<ApiResponse<Void>> handleDuplicateEmail(DuplicateEmailException e) {
        logger.warn("邮箱重复: {}", e.getMessage());
        return ResponseEntity.status(HttpStatus.CONFLICT)
                .body(ApiResponse.error("EMAIL_EXISTS", e.getMessage()));
    }
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ApiResponse<Map<String, String>>> handleValidationErrors(
            MethodArgumentNotValidException e) {
        
        Map<String, String> errors = new HashMap<>();
        e.getBindingResult().getFieldErrors().forEach(error -> 
                errors.put(error.getField(), error.getDefaultMessage()));
        
        logger.warn("输入验证失败: {}", errors);
        return ResponseEntity.badRequest()
                .body(ApiResponse.error("VALIDATION_ERROR", "输入验证失败"));
    }
    
    @ExceptionHandler(AccessDeniedException.class)
    public ResponseEntity<ApiResponse<Void>> handleAccessDenied(
            AccessDeniedException e, HttpServletRequest request) {
        
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        auditLogger.logSecurityEvent("访问被拒绝", 
                String.format("用户: %s, 路径: %s", username, request.getRequestURI()));
        
        return ResponseEntity.status(HttpStatus.FORBIDDEN)
                .body(ApiResponse.error("ACCESS_DENIED", "访问被拒绝"));
    }
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ApiResponse<Void>> handleGenericException(
            Exception e, HttpServletRequest request) {
        
        String errorId = UUID.randomUUID().toString();
        logger.error("未处理的异常 [{}]: {}", errorId, e.getMessage(), e);
        
        // 在生产环境中不暴露详细错误信息
        String message = "服务器内部错误,错误ID: " + errorId;
        
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(ApiResponse.error("INTERNAL_ERROR", message));
    }
}

// 重试机制
@Service
public class RetryableUserService {
    
    private final UserRepository userRepository;
    private final EmailService emailService;
    
    public RetryableUserService(UserRepository userRepository, EmailService emailService) {
        this.userRepository = userRepository;
        this.emailService = emailService;
    }
    
    @Retryable(value = {DataAccessException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000))
    public User saveUserWithRetry(User user) {
        return userRepository.save(user);
    }
    
    @Retryable(value = {EmailServiceException.class}, maxAttempts = 3, backoff = @Backoff(delay = 2000))
    public void sendEmailWithRetry(String to, String subject, String content) {
        emailService.sendEmail(to, subject, content);
    }
    
    @Recover
    public User recoverSaveUser(DataAccessException e, User user) {
        // 保存失败后的恢复逻辑
        throw new ServiceUnavailableException("用户保存服务暂时不可用");
    }
    
    @Recover
    public void recoverSendEmail(EmailServiceException e, String to, String subject, String content) {
        // 邮件发送失败后的恢复逻辑
        // 可以将邮件放入队列稍后重试
    }
}

8. 本章小结

8.1 Web开发技术对比

技术 优点 缺点 适用场景
Servlet 性能高、控制精细 开发复杂、代码冗余 底层控制、性能要求高
JSP 易于上手、模板简单 维护困难、性能一般 简单页面、快速原型
Spring MVC 功能完整、生态丰富 配置复杂、学习成本高 企业级应用、复杂业务
Spring Boot 开箱即用、约定优于配置 黑盒化、定制困难 快速开发、微服务

8.2 性能优化要点

  1. 数据库优化

    • 使用连接池
    • 查询优化和索引
    • 缓存策略
    • 分页查询
  2. 应用层优化

    • 异步处理
    • 缓存机制
    • 响应压缩
    • 静态资源优化
  3. 网络优化

    • CDN使用
    • HTTP/2支持
    • 资源合并
    • 懒加载

8.3 安全考虑

  1. 认证和授权

    • JWT令牌
    • 角色权限控制
    • 会话管理
  2. 输入验证

    • 参数校验
    • SQL注入防护
    • XSS防护
  3. 传输安全

    • HTTPS加密
    • 安全头设置
    • CSRF保护

8.4 开发建议

  1. 架构设计

    • 分层架构
    • 依赖注入
    • 接口抽象
    • 配置外部化
  2. 代码质量

    • 单元测试
    • 代码审查
    • 文档完善
    • 异常处理
  3. 运维监控

    • 日志记录
    • 性能监控
    • 健康检查
    • 错误追踪

下一章预告

下一章我们将学习微服务架构,包括: - Spring Cloud基础 - 服务注册与发现 - 配置中心 - 服务网关 - 分布式追踪 - 容器化部署

练习题

  1. 基础练习

    • 创建一个简单的博客系统,包含文章管理功能
    • 实现用户注册、登录和权限控制
    • 添加文章的增删改查功能
  2. 进阶练习

    • 为博客系统添加RESTful API
    • 实现JWT认证和授权
    • 添加文章搜索和分页功能
    • 集成Swagger API文档
  3. 高级练习

    • 实现文章的缓存机制
    • 添加异步邮件通知功能
    • 实现文件上传和图片处理
    • 添加应用监控和日志记录
  4. 项目练习

    • 开发一个完整的电商系统后端
    • 包含用户管理、商品管理、订单管理
    • 实现购物车、支付集成、库存管理
    • 添加完整的安全机制和性能优化

// 用户管理Servlet @WebServlet(“/users/*”) public class UserServlet extends HttpServlet {

private List<User> users = new ArrayList<>();

@Override
public void init() throws ServletException {
    // 初始化一些测试数据
    users.add(new User(1, "张三", "zhangsan@example.com"));
    users.add(new User(2, "李四", "lisi@example.com"));
    users.add(new User(3, "王五", "wangwu@example.com"));
}

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {

    String pathInfo = request.getPathInfo();
    response.setContentType("application/json;charset=UTF-8");
    PrintWriter out = response.getWriter();

    try {
        if (pathInfo == null || pathInfo.equals("/")) {
            // 获取所有用户
            out.println(usersToJson(users));
        } else {
            // 获取特定用户
            String[] parts = pathInfo.split("/");
            if (parts.length >= 2) {
                int userId = Integer.parseInt(parts[1]);
                User user = findUserById(userId);
                if (user != null) {
                    out.println(userToJson(user));
                } else {
                    response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                    out.println("{\"error\": \"用户不存在\"}");
                }
            }
        }
    } catch (NumberFormatException e) {
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        out.println("{\"error\": \"无效的用户ID\"}");
    }
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {

    // 创建新用户
    String name = request.getParameter("name");
    String email = request.getParameter("email");

    if (name == null || email == null || name.trim().isEmpty() || email.trim().isEmpty()) {
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        response.getWriter().println("{\"error\": \"姓名和邮箱不能为空\"}");
        return;
    }

    int newId = users.size() + 1;
    User newUser = new User(newId, name.trim(), email.trim());
    users.add(newUser);

    response.setStatus(HttpServletResponse.SC_CREATED);
    response.setContentType("application/json;charset=UTF-8");
    response.getWriter().println(userToJson(newUser));
}

@Override
protected void doPut(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {

    String pathInfo = request.getPathInfo();
    if (pathInfo == null || pathInfo.equals("/")) {
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        response.getWriter().println("{\"error\": \"缺少用户ID\"}");
        return;
    }

    try {
        String[] parts = pathInfo.split("/");
        int userId = Integer.parseInt(parts[1]);
        User user = findUserById(userId);

        if (user == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            response.getWriter().println("{\"error\": \"用户不存在\"}");
            return;
        }

        // 读取请求体
        StringBuilder sb = new StringBuilder();
        BufferedReader reader = request.getReader();
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }

        // 简单的JSON解析(实际项目中应使用JSON库)
        String requestBody = sb.toString();
        if (requestBody.contains("name")) {
            String name = extractJsonValue(requestBody, "name");
            if (name != null && !name.trim().isEmpty()) {
                user.setName(name.trim());
            }
        }
        if (requestBody.contains("email")) {
            String email = extractJsonValue(requestBody, "email");
            if (email != null && !email.trim().isEmpty()) {
                user.setEmail(email.trim());
            }
        }

        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(userToJson(user));

    } catch (NumberFormatException e) {
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        response.getWriter().println("{\"error\": \"无效的用户ID\"}");
    }
}

@Override
protected void doDelete(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {

    String pathInfo = request.getPathInfo();
    if (pathInfo == null || pathInfo.equals("/")) {
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        response.getWriter().println("{\"error\": \"缺少用户ID\"}");
        return;
    }

    try {
        String[] parts = pathInfo.split("/");
        int userId = Integer.parseInt(parts[1]);

        boolean removed = users.removeIf(user -> user.getId() == userId);

        if (removed) {
            response.setStatus(HttpServletResponse.SC_NO_CONTENT);
        } else {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            response.getWriter().println("{\"error\": \"用户不存在\"}");
        }

    } catch (NumberFormatException e) {
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        response.getWriter().println("{\"error\": \"无效的用户ID\"}");
    }
}

private User findUserById(int id) {
    return users.stream()
               .filter(user -> user.getId() == id)
               .findFirst()
               .orElse(null);
}

private String userToJson(User user) {
    return String.format(
        "{\"id\": %d, \"name\": \"%s\", \"email\": \"%s\"}",
        user.getId(), user.getName(), user.getEmail()
    );
}

private String usersToJson(List<User> users) {
    StringBuilder sb = new StringBuilder("[");
    for (int i = 0; i < users.size(); i++) {
        if (i > 0) sb.append(", ");
        sb.append(userToJson(users.get(i)));
    }
    sb.append("]");
    return sb.toString();
}

private String extractJsonValue(String json, String key) {
    String pattern = "\"" + key + "\":\"";
    int start = json.indexOf(pattern);
    if (start == -1) return null;

    start += pattern.length();
    int end = json.indexOf("\"", start);
    if (end == -1) return null;

    return json.substring(start, end);
}

}

// 用户实体类 class User { private int id; private String name; private String email;

public User(int id, String name, String email) {
    this.id = id;
    this.name = name;
    this.email = email;
}

// Getters and Setters
public int getId() { return id; }
public void setId(int id) { this.id = id; }

public String getName() { return name; }
public void setName(String name) { this.name = name; }

public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }

@Override
public String toString() {
    return "User{id=" + id + ", name='" + name + "', email='" + email + "'}";
}

}


### 1.2 JSP基础

```jsp
<%-- userList.jsp - 用户列表页面 --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.*, java.text.*" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>用户管理系统</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
            background-color: #f5f5f5;
        }
        .container {
            max-width: 800px;
            margin: 0 auto;
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        .header {
            text-align: center;
            color: #333;
            margin-bottom: 30px;
        }
        .user-form {
            background-color: #f9f9f9;
            padding: 20px;
            border-radius: 5px;
            margin-bottom: 30px;
        }
        .form-group {
            margin-bottom: 15px;
        }
        .form-group label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }
        .form-group input {
            width: 100%;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
            box-sizing: border-box;
        }
        .btn {
            background-color: #007bff;
            color: white;
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .btn:hover {
            background-color: #0056b3;
        }
        .btn-danger {
            background-color: #dc3545;
        }
        .btn-danger:hover {
            background-color: #c82333;
        }
        .user-table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 20px;
        }
        .user-table th, .user-table td {
            border: 1px solid #ddd;
            padding: 12px;
            text-align: left;
        }
        .user-table th {
            background-color: #f8f9fa;
            font-weight: bold;
        }
        .user-table tr:nth-child(even) {
            background-color: #f9f9f9;
        }
        .message {
            padding: 10px;
            margin-bottom: 20px;
            border-radius: 4px;
        }
        .message.success {
            background-color: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }
        .message.error {
            background-color: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1 class="header">用户管理系统</h1>
        
        <%-- 显示消息 --%>
        <c:if test="${not empty message}">
            <div class="message ${messageType}">
                ${message}
            </div>
        </c:if>
        
        <%-- 添加用户表单 --%>
        <div class="user-form">
            <h2>添加新用户</h2>
            <form action="${pageContext.request.contextPath}/users" method="post">
                <div class="form-group">
                    <label for="name">姓名:</label>
                    <input type="text" id="name" name="name" required>
                </div>
                <div class="form-group">
                    <label for="email">邮箱:</label>
                    <input type="email" id="email" name="email" required>
                </div>
                <button type="submit" class="btn">添加用户</button>
            </form>
        </div>
        
        <%-- 用户列表 --%>
        <h2>用户列表</h2>
        <c:choose>
            <c:when test="${empty users}">
                <p>暂无用户数据</p>
            </c:when>
            <c:otherwise>
                <table class="user-table">
                    <thead>
                        <tr>
                            <th>ID</th>
                            <th>姓名</th>
                            <th>邮箱</th>
                            <th>操作</th>
                        </tr>
                    </thead>
                    <tbody>
                        <c:forEach var="user" items="${users}">
                            <tr>
                                <td>${user.id}</td>
                                <td><c:out value="${user.name}" /></td>
                                <td><c:out value="${user.email}" /></td>
                                <td>
                                    <form action="${pageContext.request.contextPath}/users/${user.id}" 
                                          method="post" style="display: inline;">
                                        <input type="hidden" name="_method" value="DELETE">
                                        <button type="submit" class="btn btn-danger" 
                                                onclick="return confirm('确定要删除用户 ${user.name} 吗?')">
                                            删除
                                        </button>
                                    </form>
                                </td>
                            </tr>
                        </c:forEach>
                    </tbody>
                </table>
            </c:otherwise>
        </c:choose>
        
        <%-- 页面信息 --%>
        <div style="margin-top: 30px; text-align: center; color: #666;">
            <p>当前时间: <fmt:formatDate value="${now}" pattern="yyyy-MM-dd HH:mm:ss" /></p>
            <p>总用户数: ${users.size()}</p>
        </div>
    </div>
    
    <script>
        // 简单的客户端验证
        document.querySelector('form').addEventListener('submit', function(e) {
            const name = document.getElementById('name').value.trim();
            const email = document.getElementById('email').value.trim();
            
            if (!name) {
                alert('请输入姓名');
                e.preventDefault();
                return;
            }
            
            if (!email) {
                alert('请输入邮箱');
                e.preventDefault();
                return;
            }
            
            // 简单的邮箱格式验证
            const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
            if (!emailRegex.test(email)) {
                alert('请输入有效的邮箱地址');
                e.preventDefault();
                return;
            }
        });
    </script>
</body>
</html>

1.3 Filter和Listener

// 字符编码过滤器
@WebFilter("/*")
public class CharacterEncodingFilter implements Filter {
    
    private String encoding = "UTF-8";
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String encodingParam = filterConfig.getInitParameter("encoding");
        if (encodingParam != null && !encodingParam.trim().isEmpty()) {
            this.encoding = encodingParam;
        }
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        
        // 设置请求编码
        request.setCharacterEncoding(encoding);
        
        // 设置响应编码
        response.setCharacterEncoding(encoding);
        
        // 继续过滤链
        chain.doFilter(request, response);
    }
    
    @Override
    public void destroy() {
        // 清理资源
    }
}

// 日志记录过滤器
@WebFilter("/*")
public class LoggingFilter implements Filter {
    
    private static final Logger logger = Logger.getLogger(LoggingFilter.class.getName());
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        // 记录请求开始时间
        long startTime = System.currentTimeMillis();
        
        // 记录请求信息
        String method = httpRequest.getMethod();
        String uri = httpRequest.getRequestURI();
        String queryString = httpRequest.getQueryString();
        String remoteAddr = httpRequest.getRemoteAddr();
        String userAgent = httpRequest.getHeader("User-Agent");
        
        logger.info(String.format("请求开始 - %s %s%s from %s [%s]", 
            method, uri, 
            queryString != null ? "?" + queryString : "",
            remoteAddr, userAgent));
        
        try {
            // 继续过滤链
            chain.doFilter(request, response);
        } finally {
            // 记录响应信息
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;
            int status = httpResponse.getStatus();
            
            logger.info(String.format("请求完成 - %s %s -> %d (%d ms)", 
                method, uri, status, duration));
        }
    }
}

// 安全过滤器
@WebFilter("/admin/*")
public class SecurityFilter implements Filter {
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpSession session = httpRequest.getSession(false);
        
        // 检查用户是否已登录
        boolean isLoggedIn = (session != null && session.getAttribute("user") != null);
        
        if (!isLoggedIn) {
            // 未登录,重定向到登录页面
            String loginUrl = httpRequest.getContextPath() + "/login";
            httpResponse.sendRedirect(loginUrl);
            return;
        }
        
        // 检查用户权限
        User user = (User) session.getAttribute("user");
        if (!hasAdminRole(user)) {
            httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
            httpResponse.getWriter().println("访问被拒绝:需要管理员权限");
            return;
        }
        
        // 继续过滤链
        chain.doFilter(request, response);
    }
    
    private boolean hasAdminRole(User user) {
        // 简单的权限检查逻辑
        return user != null && "admin".equals(user.getRole());
    }
}

// 应用程序生命周期监听器
@WebListener
public class AppContextListener implements ServletContextListener {
    
    private static final Logger logger = Logger.getLogger(AppContextListener.class.getName());
    
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext context = sce.getServletContext();
        
        logger.info("Web应用程序启动中...");
        
        // 初始化应用程序配置
        initializeConfiguration(context);
        
        // 初始化数据库连接池
        initializeDatabase(context);
        
        // 初始化缓存
        initializeCache(context);
        
        // 设置应用程序启动时间
        context.setAttribute("startupTime", new Date());
        
        logger.info("Web应用程序启动完成");
    }
    
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        ServletContext context = sce.getServletContext();
        
        logger.info("Web应用程序关闭中...");
        
        // 清理缓存
        cleanupCache(context);
        
        // 关闭数据库连接池
        cleanupDatabase(context);
        
        logger.info("Web应用程序关闭完成");
    }
    
    private void initializeConfiguration(ServletContext context) {
        // 加载配置文件
        Properties config = new Properties();
        try (InputStream is = context.getResourceAsStream("/WEB-INF/app.properties")) {
            if (is != null) {
                config.load(is);
                context.setAttribute("appConfig", config);
                logger.info("配置文件加载成功");
            }
        } catch (IOException e) {
            logger.warning("配置文件加载失败: " + e.getMessage());
        }
    }
    
    private void initializeDatabase(ServletContext context) {
        // 初始化数据库连接池(示例)
        try {
            // 这里应该初始化真实的数据库连接池
            context.setAttribute("dbInitialized", true);
            logger.info("数据库连接池初始化成功");
        } catch (Exception e) {
            logger.severe("数据库连接池初始化失败: " + e.getMessage());
        }
    }
    
    private void initializeCache(ServletContext context) {
        // 初始化缓存(示例)
        Map<String, Object> cache = new ConcurrentHashMap<>();
        context.setAttribute("appCache", cache);
        logger.info("缓存初始化成功");
    }
    
    private void cleanupCache(ServletContext context) {
        Map<String, Object> cache = (Map<String, Object>) context.getAttribute("appCache");
        if (cache != null) {
            cache.clear();
            logger.info("缓存清理完成");
        }
    }
    
    private void cleanupDatabase(ServletContext context) {
        // 清理数据库连接池
        Boolean dbInitialized = (Boolean) context.getAttribute("dbInitialized");
        if (Boolean.TRUE.equals(dbInitialized)) {
            // 这里应该关闭真实的数据库连接池
            logger.info("数据库连接池清理完成");
        }
    }
}

// 会话监听器
@WebListener
public class SessionListener implements HttpSessionListener, HttpSessionAttributeListener {
    
    private static final Logger logger = Logger.getLogger(SessionListener.class.getName());
    private static final AtomicInteger activeSessions = new AtomicInteger(0);
    
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        int count = activeSessions.incrementAndGet();
        logger.info("新会话创建,会话ID: " + se.getSession().getId() + 
                   ",当前活跃会话数: " + count);
    }
    
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        int count = activeSessions.decrementAndGet();
        logger.info("会话销毁,会话ID: " + se.getSession().getId() + 
                   ",当前活跃会话数: " + count);
    }
    
    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        logger.fine("会话属性添加: " + se.getName() + " = " + se.getValue());
    }
    
    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {
        logger.fine("会话属性移除: " + se.getName());
    }
    
    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) {
        logger.fine("会话属性替换: " + se.getName() + " = " + se.getValue());
    }
    
    public static int getActiveSessionCount() {
        return activeSessions.get();
    }
}

2. Spring框架基础

2.1 Spring IoC容器

// 用户服务接口
public interface UserService {
    User findById(Long id);
    User save(User user);
    List<User> findAll();
    void deleteById(Long id);
    User findByEmail(String email);
}

// 用户服务实现
@Service
@Transactional
public class UserServiceImpl implements UserService {
    
    private final UserRepository userRepository;
    private final EmailService emailService;
    
    // 构造函数注入
    public UserServiceImpl(UserRepository userRepository, EmailService emailService) {
        this.userRepository = userRepository;
        this.emailService = emailService;
    }
    
    @Override
    @Transactional(readOnly = true)
    public User findById(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new UserNotFoundException("用户不存在: " + id));
    }
    
    @Override
    public User save(User user) {
        // 验证用户数据
        validateUser(user);
        
        // 检查邮箱是否已存在
        if (user.getId() == null && userRepository.existsByEmail(user.getEmail())) {
            throw new DuplicateEmailException("邮箱已存在: " + user.getEmail());
        }
        
        // 保存用户
        User savedUser = userRepository.save(user);
        
        // 发送欢迎邮件(新用户)
        if (user.getId() == null) {
            emailService.sendWelcomeEmail(savedUser);
        }
        
        return savedUser;
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<User> findAll() {
        return userRepository.findAll();
    }
    
    @Override
    public void deleteById(Long id) {
        if (!userRepository.existsById(id)) {
            throw new UserNotFoundException("用户不存在: " + id);
        }
        userRepository.deleteById(id);
    }
    
    @Override
    @Transactional(readOnly = true)
    public User findByEmail(String email) {
        return userRepository.findByEmail(email)
                .orElseThrow(() -> new UserNotFoundException("用户不存在: " + email));
    }
    
    private void validateUser(User user) {
        if (user == null) {
            throw new IllegalArgumentException("用户不能为空");
        }
        if (user.getName() == null || user.getName().trim().isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }
        if (user.getEmail() == null || user.getEmail().trim().isEmpty()) {
            throw new IllegalArgumentException("邮箱不能为空");
        }
        if (!isValidEmail(user.getEmail())) {
            throw new IllegalArgumentException("邮箱格式无效");
        }
    }
    
    private boolean isValidEmail(String email) {
        return email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");
    }
}

// 用户仓库接口
public interface UserRepository {
    Optional<User> findById(Long id);
    User save(User user);
    List<User> findAll();
    void deleteById(Long id);
    boolean existsById(Long id);
    Optional<User> findByEmail(String email);
    boolean existsByEmail(String email);
}

// 用户仓库实现(内存版本)
@Repository
public class InMemoryUserRepository implements UserRepository {
    
    private final Map<Long, User> users = new ConcurrentHashMap<>();
    private final AtomicLong idGenerator = new AtomicLong(1);
    
    @PostConstruct
    public void init() {
        // 初始化一些测试数据
        save(new User(null, "张三", "zhangsan@example.com"));
        save(new User(null, "李四", "lisi@example.com"));
        save(new User(null, "王五", "wangwu@example.com"));
    }
    
    @Override
    public Optional<User> findById(Long id) {
        return Optional.ofNullable(users.get(id));
    }
    
    @Override
    public User save(User user) {
        if (user.getId() == null) {
            user.setId(idGenerator.getAndIncrement());
        }
        users.put(user.getId(), user);
        return user;
    }
    
    @Override
    public List<User> findAll() {
        return new ArrayList<>(users.values());
    }
    
    @Override
    public void deleteById(Long id) {
        users.remove(id);
    }
    
    @Override
    public boolean existsById(Long id) {
        return users.containsKey(id);
    }
    
    @Override
    public Optional<User> findByEmail(String email) {
        return users.values().stream()
                   .filter(user -> email.equals(user.getEmail()))
                   .findFirst();
    }
    
    @Override
    public boolean existsByEmail(String email) {
        return users.values().stream()
                   .anyMatch(user -> email.equals(user.getEmail()));
    }
}

// 邮件服务接口
public interface EmailService {
    void sendWelcomeEmail(User user);
    void sendPasswordResetEmail(User user, String resetToken);
    void sendNotificationEmail(User user, String subject, String content);
}

// 邮件服务实现
@Service
public class EmailServiceImpl implements EmailService {
    
    private static final Logger logger = LoggerFactory.getLogger(EmailServiceImpl.class);
    
    @Value("${app.email.from:noreply@example.com}")
    private String fromEmail;
    
    @Value("${app.email.enabled:false}")
    private boolean emailEnabled;
    
    @Override
    public void sendWelcomeEmail(User user) {
        String subject = "欢迎加入我们的平台";
        String content = String.format(
            "亲爱的 %s,\n\n" +
            "欢迎加入我们的平台!您的账户已成功创建。\n\n" +
            "如有任何问题,请随时联系我们。\n\n" +
            "祝好!\n" +
            "平台团队",
            user.getName()
        );
        
        sendEmail(user.getEmail(), subject, content);
    }
    
    @Override
    public void sendPasswordResetEmail(User user, String resetToken) {
        String subject = "密码重置请求";
        String content = String.format(
            "亲爱的 %s,\n\n" +
            "我们收到了您的密码重置请求。\n\n" +
            "重置令牌: %s\n\n" +
            "如果这不是您的操作,请忽略此邮件。\n\n" +
            "祝好!\n" +
            "平台团队",
            user.getName(), resetToken
        );
        
        sendEmail(user.getEmail(), subject, content);
    }
    
    @Override
    public void sendNotificationEmail(User user, String subject, String content) {
        sendEmail(user.getEmail(), subject, content);
    }
    
    private void sendEmail(String to, String subject, String content) {
        if (!emailEnabled) {
            logger.info("邮件发送已禁用,模拟发送邮件:");
            logger.info("收件人: {}", to);
            logger.info("主题: {}", subject);
            logger.info("内容: {}", content);
            return;
        }
        
        try {
            // 这里应该实现真实的邮件发送逻辑
            // 例如使用 JavaMail API 或 Spring Mail
            logger.info("发送邮件到 {} - 主题: {}", to, subject);
            
            // 模拟邮件发送延迟
            Thread.sleep(100);
            
        } catch (Exception e) {
            logger.error("邮件发送失败: " + e.getMessage(), e);
            throw new EmailSendException("邮件发送失败", e);
        }
    }
}

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

public class DuplicateEmailException extends RuntimeException {
    public DuplicateEmailException(String message) {
        super(message);
    }
}

public class EmailSendException extends RuntimeException {
    public EmailSendException(String message, Throwable cause) {
        super(message, cause);
    }
}

// Spring配置类
@Configuration
@ComponentScan(basePackages = "com.example")
@PropertySource("classpath:application.properties")
@EnableTransactionManagement
public class AppConfig {
    
    @Bean
    public PropertySourcesPlaceholderConfigurer propertyConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
    
    @Bean
    public PlatformTransactionManager transactionManager() {
        // 简单的事务管理器(实际项目中应使用数据库事务管理器)
        return new ResourcelessTransactionManager();
    }
    
    @Bean
    @Profile("dev")
    public UserRepository devUserRepository() {
        return new InMemoryUserRepository();
    }
    
    @Bean
    @Profile("prod")
    public UserRepository prodUserRepository() {
        // 生产环境应该返回数据库实现
        return new InMemoryUserRepository();
    }
}