设为首页 加入收藏

TOP

day07-SpringMVC底层机制简单实现-03(一)
2023-07-26 08:16:17 】 浏览:130
Tags:day07-SpringMVC 简单实 -03

SpringMVC底层机制简单实现-03

https://github.com/liyuelian/springmvc-demo.git

7.任务6-完成控制器方法获取参数-@RequestParam

功能说明:自定义 @RequestParam 注解和方法参数名获取参数。

当浏览器访问 Handler 方法时,如果 url 带有参数,可以通过自定义的 @RequestParam 注解来获取该参数,将其值赋给 Handler 方法中该注解修饰的形参。如:

url=http://ip:port/web工程路径/monster/find?name=孙悟空

@RequestMapping(value = "/monster/find")
public void findMonstersByName(HttpServletRequest request,HttpServletResponse response,
@RequestParam(value = "name") String username) {
    //注解的 value 值要和 url 的参数名一致
    //代码....
}

7.1分析

之前是通过自定义的前端控制器 MyDispatcherServlet 来完成分发请求:所有的请求都通过 doGet 和 doPost 来调用 executeDispatch() 方法,在 executeDispatch() 方法中,通过反射调用控制器的方法。

原先的 executeDispatch() 方法:

//编写方法,完成分发请求
private void executeDispatch(HttpServletRequest request, HttpServletResponse response) {
    MyHandler myHandler = getMyHandler(request);
    try {
        //如果 myHandler为 null,说明请求 url没有匹配的方法,即用户请求的资源不存在
        if (myHandler == null) {
            response.getWriter().print("<h1>404 NOT FOUND</h1>");
        } else {//匹配成功,就反射调用控制器的方法
            myHandler.getMethod().invoke(myHandler.getController(), request, response);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

但是由于 Handler 业务方法的形参个数、种类的不同,因此在反射的时候要考虑目标方法形参多种形式的问题。

Method 类的 invoke() 方法如下,它支持可变参数。

image-20230211225322448

因此解决办法是:将需要传递给目标方法的实参,封装到一个参数数组,然后以反射调用的方式传递给目标方法。

控制器方法用来接收前端数据的参数,除了request 和 response,其他参数一般都是使用 String 类型来接收的,因此目标方法形参可能有两种情况:

  1. HttpServletRequest 和 HttpServletResponse 参数
  2. 接收的是String类型的参数
    • 指定 @RequestParam 的 String 参数
    • 没有指定 @RequestParam 的 String 参数

因此需要将上述两种形参对应的实参分别封装到实参数组,进行反射调用:

怎么将需要传递给目标方法的实参,封装到一个参数数组?答:获取当前目标方法的所有形参信息,遍历这个形参数组,根据形参数组的下标索引,将实参填充到实参数组对应的下标索引中。

(1)将方法的 HttpServletRequest 和 HttpServletResponse 参数封装到参数数组

(2)将方法指定 @RequestParam 的 String 参数封装到参数数组

(3)将方法中没有指定 @RequestParam 的String 参数按照默认参数名封装到参数数组

7.2代码实现

(1)@RequestParam注解

package com.li.myspringmvc.annotation;

import java.lang.annotation.*;

/**
 * @author 李
 * @version 1.0
 * RequestParam 注解标注在目标方法的参数上,表示映射http请求的参数
 */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
    String value() default "";
}

(2)MyDispatcherServlet 中修改 executeDispatch() 方法,并增加两个方法 getIndexOfRequestParameterIndex() 和 getParameterNames()。

部分代码:

//编写方法,完成分发请求
private void executeDispatch(HttpServletRequest request, HttpServletResponse response) {
    MyHandler myHandler = getMyHandler(request);
    try {
        //如果 myHandler为 null,说明请求 url没有匹配的方法,即用户请求的资源不存在
        if (myHandler == null) {
            response.getWriter().print("<h1>404 NOT FOUND</h1>");
        } else {//匹配成功,就反射调用控制器的方法
            /**
             * 1.原先的写法为 myHandler.getMethod()
             *      .invoke(myHandler.getController(), request, response);
             *  它的局限性是目标方法只能有两个形参: HttPServletRequest 和 HttPServletResponse
             * 2.改进:将需要request的实参,封装到一个参数数组,然后以反射调用的方式传递给目标方法
             * 3.public Object invoke(Object obj, Object... args)
             */
            //1.先获取目标方法的所有形参的参数信息
            Class<?>[] parameterTypes = myHandler.getMethod().getParameterTypes();
            //2.创建一个参数数组(对应实参数组),在后面反射调动目标方法时会用到
            Object[] params = new Object[parameterTypes.length];
            //遍历形参数组 parameterTypes,根据形参数组的信息,将实参填充到实参数组中

            //步骤一:将方法的Request和Response参数封装到实参数组,进行反射调用
            for (int i = 0; i < parameterTypes.length; i++) {
                //取出当前的形参的类型
                Class<?> parameterType =
首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇《分布式技术原理与算法解析》学.. 下一篇day01-2-@RequestMapping和Rest

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目