Spring Web开发
大约 6 分钟
Spring Web开发
Spring Web框架概述
Spring Web框架是Spring生态系统中用于构建Web应用程序的核心模块。它提供了完整的MVC架构支持,包括DispatcherServlet、控制器、视图解析、数据绑定、验证等功能。
Spring Web架构
Spring MVC核心组件
DispatcherServlet
DispatcherServlet是Spring MVC的核心前端控制器,负责接收所有HTTP请求并分发给相应的处理器:
// web.xml配置
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>控制器(Controller)
控制器负责处理用户请求并返回相应的模型和视图:
@Controller
@RequestMapping("/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    // 处理GET请求
    @GetMapping
    public String listUsers(Model model) {
        List<User> users = userService.findAll();
        model.addAttribute("users", users);
        return "user/list";
    }
    
    // 处理GET请求,返回用户详情
    @GetMapping("/{id}")
    public String getUser(@PathVariable Long id, Model model) {
        User user = userService.findById(id);
        model.addAttribute("user", user);
        return "user/detail";
    }
    
    // 处理GET请求,显示创建表单
    @GetMapping("/new")
    public String showCreateForm(Model model) {
        model.addAttribute("user", new User());
        return "user/form";
    }
    
    // 处理POST请求,创建用户
    @PostMapping
    public String createUser(@Valid @ModelAttribute User user, 
                           BindingResult result, 
                           RedirectAttributes redirectAttributes) {
        if (result.hasErrors()) {
            return "user/form";
        }
        
        userService.save(user);
        redirectAttributes.addFlashAttribute("message", "用户创建成功");
        return "redirect:/users";
    }
}RESTful Web服务
Spring MVC提供了强大的REST支持:
REST控制器
@RestController
@RequestMapping("/api/users")
public class UserRestController {
    
    @Autowired
    private UserService userService;
    
    // 获取所有用户
    @GetMapping
    public ResponseEntity<List<User>> getAllUsers() {
        List<User> users = userService.findAll();
        return ResponseEntity.ok(users);
    }
    
    // 根据ID获取用户
    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        User user = userService.findById(id);
        if (user != null) {
            return ResponseEntity.ok(user);
        } else {
            return ResponseEntity.notFound().build();
        }
    }
    
    // 创建用户
    @PostMapping
    public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
        User savedUser = userService.save(user);
        return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
    }
    
    // 更新用户
    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, 
                                         @Valid @RequestBody User user) {
        user.setId(id);
        User updatedUser = userService.update(user);
        if (updatedUser != null) {
            return ResponseEntity.ok(updatedUser);
        } else {
            return ResponseEntity.notFound().build();
        }
    }
    
    // 删除用户
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.delete(id);
        return ResponseEntity.noContent().build();
    }
}请求参数处理
@RestController
@RequestMapping("/api/search")
public class SearchController {
    
    // 查询参数
    @GetMapping("/users")
    public List<User> searchUsers(@RequestParam(required = false) String name,
                                 @RequestParam(defaultValue = "0") int page,
                                 @RequestParam(defaultValue = "10") int size) {
        return userService.findByName(name, page, size);
    }
    
    // 请求头
    @GetMapping("/info")
    public String getInfo(@RequestHeader("User-Agent") String userAgent,
                         @RequestHeader(value = "Authorization", required = false) String auth) {
        return "User-Agent: " + userAgent;
    }
    
    // Cookie
    @GetMapping("/profile")
    public String getProfile(@CookieValue("sessionId") String sessionId) {
        return "Session ID: " + sessionId;
    }
}数据绑定与验证
数据绑定
@Controller
public class FormController {
    
    // 数据绑定到简单类型
    @PostMapping("/simple")
    public String handleSimple(@RequestParam String name, 
                              @RequestParam int age) {
        // 处理逻辑
        return "result";
    }
    
