SpringMVC常用注解(二)

目录

一、简介

SpringMVC的注解有很多,常用的注解有下面这几个:

1、为什么要使用注解

使用注解可以简化配置,提高代码的可读性和可维护性。通过注解可以实现依赖注入,减少手动管理对象的代码量。注解还支持面向切面编程,实现切面、切入点和通知等。此外,注解提供了声明式事务管理的支持,简化了事务配置和管理。注解还可以用于组件扫描和自动装配,提高开发效率。最后,注解在测试时也有很好的支持。总之,注解使得代码更简洁、灵活,并能更好地利用框架的功能和特性。

代码简洁:减少XML配置,代码更易读

开发快速:自动完成常见任务,减少样板代码

类型安全:编译时检查,减少运行时错误

灵活强大:支持各种Web开发需求

易于测试:注解使单元测试更简单

现代化:符合现代Java开发最佳实践

二、@ResponseBody注解

1、基本概念

@ResponseBody 注解用于将方法返回值直接写入HTTP响应体中,而不是解析为视图名称。它告诉Spring:"这个方法返回的就是数据,不要把它当作视图名"。

2、基本语法

java 复制代码
@Controller
public class UserController {
    
    @GetMapping("/user/{id}")
    @ResponseBody
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
    }
}

3、基本用法

1、在@Controller类中使用

java 复制代码
@Controller
@RequestMapping("/web")
public class WebController {
    
    // 返回视图名称(默认行为)
    @GetMapping("/users")
    public String userList(Model model) {
        model.addAttribute("users", userService.findAll());
        return "user-list"; // 返回视图名,由视图解析器处理
    }
    
    // 返回数据(使用@ResponseBody)
    @GetMapping("/api/users")
    @ResponseBody
    public List<User> getUsers() {
        return userService.findAll(); // 直接返回数据,不经过视图解析器
    }
    
    // 混合使用:同一控制器中既有页面又有API
    @GetMapping("/user/{id}/profile")
    public String userProfile(@PathVariable Long id, Model model) {
        model.addAttribute("user", userService.findById(id));
        return "user-profile"; // 返回页面
    }
    
    @GetMapping("/user/{id}/data")
    @ResponseBody
    public User getUserData(@PathVariable Long id) {
        return userService.findById(id); // 返回JSON数据
    }
}

2、返回不同类型的数据

java 复制代码
@Controller
public class VariousResponseController {
    
    // 返回对象(自动转为JSON)
    @GetMapping("/user/{id}")
    @ResponseBody
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
        // 响应:{"id": 123, "name": "John", "email": "john@example.com"}
    }
    
    // 返回字符串
    @GetMapping("/status")
    @ResponseBody
    public String getStatus() {
        return "Server is running";
        // 响应:"Server is running"
    }
    
    // 返回数字
    @GetMapping("/count")
    @ResponseBody
    public int getUserCount() {
        return userService.getCount();
        // 响应:42
    }
    
    // 返回布尔值
    @GetMapping("/check/{email}")
    @ResponseBody
    public boolean checkEmail(@PathVariable String email) {
        return userService.existsByEmail(email);
        // 响应:true
    }
    
    // 返回集合
    @GetMapping("/all-users")
    @ResponseBody
    public List<User> getAllUsers() {
        return userService.findAll();
        // 响应:[{"id":1,"name":"John"},{"id":2,"name":"Jane"}]
    }
    
    // 返回Map
    @GetMapping("/stats")
    @ResponseBody
    public Map<String, Object> getStats() {
        Map<String, Object> stats = new HashMap<>();
        stats.put("userCount", userService.getCount());
        stats.put("activeUsers", userService.getActiveCount());
        stats.put("timestamp", LocalDateTime.now());
        return stats;
        // 响应:{"userCount": 100, "activeUsers": 85, "timestamp": "2024-01-15T10:30:00"}
    }
}

@ResponseBody@RestController的组成部分之一

三、@RequestMapping注解

1、基本概念

@RequestMapping 是Spring MVC中最基础、最强大的注解,用于将HTTP请求映射到特定的处理方法上。它可以用在类级别和方法级别。

2、基本语法

java 复制代码
@Controller
@RequestMapping("/users")  // 类级别:定义基本路径
public class UserController {
    
    @RequestMapping("/profile")  // 方法级别:组合成 /users/profile
    public String userProfile() {
        return "profile";
    }
}

3、支持的方法类型

1、指定HTTP方法

java 复制代码
@Controller
@RequestMapping("/products")
public class ProductController {
    
