自学内容网 自学内容网

【springboot入门-mvc常用注解使用方式及原理】

常用注解

  • @PathVariable:用于从URL路径中提取变量。
  • @RequestHeader:用于从HTTP请求头中获取数据。
  • @ModelAttribute:用于获取请求参数(包括URL参数和POST请求的表单数据),也可以用于将数据绑定到对象上。
  • @RequestParam:用于获取URL参数。
  • @CookieValue:用于获取请求中的Cookie值。
  • @RequestBody:用于获取请求体中的数据,通常用于处理POST请求中的JSON或XML数据。

java代码示例(普通)

import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;

@RestController
@RequestMapping("/api")
public class DataController {

    // 使用 @PathVariable 从URL路径中获取变量
    @GetMapping("/path/{userId}/profile")
    public String getPathVariable(@PathVariable String userId) {
        return "User ID from Path Variable: " + userId;
    }

    // 使用 @RequestHeader 从请求头中获取数据
    @GetMapping("/header")
    public String getRequestHeader(@RequestHeader("X-Request-ID") String requestId) {
        return "Request ID from Header: " + requestId;
    }

    // 使用 @ModelAttribute 获取请求参数并绑定到对象
    @PostMapping("/modelAttribute")
    public String handleModelAttribute(@ModelAttribute User user) {
        return "User Name: " + user.getName() + ", Age: " + user.getAge();
    }

    // 使用 @RequestParam 获取URL参数
    @GetMapping("/params")
    public String getRequestParam(@RequestParam("name") String name, @RequestParam("age") int age) {
        return "Name: " + name + ", Age: " + age;
    }

    // 使用 @CookieValue 获取Cookie值
    @GetMapping("/cookie")
    public String getCookieValue(@CookieValue("session") String sessionCookie) {
        return "Session Cookie: " + sessionCookie;
    }

    // 使用 @RequestBody 获取请求体中的数据
    @PostMapping("/body")
    public String handleRequestBody(@RequestBody User user) {
        return "User Name from Body: " + user.getName() + ", Age: " + user.getAge();
    }

    static class User {
        private String name;
        private int age;

        // Getters and setters
        public String getName() {
            return name;
        }

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

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }
}

测试示例(普通)

  1. @PathVariable 测试路径:
GET /api/path/123/profile
  1. @RequestHeader 测试路径:
GET /api/header
请求头中需要包含 X-Request-ID: 12345。
  1. @ModelAttribute 测试路径:
POST /api/modelAttribute
{
    "name": "John Doe",
    "age": 30
}
  1. @RequestParam 测试路径:
GET /api/params?name=John&age=30
  1. @CookieValue 测试路径:
GET /api/cookie
Cookie: session=abc123
  1. @RequestBody 测试路径:
POST /api/body
{
    "name": "Jane Doe",
    "age": 25
}

特殊(传数组)

  1. 使用 @RequestBody 传递JSON数组
    • 如果你使用POST请求并通过请求体传递一个JSON数组,你可以使用@RequestBody注解来接收这个数组。在Spring Boot中,你可以使用HttpEntity或者直接使用@RequestBody来接收数组。
    @RestController
    @RequestMapping("/api")
    public class ListController {
    
        // 使用 @RequestBody 接收JSON数组
        @PostMapping("/list")
        public String handleRequestBodyList(@RequestBody List<User> users) {
            return "Received " + users.size() + " users";
        }
    }
    
    • 测试路径和请求体
    POST /api/list
    [
        {"name": "John Doe", "age": 30},
        {"name": "Jane Doe", "age": 25}
    ]
    
  2. 使用 @RequestParam 传递多个相同参数
    • 如果你使用GET请求并通过URL参数传递多个相同名称的参数,你可以使用@RequestParam注解来接收这些参数,并将其转换为列表。
    @RestController
    @RequestMapping("/api")
    public class ListController {
    
        // 使用 @RequestParam 接收多个相同参数
        @GetMapping("/params")
        public String getRequestParamList(@RequestParam("userId") List<String> userIds) {
            return "Received user IDs: " + Arrays.toString(userIds.toArray());
        }
    }
    
    • 测试路径和参数:
    GET /api/params?userId=1&userId=2&userId=3
    
  3. 使用 @ModelAttribute 传递表单数据
    • 如果你使用POST请求并通过表单数据传递一个列表,你可以使用@ModelAttribute注解来接收这个列表。这通常用于处理表单提交。
    @RestController
    @RequestMapping("/api")
    public class ListController {
    
        // 使用 @ModelAttribute 接收表单数据
        @PostMapping("/form")
        public String handleModelAttribute(@ModelAttribute List<User> users) {
            return "Received " + users.size() + " users";
        }
    }
    
    • 测试路径和请求体:
    POST /api/form
    请求体中包含表单数据(使用application/x-www-form-urlencoded):
    userId=1&userId=2&userId=3
    或者,如果你使用JSON格式的请求体,你需要自定义一个包装类来接收这个列表。
    
  4. 使用自定义包装类
    • 对于复杂的数据结构,你可以创建一个自定义的包装类来接收列表。
    public class UserListWrapper {
        private List<User> users;
    
        // Getters and setters
        public List<User> getUsers() {
            return users;
        }
    
        public void setUsers(List<User> users) {
            this.users = users;
        }
    }
    
    @RestController
    @RequestMapping("/api")
    public class ListController {
    
        // 使用自定义包装类接收JSON数组
        @PostMapping("/custom")
        public String handleCustomList(@RequestBody UserListWrapper wrapper) {
            return "Received " + wrapper.getUsers().size() + " users";
        }
    }
    
    • 测试路径和请求体:
    POST /api/custom
    {
        "users": [
            {"name": "John Doe", "age": 30},
            {"name": "Jane Doe", "age": 25}
        ]
    }
    

