zoukankan      html  css  js  c++  java
  • 自己实现spring核心功能 三

    前言

     前两篇已经基本实现了spring的核心功能,下面讲到的参数绑定是属于springMvc的范畴了。本篇主要将请求到servlet后怎么去做映射和处理。首先来看一看dispatherServlet的基本流程,这我在以前的博客里面也讲过,传送门

    这里先给个我们的简易处理流程

    准备工作

     为了能将请求传递,我们需要写一个控制器类来接收请求,写两个接口来处理请求

    HomeController类
     1 @JCController
     2 @JCRequestMapping("/home")
     3 public class HomeController {
     4     @JCAutoWrited
     5     private IHomeService homeService;
     6 
     7     @JCRequestMapping("/sayHi")
     8     public String sayHi() {
     9         return homeService.sayHi();
    10     }
    11 
    12     @JCRequestMapping("/getName")
    13     public String getName(Integer id,String no) {
    14         return homeService.getName(id,no);
    15     }
    16     @JCRequestMapping("/getRequestBody")
    17     public String getRequestBody(Integer id, String no, GetUserInfo userInfo) {
    18         return homeService.getRequestBody(id,no,userInfo);
    19     }
    20 }
    View Code
    HomeService类
     1 @JCService
     2 public class HomeService  implements IHomeService{
     3 
     4     @JCAutoWrited
     5      StudentService studentService;
     6     @Override
     7     public String sayHi() {
     8       return   studentService.sayHi();
     9     }
    10 
    11     @Override
    12     public String getName(Integer id,String no) {
    13         return "SB0000"+id;
    14     }
    15 
    16     @Override
    17     public String getRequestBody(Integer id, String no, GetUserInfo userInfo) {
    18         return "userName="+userInfo.getName()+" no="+no;
    19     }
    20 }
    View Code
    StudentService类
    1 @JCService
    2 public class StudentService  implements IStudentService{
    3     @Override
    4     public String sayHi(){
    5         return "Hello world!";
    6     }
    7 }
    View Code

    无参请求过程

    根据上面的图,我们在浏览器发起请求localhost:8080/home/sayHi,请求会到达JCDispatherServlet类,由于我们是GET请求

     @Override
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         doPost(req, resp);
     }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    try {
    doDispatcherServlet(req, resp);
    } catch (Exception e) {
    e.printStackTrace();
    }

    }

    会走到doDispatcherServlet方法里面处理请求

     1     void doDispatcherServlet(HttpServletRequest req, HttpServletResponse resp) throws Exception {
     2         String url = req.getRequestURI();
     3         url = url.replace(req.getContextPath(), "").replaceAll("/+", "/");
     4         if (!urlMapping.containsKey(url)) {
     5             resp.getWriter().write("404! url is not found!");`
     6             return;
     7         }
     8 
     9         Method method = urlMapping.get(url);
    10         String className = 
    11         method.getDeclaringClass().getSimpleName();
    12         className = firstLowerCase(className);
    13         if (!ioc.containsKey(className)) {
    14             resp.getWriter().write("500! claas not defind !");
    15             return;
    16         }
    17         Object[] args=null ;
    18 
    19         //调用目标方法
    20         Object res = method.invoke(ioc.get(className), args);
    21 
    22         resp.setContentType("text/html;charset=utf-8");
    23         resp.getWriter().write(res.toString());
    24     }

    第九行代码会以url为key从HashMap取出数据,返回Method对象,它对应到我们在HomeController中定义的方法public String sayHi() 。

    public Object invoke(Object obj, Object... args) 

    通过反射调用方法,需要2个参数,第一个是方法所在类的对象,一个是方法所需要的参数。

    下面的代码就是在ioc容器中取HomeController类对象,如果没有,就抛500错误。

    Method method = urlMapping.get(url);
    String className = method.getDeclaringClass().getSimpleName();
    className = firstLowerCase(className);
    if (!ioc.containsKey(className)) {
    resp.getWriter().write("500! claas not defind !");
    return;
    }

    //调用目标方法
    Object res = method.invoke(ioc.get(className), null);
    resp.setContentType("text/html;charset=utf-8");
    resp.getWriter().write(res.toString());

    可以看到,无参请求args传过去null,然后把调用结果返回,浏览器打印结果,证明无参可以使用了。

    接下来就需要接收参数的传递了。

    参数传递

     我们常用参数传递大致分为三种,GET传参,POST方式form传参,POST方式json传参。

      前两种传参都可以通过HttpServletRequest的getParameterMap()获取到,它返回的是Map<String, String[]>结构,我们需要遍历map,拿到里面的key和值。

     @JCRequestMapping("/getName")
      public String getName(Integer id,String no) {
            return homeService.getName(id,no);
        }

    我们在浏览器请求输入http://localhost:8080/home/getName?id=11&no=lisi

    传过来的是这样一个数据,里面有字段名称,有字段的value

     而json格式的参数,需要从输入流里面获取 req.getInputStream() 

     知道了传进来的字段名称后,现在有2个问题,一个是方法参数的类型,一个是方法参数的顺序,这就涉及到了参数的绑定

    参数绑定

     什么叫参数绑定,举个例子

    从这个方法的声明,可以看到,第一个参数要求是名称为id且类型为Integer,第二个参数要求名称为no且类型为String。

    我们需要把request传进来的参数列表,按照方法的要求,一个一个传进去,不能少也不能类型错乱。

    要完成这个要求,我们首先需要获得方法的形参列表,其次要把参数按顺序,按类型给组装好。

    1.获取形参列表

    2.按要求组装好参数

    获取形参列表

    从这里可以看到 获取到的参数个数是正常的,类型也没有问题,但字段名称显然是错误的,咱们正确的字段名称应该是id、no

    通过网上可以查到,这个需要达到3个要求才能正常使用。

    1.jdk1.8 

    2.在idea设置参数 -parameters

    3.Build->RebuildProject

     设置方法博客地址

     还有个前提,每次JCDispatherServlet代码变更,需要重新编译项目Build->RebuildProject 生成最新的代码

     重新编译后,调试结果如下

    到此获取形参的工作已经做好了,只需要循环parameters数组就好了。

    类型转换

    不过还有个比较棘手的问题

    我们发现,从request获取到的实参都是String数组类型,需要根据形参转成指定类型,而且只能通过反射转换。

     所以一番折腾后,把入参转换成指定类型的实参的代码如下

     1   Object getInstanceField(Parameter parameter, String value) {
     2         if (parameter.getName().isEmpty()) {
     3             return null;
     4         }
     5         Class typeClass = parameter.getType();
     6         Constructor con = null;
     7         try {
     8             con = typeClass.getConstructor(value.getClass());
     9             return con.newInstance(value);
    10         } catch (InvocationTargetException e) {
    11             e.printStackTrace();
    12         } catch (IllegalAccessException e) {
    13             e.printStackTrace();
    14         } catch (InstantiationException e) {
    15             e.printStackTrace();
    16         } catch (NoSuchMethodException e) {
    17             e.printStackTrace();
    18         }
    19         return null;
    20     }
    21 
    22     Object[] doPostParam(HttpServletRequest req, Method method) {
    23         Parameter[] parameters = method.getParameters();
    24         Object[] requestParam = new Object[parameters.length];
    25         int i = 0;
    26         for (Parameter p : parameters) {
    27             requestParam[i] = null;
    28             if (!p.getName().isEmpty()) {
    29                 requestParam[i] = getInstanceField(p, req.getParameter(p.getName()));
    30             }
    31             i++;
    32         }
    33         return requestParam;
    34     }
    35 
    36     Object[] doJsonParam(String json, Method method) {
    37         if (null == json || json.isEmpty()) {
    38             return null;
    39         }
    40         Parameter[] parameters = method.getParameters();
    41         Object[] requestParam = new Object[parameters.length];
    42         JSONObject jsonObject = JSONObject.parseObject(json);
    43         int i = 0;
    44         for (Parameter p : parameters) {
    45             Object val = jsonObject.getObject(p.getName(), p.getType());
    46             requestParam[i] = val;
    47             i++;
    48         }
    49         return requestParam;
    50     }
    51 
    52     Object[] doGetParam(Map<String, String[]> map, Method method) {
    53         if (null == map || map.size() == 0) {
    54             return null;
    55         }
    56         Parameter[] parameters = method.getParameters();
    57         int i = 0;
    58         Object[] requestParam = new Object[parameters.length];
    59         for (Parameter p : parameters) {
    60             requestParam[i] = null;
    61             if (map.containsKey(p.getName())) {
    62                 String[] values = map.get(p.getName());
    63                 requestParam[i] = getInstanceField(p, values[0]);
    64             }
    65             i++;
    66         }
    67         return requestParam;
    68     }
    View Code

    返回Object[],里面的类型和顺序需要保证准确 

    浏览器调用结果Get请求

    也可以用PostMan 发起Post请求

     

     这两种都每办法传对象,而我们开发者需要传递对象。所以,再加个接口,测试包含对象时的混合绑定

    对象类型参数绑定

       @JCRequestMapping("/getRequestBody")
        public String getRequestBody(Integer id, String no, GetUserInfo userInfo) {
            return homeService.getRequestBody(id,no,userInfo);
        }

     
    doDispatcherServlet() 处理请求需要根据请求方式做不同处理,改造后如下
     1       Object[] args;
     2         if ("GET".equalsIgnoreCase(req.getMethod())) {
     3             args = doGetParam(req.getParameterMap(), method);
     4         } else if ("POST".equalsIgnoreCase(req.getMethod()) && req.getContentType().contains("json")) {
     5             String str = getJson(req);
     6             args = doJsonParam(str, method);
     7         } else {
     8             args = doPostParam(req, method);
     9         }
    10         //调用目标方法
    11         Object res = method.invoke(ioc.get(className), args);

    传对象只能通过json方式传进来,所以我们postMan请求json格式数据

    处理json请求

    请求地址 请求方式 请求参数
    http://localhost:8080/home/getRequestBody application/json {"id":11,"no":"SB00011","userInfo":{"name":"小提莫","age":20}}

     

    json请求核心代码就是使用fastjson根据字段名取值

     1     Object[] doJsonParam(String json, Method method) {
     2         if (null == json || json.isEmpty()) {
     3             return null;
     4         }
     5         Parameter[] parameters = method.getParameters();
     6         Object[] requestParam = new Object[parameters.length];
     7         JSONObject jsonObject = JSONObject.parseObject(json);
     8         int i = 0;
     9         for (Parameter p : parameters) {
    10             Object val = jsonObject.getObject(p.getName(), p.getType());
    11             requestParam[i] = val;
    12             i++;
    13         }
    14         return requestParam;
    15     }

     返回结果:

     结束

    到这里,servlet处理请求,并响应已经得到验证,能够正常的对外提供服务。一个微型的springMvc框架已经完成了。

    完整代码

  • 相关阅读:
    解决Mac开机变慢 command +option + P + R
    Mac iphone 使用 如何修改apple 用户名 XXX的mac Mac 与iphone如何连接 传递文件 为iphone增加铃声 iphone铃声的制作---城
    Xcode7 iOS9.0 的真机调试
    苹果电脑Mac OS系统重装图文详解
    关闭自动弹出照片自动弹出iTunes以及关闭手机照片流
    mac 使用清除废纸篓或彻底删除某文件 附加: smb afp ftp NAS 访问服务器相关
    辨别苹果数据线真伪 苹果计算器 Dashboard 知识
    使用OC swift 截取路径中的最后的文件名
    升级OS10.11系统后 Xcode6.4的变化少了个按钮 could not launch “Xcode” Xcode 插件安装
    如何在苹果官网下载旧版本的Xcode
  • 原文地址:https://www.cnblogs.com/jingch/p/11378510.html
Copyright © 2011-2022 走看看