    // 数据绑定到对象
    @PostMapping("/user")
    public String handleUser(@ModelAttribute User user) {
        // Spring会自动将请求参数绑定到User对象的属性上
        return "result";
    }
    
    // 数据绑定到嵌套对象
    @PostMapping("/order")
    public String handleOrder(@ModelAttribute Order order) {
        // order.getUser().getName() 可以直接绑定
        return "result";
    }
}验证
// 实体类验证
public class User {
    
    @NotNull(message = "用户名不能为空")
    @Size(min = 2, max = 20, message = "用户名长度必须在2-20之间")
    private String name;
    
    @NotNull(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @Min(value = 0, message = "年龄不能小于0")
    @Max(value = 150, message = "年龄不能大于150")
    private Integer age;
    
    // getters and setters
}
@Controller
public class UserController {
    
    @PostMapping("/users")
    public String createUser(@Valid @ModelAttribute User user, 
                           BindingResult result, 
                           Model model) {
        if (result.hasErrors()) {
            // 验证失败,返回表单页面显示错误信息
            return "user/form";
        }
        
        userService.save(user);
        return "redirect:/users";
    }
}自定义验证器
// 自定义验证注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneNumberValidator.class)
public @interface PhoneNumber {
    String message() default "手机号格式不正确";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
// 自定义验证器
public class PhoneNumberValidator implements ConstraintValidator<PhoneNumber, String> {
    
    @Override
    public boolean isValid(String phoneNumber, ConstraintValidatorContext context) {
        if (phoneNumber == null) {
            return true; // 让@NotNull处理空值
        }
        return phoneNumber.matches("^1[3-9]\\d{9}$");
    }
}
// 使用自定义验证
public class User {
    @PhoneNumber
    private String phone;
    // ...
}视图技术集成
Thymeleaf模板引擎
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    
    @Bean
    public ViewResolver viewResolver() {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine());
        return resolver;
    }
    
    @Bean
    public TemplateEngine templateEngine() {
        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.setTemplateResolver(templateResolver());
        return engine;
    }
    
    @Bean
    public ITemplateResolver templateResolver() {
        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
        resolver.setPrefix("/WEB-INF/templates/");
        resolver.setSuffix(".html");
        resolver.setTemplateMode(TemplateMode.HTML);
        return resolver;
    }
}Thymeleaf模板示例
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>用户列表</title>
</head>
<body>
    <h1>用户列表</h1>
    
    <div th:if="${message}" class="alert alert-success" th:text="${message}"></div>
    
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>姓名</th>
                <th>邮箱</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="user : ${users}">
                <td th:text="${user.id}"></td>
                <td th:text="${user.name}"></td>
                <td th:text="${user.email}"></td>
                <td>
                    <a th:href="@{/users/{id}(id=${user.id})}" class="btn btn-info">查看</a>
                    <a th:href="@{/users/{id}/edit(id=${user.id})}" class="btn btn-primary">编辑</a>
                </td>
            </tr>
        </tbody>
    </table>
    
    <a href="/users/new" class="btn btn-success">创建用户</a>
</body>
</html>异常处理
全局异常处理器
@ControllerAdvice
public class GlobalExceptionHandler {
    
    // 处理所有异常
    @ExceptionHandler(Exception.class)
    public ModelAndView handleException(Exception e) {
        ModelAndView mav = new ModelAndView("error");
        mav.addObject("message", "系统发生错误: " + e.getMessage());
        return mav;
    }
    
    // 处理特定异常
    @ExceptionHandler(UserNotFoundException.class)
    public ModelAndView handleUserNotFound(UserNotFoundException e) {
        ModelAndView mav = new ModelAndView("error");
        mav.addObject("message", "用户未找到: " + e.getUserId());
        return mav;
    }
    
