手写springmvc

手写springmvc

既然已经手写了spring的IOC,那springmvc肯定也要尝试写写了。手写spring博客:https://www.cnblogs.com/xiaojiesir/p/11139203.html

SpringMVC的运行流程:

 

(1)首先浏览器发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;

(2)DispatcherServlet——>HandlerMapping,处理器映射器将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器对象、多个HandlerInterceptor拦截器)对象;

(3)DispatcherServlet——>HandlerAdapter,处理器适配器将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;

(4)HandlerAdapter——>调用处理器相应功能处理方法,并返回一个ModelAndView对象(包含模型数据、逻辑视图名);

(5)ModelAndView对象(Model部分是业务对象返回的模型数据,View部分为逻辑视图名)——> ViewResolver, 视图解析器将把逻辑视图名解析为具体的View;

(6)View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构;

(7)返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。

 

梳理SpringMVC的设计思路

在没有使用框架前,我们是使用servlet来实现前后端交互的,所以springmvc说到底还是在servlet基础上展开的。如果对servlet不是很熟悉的话,需要先学习下servlet。

框架只是方便开发,底层知识才是最重要的。在掌握底层知识的基础上,再学习框架的设计理念来成长自己。

Servlet 生命周期

Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:

  • Servlet 通过调用 init () 方法进行初始化。
  • Servlet 调用 service() 方法来处理客户端的请求。
  • Servlet 通过调用 destroy() 方法终止(结束)。
  • 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。

 

根据servlet生命周期来看,我们可以在init()方法中初始化基本的beans,并实例化controller层中的定义的service的变量,同时实现映射URL请求的Path和方法。

而service方法我们用doget方法和dopost方法。doPost()主要是实现参数的解析,并通过反射的机制实现方法的调用。

 

这里只粘贴了部分代码,具体代码可以看下:https://github.com/xiaojiesir/handwritingspringmvc

项目结构

配置web.xml

使用servlet前,我们需要再web.xml中配置以下代码

