自己实现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 }

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 }

StudentService类

1 @JCService
2 public class StudentService  implements IStudentService{
3     @Override
4     public String sayHi(){
5         return "Hello world!";
6     }
7 }

无参请求过程

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

 @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     doPost(req, resp);
 }
@Overrideprotected 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     }

返回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框架已经完成了。

完整代码

原文地址:https://www.cnblogs.com/jingch/p/11378510.html

时间: 2024-08-01 17:06:38

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

Spring 核心功能演示

Spring 核心功能演示 Spring Framework 简称 Spring,是 Java 开发中最常用的框架,地位仅次于 Java API,就连近几年比较流行的微服务框架 SpringBoot,也是基于 Spring 实现的,SpringBoot 的诞生是为了让开发者更方便地使用 Spring,因此 Spring 在 Java 体系中的地位可谓首屈一指. 当然,如果想要把 Spring 所有功能都讲的一清二楚,远远不是一两篇文章能够做到的,但幸运的是,Spring 的基础资料可以很轻易的搜

关于Spring核心容器功能

一句话总结:Spring框架的核心容器能为我们创建对象 spring框架是一种粘合层框架,它是用来整合web层框架和持久层框架的spring可以支持我们各个层级包括数据展示层,业务层和持久层都有对应的解决方案.spring核心容器core container提供了创建对象管理对象的功能core container包括bean,core,context,Expression Languagebean提供了整个框架最基本的部分,包括了IOC控制反转和依赖注入Bean的创建用到了工厂模式:父类是Bea

阿里P7级架构师总结Spring核心模块及功能汇总

如果你在使用Spring,而且没有使用SpringBoot,那么每个Spring的功能都需要引入相应的jar包依赖.而Spring的jar包依赖又有一二十个,很容易混淆,造成编译或运行错误. 下面我们就整理一下Spring3和Spring4的核心模块和对应的jar包,方便我们在具体使用的过程中更加清晰的了解到我们都需要什么. 与Spring3相比去掉了Struts,新增了Messaging和Websocket. 分析上面的框架结构图,大概包括以下模块和jar包依赖. 核心容器(Core Cont

Spring核心学习(1)实现基本的容器-包括注入和获取功能

前导:开始学习Spring核心思想,通过一个山寨精简版Spriing代码结合学习. 内容:1. BeanDefinition-保存Bean及配置信息 2. BeanFactory-对Bean进行管理. BeanDefinition: public class BeanDefinition { private Object bean; public BeanDefinition(Object object) { this.bean = object; } public Object getBean

Spring Data 系列(三) Spring+JPA(spring-data-commons)

本章是Spring Data系列的第三篇.系列文章,重点不是讲解JPA语法,所以跑开了JPA的很多语法等,重点放在环境搭建,通过对比方式,快速体会Spring 对JPA的强大功能. 准备代码过程中,保持了每个例子的独立性,和简单性,准备的源码包,下载即可使用.如果,对JPA语法想深入研究的话,直接下载在此基础上进行测试. 前言 Spring Data 系列(一) 入门:简单介绍了原生态的SQL使用,以及JdbcTemplate的使用,在这里写SQL的活还需要自己准备. Spring Data 系

Spring核心知识点

目录 Spring概述 依赖注入 Spring Beans Spring注解 Spring的对象访问 Spring面向切面编程 Spring MVC框架 Spring概述 1.什么是Spring? Spring是一个开源的Java EE开发框架.Spring框架的核心功能可以应用在任何Java应用程序中,但对Java EE平台上的Web应用程序有更好的扩展性.Spring框架的目标是使得Java EE应用程序的开发更加简捷,通过使用POJO为基础的编程模型促进良好的编程风格. 2.Spring有

第一章 spring核心概念

一.Spring作用:管理项目中各种业务Bean(service类.Dao类.Action类),实例化类,属性赋值 二.Spring IOC(Inversion of Control )控制反转,也被称为依赖注入(Dependency Injection[DI]),是面向对象编程 中的一种设计理念,用来减轻程序代码之间的耦合度. IOC原理:侧重原理 在业务代码中不使用我们之间司空见惯的关键字new来构建一个业务实例,而是在配置文件中.通过xml节点来告知容器如何对内存中构建的对应类型的对象名称

第5章 Spring Boot 功能

Spring Boot 功能 本节将会介绍Spring Boot的一些细节. 在这里,您可以了解您将要使用和自定义的主要功能. 如果还没有准备好,您可能需要阅读第二部分“入门指南”和第三部分“使用 Spring Boot”部分,以使您有基础的良好基础. 23. SpringApplication SpringApplication类提供了一种方便的方法来引导将从main()方法启动的Spring应用程序. 在许多情况下,您只需委派静态SpringApplication.run()方法: publ

Spring实战笔记:Spring核心(二)

spring core 面向切面的Spring 1.AOP:面向切面编程 在OOP中模块化的关键单元是类(classes),而在AOP中模块化的单元则是切面. AOP框架是Spring的一个重要组成部分.但是Spring IoC容器并不依赖于AOP,这意味着你有权利选择是否使用AOP,AOP做为Spring IoC容器的一个补充,使它成为一个强大的中间件解决方案. AOP在Spring Framework中的作用 提供声明式企业服务,特别是为了替代EJB声明式服务.最重要的服务是声明性事务管理.