    // REST API异常处理
    @ExceptionHandler(ValidationException.class)
    @ResponseBody
    public ResponseEntity<ErrorResponse> handleValidationException(ValidationException e) {
        ErrorResponse error = new ErrorResponse("VALIDATION_ERROR", e.getMessage());
        return ResponseEntity.badRequest().body(error);
    }
}
// 错误响应类
public class ErrorResponse {
    private String code;
    private String message;
    
    public ErrorResponse(String code, String message) {
        this.code = code;
        this.message = message;
    }
    
    // getters and setters
}文件上传
配置文件上传
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    
    @Bean
    public MultipartResolver multipartResolver() {
        CommonsMultipartResolver resolver = new CommonsMultipartResolver();
        resolver.setMaxUploadSize(10485760); // 10MB
        resolver.setMaxInMemorySize(1048576); // 1MB
        return resolver;
    }
}文件上传控制器
@Controller
public class FileUploadController {
    
    @PostMapping("/upload")
    public String handleFileUpload(@RequestParam("file") MultipartFile file,
                                  RedirectAttributes redirectAttributes) {
        if (file.isEmpty()) {
            redirectAttributes.addFlashAttribute("message", "请选择文件");
            return "redirect:/uploadStatus";
        }
        
        try {
            // 保存文件
            byte[] bytes = file.getBytes();
            Path path = Paths.get("uploads/" + file.getOriginalFilename());
            Files.write(path, bytes);
            
            redirectAttributes.addFlashAttribute("message", 
                "文件上传成功: " + file.getOriginalFilename());
        } catch (IOException e) {
            redirectAttributes.addFlashAttribute("message", "文件上传失败: " + e.getMessage());
        }
        
        return "redirect:/uploadStatus";
    }
    
    // RESTful文件上传
    @PostMapping("/api/upload")
    public ResponseEntity<String> handleFileUploadRest(@RequestParam("file") MultipartFile file) {
        try {
            // 处理文件上传逻辑
            String fileName = fileService.saveFile(file);
            return ResponseEntity.ok("文件上传成功: " + fileName);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("文件上传失败: " + e.getMessage());
        }
    }
}拦截器
自定义拦截器
@Component
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 {
        logger.info("请求开始: {} {}", request.getMethod(), request.getRequestURI());
        request.setAttribute("startTime", System.currentTimeMillis());
        return true; // 继续执行后续拦截器和处理器
    }
    
    @Override
    public void postHandle(HttpServletRequest request, 
                          HttpServletResponse response, 
                          Object handler, 
                          ModelAndView modelAndView) throws Exception {
        long startTime = (Long) request.getAttribute("startTime");
        long endTime = System.currentTimeMillis();
        logger.info("请求处理完成,耗时: {} ms", (endTime - startTime));
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, 
                              HttpServletResponse response, 
                              Object handler, 
                              Exception ex) throws Exception {
        logger.info("请求结束: {} {}", request.getMethod(), request.getRequestURI());
        if (ex != null) {
            logger.error("请求处理异常", ex);
        }
    }
}注册拦截器
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    
    @Autowired
    private LoggingInterceptor loggingInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loggingInterceptor)
                .addPathPatterns("/api/**") // 拦截/api开头的请求
                .excludePathPatterns("/api/public/**"); // 排除/api/public开头的请求
    }
}跨域资源共享(CORS)
全局CORS配置
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("http://localhost:3000")
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowedHeaders("*")
                .allowCredentials(true);
    }
}控制器级别CORS
@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "http://localhost:3000")
public class UserRestController {
    
    @GetMapping
    public List<User> getAllUsers() {
        return userService.findAll();
    }
    
    // 可以为特定方法设置不同的CORS配置
    @CrossOrigin(origins = "http://admin.example.com")
    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.save(user);
    }
}通过以上内容,我们可以全面了解Spring Web开发的各个方面,包括MVC架构、RESTful服务、数据绑定与验证、视图技术、异常处理、文件上传、拦截器和跨域处理等。Spring Web框架提供了完整的Web开发解决方案,能够满足各种复杂的Web应用需求。
