前言

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

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

准备工作

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

HomeController类
 @JCController
@JCRequestMapping("/home")
public class HomeController {
@JCAutoWrited
private IHomeService homeService; @JCRequestMapping("/sayHi")
public String sayHi() {
return homeService.sayHi();
} @JCRequestMapping("/getName")
public String getName(Integer id,String no) {
return homeService.getName(id,no);
}
@JCRequestMapping("/getRequestBody")
public String getRequestBody(Integer id, String no, GetUserInfo userInfo) {
return homeService.getRequestBody(id,no,userInfo);
}
}
HomeService类
 @JCService
public class HomeService implements IHomeService{ @JCAutoWrited
StudentService studentService;
@Override
public String sayHi() {
return studentService.sayHi();
} @Override
public String getName(Integer id,String no) {
return "SB0000"+id;
} @Override
public String getRequestBody(Integer id, String no, GetUserInfo userInfo) {
return "userName="+userInfo.getName()+" no="+no;
}
}
StudentService类
 @JCService
public class StudentService implements IStudentService{
@Override
public String sayHi(){
return "Hello world!";
}
}

无参请求过程

根据上面的图,我们在浏览器发起请求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方法里面处理请求

     void doDispatcherServlet(HttpServletRequest req, HttpServletResponse resp) throws Exception {
String url = req.getRequestURI();
url = url.replace(req.getContextPath(), "").replaceAll("/+", "/");
if (!urlMapping.containsKey(url)) {
resp.getWriter().write("404! url is not found!");`
return;
} 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[] args=null ; //调用目标方法
Object res = method.invoke(ioc.get(className), args); resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write(res.toString());
}

第九行代码会以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数组类型,需要根据形参转成指定类型,而且只能通过反射转换。

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

   Object getInstanceField(Parameter parameter, String value) {
if (parameter.getName().isEmpty()) {
return null;
}
Class typeClass = parameter.getType();
Constructor con = null;
try {
con = typeClass.getConstructor(value.getClass());
return con.newInstance(value);
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
} Object[] doPostParam(HttpServletRequest req, Method method) {
Parameter[] parameters = method.getParameters();
Object[] requestParam = new Object[parameters.length];
int i = 0;
for (Parameter p : parameters) {
requestParam[i] = null;
if (!p.getName().isEmpty()) {
requestParam[i] = getInstanceField(p, req.getParameter(p.getName()));
}
i++;
}
return requestParam;
} Object[] doJsonParam(String json, Method method) {
if (null == json || json.isEmpty()) {
return null;
}
Parameter[] parameters = method.getParameters();
Object[] requestParam = new Object[parameters.length];
JSONObject jsonObject = JSONObject.parseObject(json);
int i = 0;
for (Parameter p : parameters) {
Object val = jsonObject.getObject(p.getName(), p.getType());
requestParam[i] = val;
i++;
}
return requestParam;
} Object[] doGetParam(Map<String, String[]> map, Method method) {
if (null == map || map.size() == 0) {
return null;
}
Parameter[] parameters = method.getParameters();
int i = 0;
Object[] requestParam = new Object[parameters.length];
for (Parameter p : parameters) {
requestParam[i] = null;
if (map.containsKey(p.getName())) {
String[] values = map.get(p.getName());
requestParam[i] = getInstanceField(p, values[0]);
}
i++;
}
return requestParam;
}

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

浏览器调用结果Get请求

也可以用PostMan 发起Post请求

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

对象类型参数绑定

   @JCRequestMapping("/getRequestBody")
public String getRequestBody(Integer id, String no, GetUserInfo userInfo) {
return homeService.getRequestBody(id,no,userInfo);
}
 
doDispatcherServlet() 处理请求需要根据请求方式做不同处理,改造后如下
       Object[] args;
if ("GET".equalsIgnoreCase(req.getMethod())) {
args = doGetParam(req.getParameterMap(), method);
} else if ("POST".equalsIgnoreCase(req.getMethod()) && req.getContentType().contains("json")) {
String str = getJson(req);
args = doJsonParam(str, method);
} else {
args = doPostParam(req, method);
}
//调用目标方法
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根据字段名取值

     Object[] doJsonParam(String json, Method method) {
if (null == json || json.isEmpty()) {
return null;
}
Parameter[] parameters = method.getParameters();
Object[] requestParam = new Object[parameters.length];
JSONObject jsonObject = JSONObject.parseObject(json);
int i = 0;
for (Parameter p : parameters) {
Object val = jsonObject.getObject(p.getName(), p.getType());
requestParam[i] = val;
i++;
}
return requestParam;
}

返回结果:

结束

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

完整代码

自己实现spring核心功能 三的更多相关文章

  1. Spring 核心功能演示

    Spring 核心功能演示 Spring Framework 简称 Spring,是 Java 开发中最常用的框架,地位仅次于 Java API,就连近几年比较流行的微服务框架 SpringBoot, ...

  2. 自己实现spring核心功能 一

    聊聊spring spring对于java开发者来说,是最熟悉不过的框架了,我们日常开发中每天都在使用它.它有着各种各样的好处,简单易用,得心应手... ... 我们一说到spring就会讲到ioc ...

  3. 自己实现spring核心功能 二

    前言 上一篇我们讲了spring的一些特点并且分析了需要实现哪些功能,已经把准备工作都做完了,这一篇我们开始实现具体功能. 容器加载过程 我们知道,在spring中refesh()方法做了很多初始化的 ...

  4. spring 核心

    1 Spring 1.1 专业术语了解 1.1.1 组件/框架设计 侵入式设计 引入了框架,对现有的类的结构有影响:即需要实现或继承某些特定类. 例如:     Struts框架 非侵入式设计 引入了 ...

  5. 读源码之Spring 核心内容

    为什么有这篇文档 工作两三年之后,总感觉什么东西都懂,但是什么东西又都不会.所以日常学习是很有必要的,俗话说学而不思则罔 ,思而不学则殆.所以我们要学思结合,学习的方法有很多,但是思考的深度或者说有没 ...

  6. Spring框架的IOC核心功能快速入门

    2. 步骤一:下载Spring框架的开发包 * 官网:http://spring.io/ * 下载地址:http://repo.springsource.org/libs-release-local/ ...

  7. Spring框架的核心功能之AOP技术

     技术分析之Spring框架的核心功能之AOP技术 AOP的概述        1. 什么是AOP的技术?        * 在软件业,AOP为Aspect Oriented Programming的 ...

  8. Spring 框架的核心功能之AOP技术

    1. AOP 的概述 AOP, Aspect Oriented Programming, 面向切面编程; 通过预编译方式和运行期动态代理实现程序功能的统一维护的技术; AOP 采取横向抽取机制,取代了 ...

  9. 第一章 spring核心概念

    一.Spring作用:管理项目中各种业务Bean(service类.Dao类.Action类),实例化类,属性赋值 二.Spring IOC(Inversion of Control )控制反转,也被 ...

随机推荐

  1. Java编程思想:File类getCanonicalFile()方法,listFile()方法

    import java.io.IOException; import java.util.*; import java.util.regex.Pattern; import java.io.File; ...

  2. nuxt项目打包上线,以及nuxt项目基础代码分享

    nuxt的项目部署到线上环境,有多种方法,这里分享我使用的方法,一步步照着配置,就可以配置成功~ (1)项目先执行npm run build 打包好   (2)服务器安装node 和pm2依赖 服务器 ...

  3. //Thread::Stop();

    //Thread::Stop(); Thread::StopSoon();

  4. 个人永久性免费-Excel催化剂功能第82波-复制粘贴按源区域大小自动扩展收缩目标区域

    日常工作中,复制粘贴的操作,永远是最高频的操作,没有之一,在最高频的操作上,进行优化,让过程更智能,比一天到晚鼓吹人工智能替换人的骇人听闻的新闻来得更实际.此篇带来一点点的小小的改进,让日后无数的复制 ...

  5. springboot3(目录结构)

    2.springboot中的目录结构 1.基本信息 - static :保存所有的静态资源,css.js.img - templates :保存所有的模版页面(springboot内嵌tomcat,默 ...

  6. 庖丁解牛Linux内核分析 0x00:《庖丁解牛》

    庖丁解牛  吾生也有涯,而知也无涯 .以有涯随无涯,殆已!已而为知者,殆而已矣!为善无近名,为恶无近刑.缘督以为经,可以保身,可以全生,可以养亲,可以尽年. 庖丁为文惠君解牛,手之所触,肩之所倚,足之 ...

  7. TensorFlow笔记-图片读取

    回到上一篇文件的读取分这么几步: # 构造队列 # 1,构造图片文件的队列 file_queue = tf.train.string_input_producer(filelist) # 构造阅读器 ...

  8. 钉钉E应用(小程序)之日历

    唠叨几句:其实钉钉E应用的编写类似支付宝小程序(毕竟是阿里爸爸下的产业),而支付宝小程序又是chao xi 微信小程序(只不过人家是wxml / wxss ,他是 axml / acss罢了),这三者 ...

  9. Linux学习(一)--VMware下Linux安装和配置

    本片随便将给大家讲述linux在VM虚拟机上安装及终端的安装和配置 一.Linux介绍 Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户.多任务.支持多线 ...

  10. Spark Streaming自定义Receiver

    一 背景 Spark社区为Spark Streaming提供了很多数据源接口,但是有些比较偏的数据源没有覆盖,由于公司技术栈选择,用了阿里云的MQ服务ONS,要做实时需求,要自己编写Receiver ...