    // 指定GET方法
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public String productList() {
        return "product-list";
    }
    
    // 指定POST方法
    @RequestMapping(value = "/create", method = RequestMethod.POST)
    public String createProduct() {
        return "redirect:/products/list";
    }
    
    // 指定多个HTTP方法
    @RequestMapping(value = "/update", method = {RequestMethod.PUT, RequestMethod.PATCH})
    public String updateProduct() {
        return "success";
    }
    
    // 不指定method,默认支持所有HTTP方法
    @RequestMapping("/all-methods")
    public String handleAllMethods() {
        return "all-methods";
    }
}

2、专用注解(推荐)

@RequestMapping注解可以大致分为以下的四种:@GetMapiing@PostMapping@PutMapping@DeleteMapping

java 复制代码
@RestController
@RequestMapping("/api")
public class ModernController {
    
    // 等价于 @RequestMapping(method = RequestMethod.GET)
    @GetMapping("/users")
    public List<User> getUsers() {
        return userService.findAll();
    }
    
    // 等价于 @RequestMapping(method = RequestMethod.POST)
    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
        return userService.save(user);
    }
    
    // 等价于 @RequestMapping(method = RequestMethod.PUT)
    @PutMapping("/users/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        return userService.update(id, user);
    }
    
    // 等价于 @RequestMapping(method = RequestMethod.DELETE)
    @DeleteMapping("/users/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.delete(id);
    }
    
    // 等价于 @RequestMapping(method = RequestMethod.PATCH)
    @PatchMapping("/users/{id}")
    public User patchUser(@PathVariable Long id, @RequestBody Map<String, Object> updates) {
        return userService.partialUpdate(id, updates);
    }
}

四、@ModelAttribute注解(不常用)

1、基本概念

@ModelAttribute 用于将请求参数绑定到模型对象 上,主要用于表单数据处理模型数据预加载

2、方法级别的 @ModelAttribute

1、预加载数据(在控制器方法执行前)

java 复制代码
@Controller
@RequestMapping("/users")
public class UserController {
    
    // 在每个控制器方法执行前都会调用
    @ModelAttribute("categories")
    public List<String> loadCategories() {
        return Arrays.asList("Admin", "User", "Guest", "Manager");
    }
    
    @ModelAttribute("departments")
    public List<Department> loadDepartments() {
        return departmentService.findAll();
    }
    
    // 使用预加载的数据
    @GetMapping("/create")
    public String showCreateForm(Model model) {
        // categories 和 departments 已经自动添加到model中
        model.addAttribute("user", new User());
        return "user-create";
    }
}

2、条件性预加载

java 复制代码
@Controller
public class ConditionalModelAttribute {
    
    // 只在特定条件下预加载
    @ModelAttribute("currentUser")
    public User loadCurrentUser(HttpSession session) {
        Long userId = (Long) session.getAttribute("currentUserId");
        if (userId != null) {
            return userService.findById(userId);
        }
        return null;
    }
    
    @ModelAttribute("appConfig")
    public Map<String, Object> loadConfig() {
        Map<String, Object> config = new HashMap<>();
        config.put("appName", "My Application");
        config.put("version", "1.0.0");
        config.put("maxFileSize", "10MB");
        return config;
    }
}

3、参数级别的 @ModelAttribute

1、绑定表单数据

java 复制代码
@Controller
@RequestMapping("/products")
public class ProductController {
    
    // 显示表单
    @GetMapping("/create")
    public String showCreateForm(Model model) {
        model.addAttribute("product", new Product());
        return "product-create";
    }
    
    // 处理表单提交
    @PostMapping("/create")
    public String createProduct(@ModelAttribute Product product, 
                               BindingResult result) {
        if (result.hasErrors()) {
            return "product-create"; // 返回表单页面显示错误
        }
        productService.save(product);
        return "redirect:/products";
    }
}

2、自定义模型属性名

java 复制代码
@Controller
public class CustomNameController {
    
    @PostMapping("/register")
    public String registerUser(@ModelAttribute("newUser") User user) {
        // 模型属性名为 "newUser" 而不是 "user"
        userService.register(user);
        return "redirect:/login";
    }
    
    @GetMapping("/edit")
    public String editUser(@RequestParam Long id, Model model) {
        User user = userService.findById(id);
        model.addAttribute("editingUser", user); // 手动设置属性名
        return "user-edit";
    }
    
    @PostMapping("/update")
    public String updateUser(@ModelAttribute("editingUser") User user) {
        userService.update(user);
        return "redirect:/users";
    }
}

五、@ExceptionHandler 注解