原理分析

主要是DispatcherServlet 的doDispatch方法

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 获取当前请求的Handler
    MappedHandler mappedHandler = getHandler(processedRequest);
    if (mappedHandler == null) {
        noHandlerFound(processedRequest, response);
        return;
    }
    // 获取HandlerAdapter
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    // 执行前置拦截器
    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
        return;
    }
    // 调用Handler(包含参数解析器处理参数过程)
    ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

}

在handle方法内部,HandlerAdapter会使用一系列的参数解析器(HandlerMethodArgumentResolver)来处理Controller方法的参数。这些解析器负责从请求中提取数据,并将其转换为Controller方法所需的参数类型。

参数解析的主要代码位于RequestMappingHandlerAdapter的handleInternal方法中

protected ModelAndView handleInternal(HttpServletRequest request,
                                      HttpServletResponse response, 
                                      HandlerMethod handlerMethod) throws Exception {
    ModelAndView mav;
    checkRequest(request);
    // 如果synchronizeOnSession为true且能从request获取会话对象,则以同步方式执行handler
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        } else {
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    } else {
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }
    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        return null;
    }
    applyDefaultViewName(request, mav);
    MappedHandler mappedHandler = getHandlerExecutionChain(handlerMethod);
    mappedHandler.applyPostHandle(request, response, mav);
    return mav;
}

在invokeHandlerMethod方法中,会遍历所有的参数解析器,寻找能够解析当前参数的解析器,并将其缓存。然后,通过参数解析器将需要的参数从请求中获取出来,并进行类型转换和数据绑定。

public Object invokeHandlerMethod(HttpServletRequest request, 
                                  HttpServletResponse response, 
                                  HandlerMethod handlerMethod) throws Exception {
    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    invocableMethod.invokeAndHandle(request, response);
    return invocableMethod.getReturnValue();
}

在ServletInvocableHandlerMethod的invokeAndHandle方法中,会调用getMethodArgumentValues方法来获取参数值,这个方法会遍历所有的参数解析器,使用它们来解析参数。

public Object[] getMethodArgumentValues(NativeWebRequest request, 
                                         @Nullable ModelAndViewContainer mavContainer, 
                                         Object... providedArgs) throws Exception {
    MethodParameter[] parameters = getMethodParameters();
    if (ObjectUtils.isEmpty(parameters)) {
        return EMPTY_ARGS;
    }
    Object[] args = new Object[parameters.length];
    for (int i = 0; i < parameters.length; i++) {
        MethodParameter parameter = parameters[i];
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        args[i] = findProvidedArgument(parameter, providedArgs);
        if (args[i] == null) {
            HandlerMethodArgumentResolver resolver = 
                this.resolvers.resolveArgument(parameter, mavContainer, request, 
                                                  this.dataBinderFactory);
            args[i] = resolver.resolveArgument(parameter, mavContainer, request, 
                                                this.dataBinderFactory);
        }
    }
    return args;
}

在这个方法中,resolvers.resolveArgument会调用具体的参数解析器来解析参数。这些解析器主要有:

  • RequestParamMethodArgumentResolver:处理@RequestParam注解的参数。
  • PathVariableMethodArgumentResolver:处理@PathVariable注解的参数。
  • RequestBodyMethodArgumentResolver:处理@RequestBody注解的参数,通常用于接收JSON或XML格式的请求体。
  • RequestHeaderMethodArgumentResolver:处理@RequestHeader注解的参数。
  • CookieValueMethodArgumentResolver:处理@CookieValue注解的参数。
    每个解析器都实现了HandlerMethodArgumentResolver接口,它们负责从请求中提取数据,并将其转换为Controller方法所需的参数类型。

原文地址:https://blog.csdn.net/lixiaoyi01/article/details/142900148

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!