<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app><display-name>Archetype Created Web Application</display-name><servlet><servlet-name>mvc</servlet-name><servlet-class>com.springframework.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>application.properties</param-value></init-param><!-- Servlet 就会在服务器启动 时执行了。(注意:如果设置为负整数或者不配置,则不会在启动 服务器时执行,而要等此Servlet 被调用时才会被执行。 )--><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>mvc</servlet-name><url-pattern>/*</url-pattern></servlet-mapping></web-app>

<url-pattern>/*</url-pattern> 这种拦截匹配规则其实是一种错误方式,因为只是简易springmvc框架,不需要jsp,所以没问题。具体可以百度了解这方面知识。

实现init方法

    @Overridepublic void init(ServletConfig config) throws ServletException {System.out.println("init方法");//1.加载配置文件
        doLoadCongig(config.getInitParameter(LOCATION));//2.扫描所有相关的类doScanner(p.getProperty("scanPackage"));//3.初始化所有的相关类的实例,并保存到IOC容器中
        doInstance();//4.依赖注入
        doAutowired();//5.构造HandlerMapping
        initHandlerMapping();//6.等待请求,匹配URL,定位方法,反射调用执行//调用doGet或者doPost方法//提示信息System.out.println("my springmvc is success");}

init方法中的1.2.3.4可以参考之前手写spring中的代码,我详细讲解下5

第五步遍历容器中的bean,利用@MyRequestMapping注解将url与方法做映射

    private void initHandlerMapping() {if (ioc.isEmpty()) {return;}try {for (Entry<String, Object> entry : ioc.entrySet()) {Class<? extends Object> clazz = entry.getValue().getClass();if (!clazz.isAnnotationPresent(MyController.class)) {continue;}// 拼url时,是controller头的url拼上方法上的urlString baseUrl = "";if (clazz.isAnnotationPresent(MyRequestMapping.class)) {MyRequestMapping annotation = clazz.getAnnotation(MyRequestMapping.class);baseUrl = annotation.value();}Method[] methods = clazz.getMethods();for (Method method : methods) {if (!method.isAnnotationPresent(MyRequestMapping.class)) {continue;}MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);String url = annotation.value();url = (baseUrl + "/" + url).replaceAll("/+", "/");handlerMapping.put(url, method);System.out.println(url + "," + method);}}} catch (Exception e) {e.printStackTrace();}}

实现dopost方法

根据请求url,获取method,根据@MyRequestParam注解获取方法参数并赋值,利用反射执行方法

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// TODO Auto-generated method stubtry {System.out.println("doPost");doDispatch(req,resp);//未采用策略模式//doDispatchDesignPatterns(req,resp);//采用策略模式} catch (Exception e) {// TODO Auto-generated catch block
            e.printStackTrace();}
}

未采用策略模式,参数类型使用if else if判断类型,

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {// TODO Auto-generated method stubif(this.handlerMapping.isEmpty()){return;}String url =req.getRequestURI();//   /myspringmvc/demo/userString contextPath = req.getContextPath();//   /myspringmvcurl = url.replace(contextPath, "").replaceAll("/+", "/");if(!this.handlerMapping.containsKey(url)){resp.getWriter().write("404 Not Found!!");return;}Map<String,String[]> params = req.getParameterMap();Method method =this.handlerMapping.get(url);//获取方法的参数列表Class<?>[] paramerterTypes = method.getParameterTypes();//获取请求的参数Map<String,String[]> parameterMap  = req.getParameterMap();//保留参数值Object[] paramValues = new Object[paramerterTypes.length];//方法的参数列表for (int i = 0; i < paramerterTypes.length; i++) {//根据参数名称,做某些处理Class parameterType =paramerterTypes[i];if(parameterType == HttpServletRequest.class){//参数类型已明确,这边强转类型paramValues[i] =req;continue;}else if(parameterType == HttpServletResponse.class){paramValues[i] = resp;continue;}else if(parameterType == String.class){//获取当前方法的参数Annotation[][] an = method.getParameterAnnotations();//个数和paramerterTypes.length一样Annotation[] paramAns = an[i];for (Annotation paramAn : paramAns) {//判断传进的paramAn.getClass()是不是 MyRequestParam 类型if (MyRequestParam.class.isAssignableFrom(paramAn.getClass())) {MyRequestParam cr = (MyRequestParam) paramAn;String value = cr.value();paramValues[i] = req.getParameter(value);}}}}try {//String beanName = lowerFirstCase(method.getDeclaringClass().getSimpleName());//获取源代码中给出的‘底层类’简称String beanName = "/" + url.split("/")[1];method.invoke(this.ioc.get(beanName), paramValues);} catch (Exception e) {// TODO: handle exception
            e.printStackTrace();}}

采用策略模式

    private void doDispatchDesignPatterns(HttpServletRequest req, HttpServletResponse resp) {// 通过req获取请求的url /myspringmvc/demo/userString url = req.getRequestURI();// /myspringmvcString context = req.getContextPath();// /demo/userString path = url.replaceAll(context, "");// 通过当前的path获取handlerMap的方法名Method method = this.handlerMapping.get(path);// 获取beans容器中的beanObject instance = this.ioc.get("/" + path.split("/")[1]);// 处理参数HandlerAdapterService ha = (HandlerAdapterService) this.ioc.get("myHandlerAdapter"); Object[] args = ha.handle(req, resp, method, ioc);// 通过反射来实现方法的调用try {method.invoke(instance, args);} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}

处理参数接口

package com.xiaojiesir.demo.handlerAdapter;import java.lang.reflect.Method;
import java.util.Map;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public interface HandlerAdapterService {public Object[] handle(HttpServletRequest req, HttpServletResponse resp,Method method, Map<String, Object> beans);
}

处理参数的实现类

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import com.springframework.annotation.MyService;
import com.xiaojiesir.demo.argumentResolver.ArgumentResolver;@MyService("myHandlerAdapter")
public class MyHandlerAdapter implements HandlerAdapterService {@Overridepublic Object[] handle(HttpServletRequest req, HttpServletResponse resp, Method method, Map<String, Object> beans) {// TODO Auto-generated method stub//获取方法中含义的参数Class<?>[] paramClazzs = method.getParameterTypes();System.out.println("======当前需要解析的参数对应的类=========");for(Class<?> clazz: paramClazzs) {System.out.println(clazz);}// 定义一个返回参数的结果集Object[] args = new Object[paramClazzs.length];
//                Object[] args = {req, resp, "name", "xiaojiesir"};// 定义一个ArgumentResolver实现类的MapMap<String, Object> argumentResolvers = getBeansOfType(beans, ArgumentResolver.class);System.out.println("======当前需要解析的参数对应的类实例化=========");for(Map.Entry<String, Object> map: argumentResolvers.entrySet()) {System.out.println("key:" + map.getKey() + "; value:" + map.getValue());}//定义参数索引int paramIndex = 0;//定义数组下标索引int i = 0; // 开始处理参数for(Class<?> paramClazz: paramClazzs) {//哪个参数对应了哪个参数解析类,用策略模式来找for (Map.Entry<String, Object> entry : argumentResolvers.entrySet()) {ArgumentResolver ar = (ArgumentResolver)entry.getValue();if (ar.support(paramClazz, paramIndex, method)) {args[i++] = ar.argumentResolver(req,resp,paramClazz,paramIndex,method);}}paramIndex++;}return args;}/*** @param beans IOC容器中全部的bean* @param intfType 定义的ArgumentResolver类* @return*/private Map<String, Object> getBeansOfType(Map<String, Object> beans,Class<ArgumentResolver> intfType) {Map<String, Object> resultBeans = new HashMap<>();for(Map.Entry<String, Object> map: beans.entrySet()) {// 获取满足ArgumentResolver接口的beanClass<?>[] intfs = map.getValue().getClass().getInterfaces();if(intfs != null && intfs.length >0) {for(Class<?> intf: intfs) {// 将满足的bean存储在resultBeans中if(intf.isAssignableFrom(intfType)) {resultBeans.put(map.getKey(), map.getValue());}}}}return resultBeans;}}

参数接口

import java.lang.reflect.Method;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public interface ArgumentResolver {/*** 判断当前的类是继承于ArgumentResolver* @param type 当前参数注解的类对象* @param paramIndex 参数下标* @param method 当前的方法* @return*/public boolean support(Class<?> type, int paramIndex, Method method);/*** * @param request* @param response* @param type* @param paramIndex* @param method* @return*/public Object argumentResolver(HttpServletRequest request,HttpServletResponse response, Class<?> type, int paramIndex,Method method);
}

处理Request请求参数

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import com.springframework.annotation.MyService;/** 处理Request请求参数*/
@MyService("httpServletRequestArgumentResolver")
public class HttpServletRequestArgumentResolver implements ArgumentResolver {@Overridepublic boolean support(Class<?> type, int paramIndex, Method method) {return ServletRequest.class.isAssignableFrom(type);}@Overridepublic Object argumentResolver(HttpServletRequest request,HttpServletResponse response, Class<?> type, int paramIndex,Method method) {return request;}}

处理Response参数

import java.lang.reflect.Method;import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import com.springframework.annotation.MyService;
/** 处理Response参数*/
@MyService("httpServletResponseArgumentResolver")
public class HttpServletResponseArgumentResolver implements ArgumentResolver {@Overridepublic boolean support(Class<?> type, int paramIndex, Method method) {return ServletResponse.class.isAssignableFrom(type);}@Overridepublic Object argumentResolver(HttpServletRequest request,HttpServletResponse response, Class<?> type, int paramIndex,Method method) {return response;}}

处理自定义的参数

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import com.springframework.annotation.MyRequestParam;
import com.springframework.annotation.MyService;@MyService("requestParamArgumentResolver")
public class RequestParamArgumentResolver implements ArgumentResolver {@Overridepublic boolean support(Class<?> type, int paramIndex, Method method) {// type = class java.lang.String// @MyRequestParam("name")String name//获取当前方法的参数Annotation[][] an = method.getParameterAnnotations();Annotation[] paramAns = an[paramIndex];for (Annotation paramAn : paramAns) {//判断传进的paramAn.getClass()是不是 MyRequestParam 类型if (MyRequestParam.class.isAssignableFrom(paramAn.getClass())) {return true;}}return false;}@Overridepublic Object argumentResolver(HttpServletRequest request,HttpServletResponse response, Class<?> type, int paramIndex,Method method) {//获取当前方法的参数Annotation[][] an = method.getParameterAnnotations();Annotation[] paramAns = an[paramIndex];for (Annotation paramAn : paramAns) {//判断传进的paramAn.getClass()是不是 MyRequestParam 类型if (MyRequestParam.class.isAssignableFrom(paramAn.getClass())) {MyRequestParam cr = (MyRequestParam) paramAn;String value = cr.value();return request.getParameter(value);}}return null;}}

启动服务,在URL输入请求地址:

http://localhost:8080/myspringmvc/demo/user?name=xiaojiesir123&id=1

 

总结:

手写的spring和springmvc主要用了反射、注解。正式的spring中还有很多设计模式,值得我们学习。所以我们在掌握基础知识后,多读框架的源码,获取框架中的设计模式,设计理念更助于提升自己。

 

转载于:https://www.cnblogs.com/xiaojiesir/p/11157235.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/410832.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux可用机场客户端,Linux系统可用的6款Bittorrent客户端

大家都知道迅雷目前尚不支持Linux系统&#xff0c;其实使用Bittorrent客户端进行下载未尝不是一个好的选择&#xff0c;这里给大家介绍6款Linux可用Bittorrent客户端&#xff0c;方便经常需要进行文件下载的Linux用户。1.KtorrentKtorrent是KDE桌面环境默认安装的Bittorrent工具…

H3C 环路避免机制一:路由毒化

转载于:https://www.cnblogs.com/fanweisheng/p/11156838.html

.net自定义控件Control、WebControl、CompositeControl

.net自定义控件Control、WebControl、CompositeControl 一、呈现方法 1、Control主要有以下4个方法用于呈现 1 //该方法为入口方法2 public virtual void RenderControl (HtmlTextWriter writer) 3 { 4 this.RenderControl(writer,this.xxxAdapter); 5 } 6 7 p…

About Me

&#xff08;参考Matirx67大牛的格式&#xff09; 网名&#xff1a;Sephiroth Lee 年龄&#xff1a;不会算 生日&#xff1a;1994-1-20 性别&#xff1a;男 血型&#xff1a;不知道 星座&#xff1a;摩羯座 家乡&#xff1a;河北 学校&#xff1a;衡水中学 地址&#xff1a;衡水…

Java中连接池

最近在看书&#xff0c;其中有一段是&#xff1a; 相信有大佬已经能看得出来这是《企业IT架构转型之道》这本书了&#xff08;这是一本不错的书&#xff0c;推荐工作时长>2年的软件人员可以看看&#xff09;~~ 对于红色框内的那段文字&#xff0c;我有两个概念不是很明白&am…

C语言中 用选择结构编译算法,C语言程序设计立体化教程(高等教育立体化精品系列规划教材)...

导语内容提要李刚、唐炜主编的《C语言程序设计立体化教程(高等教育立体化精品系列规划教材)》主要分为四篇&#xff1a;语法基础篇、程序设计结构篇、初级应用篇和高级应用篇&#xff1b;其中第一篇语法基础部分介绍了C语言概述和C语言数据与运算&#xff1b;第二篇程序设计结构…

第二次实验报告(漏)

C程序设计实验报告 实验项目&#xff1a; 1.if语句的应用2.switch/case语句的应用3.switch/case语句嵌套if语句的应用4.switch/case结构的嵌套应用5.分析程序 姓名&#xff1a;王治林   实验地点&#xff1a;514教室   实验时间&#xff1a;2019.4.3 一、实验目的与要求 …

自我总结篇之vue的组件通信(父传子 子传父 非父子)

一&#xff1a;父传子 父组件代码如下&#xff1a; <template><div class"father"><child :messagemessage :message2message2></child> </div> </template> <script> import child from /components/child.vue export de…

浅谈“微服务”

微服务概述 1.1 易于扩展 1.2 部署简单 1.3 技术异构性 数据库的服务化切分 2.1 什么是“分库分表”&#xff1f; 2.2 数据库扩展的几种方式 2.3 分库分表的几种方式 2.4 引入分库分表中间件后面临的问题 2.5 现有分库分表中间件的横向对比 微服务架构中的分布式事务 3.1 什么…

liigo:爱可视70平板电脑使用感受,遗憾与满足并存

我想大部分人来这里&#xff0c;不是想听美言的。许多资料、宣传性文章、评测、视频等等&#xff0c;网络上已经有很多了&#xff08;其中外文占很大比例&#xff09;。 我想大部分人来这里&#xff0c;是想听真正的使用感受的。我想&#xff0c;我这里提到的许多内容&#xff…

visual studio 正则表达式 查找与替换文本

好多时候想要重构一些代码&#xff0c;但是修改起来发现很麻烦&#xff0c;因为简单的文本替换不能满足需求&#xff0c;这时候就要借助ide的力量了。还好visual studio 2010支持正则表达式查找和替换。如下图所示&#xff1a; document.all.domElementA.style.visibility hid…

51 Python - 装饰器 参数化装饰器——装饰器更通用

05参数化装饰器——装饰器更通用 参数化装饰器如何理解&#xff0c;简单理解就是让装饰器可以通用。场景举例&#xff0c;现在有个需求要改某一段文字&#xff0c;既要加<P>标签&#xff0c;又要加<B>&#xff0c;还有加<Div>。是不是意味着需要定义多个装饰…

python中numpy矩阵运算操作大全(非常全)!

python中numpy矩阵运算操作大全&#xff08;非常全&#xff09; //2019.07.10晚python矩阵运算大全1、矩阵的输出形式&#xff1a;对于任何一个矩阵&#xff0c;python输出的模板是&#xff1a;import numpy as np #引入numpy模块np1np.array([[1,2,3],[1,3,4],[1,6,2]...]) #数…

【摘录】MTK按键扫描原理及相关代码

一&#xff0e;MTK按键扫描原理&#xff1a; 不同的MTK平台内部提供的按键数目各有不同&#xff0c;但是扫描原理大致一样&#xff0c;这里以MTK6253为例来讲解记录。 MTK提供6*7共42个矩阵键盘&#xff0c;加上Power键总共43个按键&#xff0c;其中BB芯片引出6根行线ROW&#…

导致Android手机崩溃的壁纸,使用错误的壁纸会使你的Android手机崩溃

原标题&#xff1a;使用错误的壁纸会使你的Android手机崩溃现代智能手机是非常复杂的&#xff0c;能够显示大量的内容&#xff0c;并能浏览一系列复杂的色域、文件格式和媒体类型。然而&#xff0c;其中一些功能会以意想不到的方式影响到正常使用。Twitter用户Ice Universe发现…

android app逆向分析,如何开始对Android应用的逆向分析?

本文是我的关于如何开始Android逆向系列文章的第一部分。在文末提供了一个文档&#xff0c;你可以根据该文档说明部署同我一样的实验环境。在了解android应用的逆向之前&#xff0c;你必须对android平台及其架构和android应用程序的构建块(组件)已有了一个较好的理解。如果你并…

Swipper.js实现轮播功能

我是歌谣 放弃很难 但是坚持一定很酷 微信公众号关注小歌谣 今天我们来说一下工作中的一个小需求 就是给我们的页面去实现一个轮播的功能 最近需要实现一个小的需求 就是如何类似于如何把一个图片变成一个轮播效果 于是乎就开始考虑 用一款插件去实现这个功能 所以选择了一…

Spring.NET 1.3.1 正式版已发布

Spring.NET 1.3.1 下载地址为http://www.springframework.net/download.html 正如已经提到的其他地方&#xff0c;这将是Spring.NET的最终版本提供支持的。NET1.x中Spring.NET的未来版本将只针对。NET 2.0和更高版本&#xff0c;让Spring.NET更积极地利用近期功能的更多信息在。…

android wifi连接手机,Android手机无线连接利器-AirDroid

AirDroid是一款可以在电脑的浏览器上对手机进行管理的应用&#xff0c;需要wifi网络支持&#xff0c;手机安装启用服务后&#xff0c;在pc的浏览器即可登陆进行管理和操作&#xff0c;可以管理联系人、短信、文件、应用、照片、铃声、音乐、通话记录&#xff0c;还可以快速搜索…

RFC函数的初步使用-同步

1、由于没有外围系统&#xff0c;采用不同SAP不同client之间进行测试。 首先在A-client搭建需要被调用的RFC函数。在A-client里运行SE37创建函数 在属性页签选择“远程启用的模块” 设定inport参数&#xff0c;传入人员名称去取usr21中的值 设定export参数&#xff0c;其中zper…