1、基本概念

@ExceptionHandler 用于在控制器内部处理特定类型的异常,提供优雅的错误处理机制。

2、基本用法

控制器内部的异常处理:

java 复制代码
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
    }
    
    // 处理当前控制器中的 UserNotFoundException
    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex) {
        ErrorResponse error = new ErrorResponse("USER_NOT_FOUND", ex.getMessage());
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }
    
    // 处理参数验证异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidationErrors(MethodArgumentNotValidException ex) {
        List<String> errors = ex.getBindingResult()
            .getFieldErrors()
            .stream()
            .map(error -> error.getField() + ": " + error.getDefaultMessage())
            .collect(Collectors.toList());
        
        ErrorResponse errorResponse = new ErrorResponse("VALIDATION_FAILED", "数据验证失败", errors);
        return ResponseEntity.badRequest().body(errorResponse);
    }
}

六、@ControllerAdvice 全局异常处理

1、基本概念

@ControllerAdvice 是一个组件注解 ,用于定义全局的异常处理、数据绑定和数据预处理。它让异常处理代码能够跨控制器重用

2、核心作用

java 复制代码
@ControllerAdvice
public class GlobalExceptionHandler {
    // 这里的方法会应用到所有 @Controller 和 @RestController
}

✅ 全局异常处理:处理所有控制器的异常

✅ 全局数据绑定:为所有控制器预加载数据

✅ 全局数据预处理:预处理所有控制器的请求数据

3、基本用法

java 复制代码
@ControllerAdvice
public class BasicExceptionHandler {
    
    // 处理所有 NullPointerException
    @ExceptionHandler(NullPointerException.class)
    public ResponseEntity<String> handleNullPointer(NullPointerException ex) {
        return ResponseEntity.badRequest().body("空指针异常: " + ex.getMessage());
    }
    
    // 处理所有 Exception
    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleAllExceptions(Exception ex) {
        return ResponseEntity.status(500).body("系统错误: " + ex.getMessage());
    }
}

七、全局异常处理实战

1、后端统一返回结果实体类

java 复制代码
package com.sky.result;

import lombok.Data;

import java.io.Serializable;

/**
 * 后端统一返回结果
 * @param <T>
 */
@Data
public class Result<T> implements Serializable {

    private Integer code; //编码:1成功,0和其它数字为失败
    private String msg; //错误信息
    private T data; //数据

    public static <T> Result<T> success() {
        Result<T> result = new Result<T>();
        result.code = 1;
        return result;
    }

    public static <T> Result<T> success(T object) {
        Result<T> result = new Result<T>();
        result.data = object;
        result.code = 1;
        return result;
    }

    public static <T> Result<T> error(String msg) {
        Result result = new Result();
        result.msg = msg;
        result.code = 0;
        return result;
    }

}

2、全局异常处理实现类

java 复制代码
package com.sky.handler;

import com.sky.exception.BaseException;
import com.sky.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.sql.SQLIntegrityConstraintViolationException;

/**
 * 全局异常处理器,处理项目中抛出的业务异常
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 捕获业务异常
     * @param ex
     * @return
     */
    @ExceptionHandler
    public Result exceptionHandler(BaseException ex){
        log.error("异常信息:{}", ex.getMessage());
        return Result.error(ex.getMessage());
    }
    @ExceptionHandler
    public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
        log.error("异常信息:{}", ex.getMessage());
        String[] strings = ex.getMessage().split(" ");
        String msg = strings[2] + "已存在";
        return Result.error(msg);
    }

}

使用@RestControllerAdvice统一返回json数据格式,利用@ExceptionHandler处理每一份异常对应的类,如果是这个异常类就会返回相对应的处理方式。

相关推荐
代码or搬砖2 小时前
SpringMVC常用注解
log4j
R***62311 天前
Spring Boot 整合 log4j2 日志配置教程
spring boot·单元测试·log4j
百花~6 天前
自动化测试概念篇~
selenium·log4j
k***3888 天前
SpringBoot Test详解
spring boot·后端·log4j
马达加斯加D9 天前
C# --- 如何写UT
前端·c#·log4j
秃了也弱了。9 天前
testng:Java界功能强大的单元测试框架
java·单元测试·log4j
q***420512 天前
Spring Boot 整合 log4j2 日志配置教程
spring boot·单元测试·log4j
m0_5656111317 天前
Java高级特性:单元测试、反射、注解、动态代理
java·单元测试·log4j
LSL666_17 天前
Spring 框架整合 JUnit 单元测试——包含完整执行流程
spring·junit·log4j