js如何在当前页面加载springmvc返回的页面_手写SpringMVC学习

前面我们学习了spring框架源码,做了一些自己手写的学习,最近,我们开始学习springMVC框架的学习 ,springMVC框架,相信大家不陌生了,所以这里不做过多的介绍了。

SpringMVC以DispatcherServlet为核心,负责协调和组织不同组件以完成请求处理并返回响应的工作,实现了MVC模式。想要实现自己的SpringMVC框架,需要从以下几点入手:

1、了解SpringMVC运行流程及九大组件

2、梳理自己的SpringMVC的设计思路

3、实现自己的SpringMVC框架

一、SpringMVC流程图

532f86f6878451498f1acc51a0728701.png

二、SpringMVC流程

1、 用户发送请求至前端控制器DispatcherServlet。

2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。

3、 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

4、 DispatcherServlet调用HandlerAdapter处理器适配器。

5、 HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。

6、 Controller执行完成返回ModelAndView。

7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。

8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器。

9、 ViewReslover解析后返回具体View。

10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。

11、 DispatcherServlet响应用户。

三、SpringMVC的九大组件

protected void initStrategies(ApplicationContext context) {
//用于处理上传请求。处理方法是将普通的request包装成MultipartHttpServletRequest,后者可以直接调用getFile方法获取File.
initMultipartResolver(context);
//SpringMVC主要有两个地方用到了Locale:一是ViewResolver视图解析的时候;二是用到国际化资源或者主题的时候。
initLocaleResolver(context);
//用于解析主题。SpringMVC中一个主题对应一个properties文件,里面存放着跟当前主题相关的所有资源、
//如图片、css样式等。SpringMVC的主题也支持国际化,
initThemeResolver(context);
//用来查找Handler的。
initHandlerMappings(context);
//从名字上看,它就是一个适配器。Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。
//如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这就是HandlerAdapter要做的事情
initHandlerAdapters(context);
//其它组件都是用来干活的。在干活的过程中难免会出现问题,出问题后怎么办呢?
//这就需要有一个专门的角色对异常情况进行处理,在SpringMVC中就是HandlerExceptionResolver。
initHandlerExceptionResolvers(context);
//有的Handler处理完后并没有设置View也没有设置ViewName,这时就需要从request获取ViewName了,
//如何从request中获取ViewName就是RequestToViewNameTranslator要做的事情了。
initRequestToViewNameTranslator(context);
//ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。
//View是用来渲染页面的,也就是将程序返回的参数填入模板里,生成html(也可能是其它类型)文件。
initViewResolvers(context);
//用来管理FlashMap的,FlashMap主要用在redirect重定向中传递参数。
initFlashMapManager(context);
}

四、SpringMVC核心组件讲解

1、前端控制器DispatcherServlet
作用:接收请求,响应结果,相当于转发器,中央处理器。有了dispatcherServlet减少了其它组件之间的耦合度。
用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。

2、处理器映射器HandlerMapping
作用:根据请求的url查找Handler
HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

3、处理器适配器HandlerAdapter
作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

4、处理器Handler
Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
由于Handler涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发Handler。

5、视图解析器View resolver(不需要工程师开发),由框架提供
作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)
View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。 springmvc框架提供了很多的View视图类型,包括:jstlView、freemarkerView、pdfView等。
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由工程师根据业务需求开发具体的页面。

6、视图View
View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf...)

五、手写springMvc框架思路:

1、配置web.xml,加载自定义的DispatcherServlet。

2、初始化阶段,在DispatcherServlet类中,实现下面几个步骤:

    1. 加载配置类。
    2. 扫描当前项目下的所有文件。
    3. 拿到扫描到的类,通过反射机制,实例化。并且放到ioc容器中。
    4. 初始化path与方法的映射。
    5. 获取请求传入的参数并处理参数通过初始化好的handlerMapping中拿出url对应的方法名,反射调用。

3、运行阶段,每一次请求将会调用doGet或doPost方法,它会根据url请求去HandlerMapping中匹配到对应的Method,然后利用反射机制调用Controller中的url对应的方法,并得到结果返回。

六、代码实现

在SpringMVC的框架实现中,少不了一些基本的注入类,例如:@Controller、@RequestMapping、@RequestParam等等,在上一篇文章中,根据需要实现的代码块,需要自定义一些注解类:

@MyController

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {String value() default "";

@MyRequestMapping

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {String value() default "";
}

@MyAutowired

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowired {boolean required() default true;
}

@MyRequestParam

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestParam {String value() default "";
}

@MyService

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyService {String value() default "";
}

web.xm加载

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"><display-name>spring_mvc</display-name><servlet><servlet-name>DispatcherServlet</servlet-name><servlet-class>com.spring.dispathcer.DispatcherServlet</servlet-class></servlet><servlet-mapping><servlet-name>DispatcherServlet</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
</web-app>

ClassUtils工具类封装

public class ClassUtils {/*** 从包package中获取所有的Class** @param pack* @return*/public static List<Class<?>> getClasses(String packageName) {// 第一个class类的集合List<Class<?>> classes = new ArrayList<Class<?>>();// 是否循环迭代boolean recursive = true;// 获取包的名字 并进行替换String packageDirName = packageName.replace('.', '/');// 定义一个枚举的集合 并进行循环来处理这个目录下的thingsEnumeration<URL> dirs;try {dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);// 循环迭代下去while (dirs.hasMoreElements()) {// 获取下一个元素URL url = dirs.nextElement();// 得到协议的名称String protocol = url.getProtocol();// 如果是以文件的形式保存在服务器上if ("file".equals(protocol)) {// 获取包的物理路径String filePath = URLDecoder.decode(url.getFile(), "UTF-8");// 以文件的方式扫描整个包下的文件 并添加到集合中findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);} else if ("jar".equals(protocol)) {// 如果是jar包文件// 定义一个JarFileJarFile jar;try {// 获取jarjar = ((JarURLConnection) url.openConnection()).getJarFile();// 从此jar包 得到一个枚举类Enumeration<JarEntry> entries = jar.entries();// 同样的进行循环迭代while (entries.hasMoreElements()) {// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件JarEntry entry = entries.nextElement();String name = entry.getName();// 如果是以/开头的if (name.charAt(0) == '/') {// 获取后面的字符串name = name.substring(1);}// 如果前半部分和定义的包名相同if (name.startsWith(packageDirName)) {int idx = name.lastIndexOf('/');// 如果以"/"结尾 是一个包if (idx != -1) {// 获取包名 把"/"替换成"."packageName = name.substring(0, idx).replace('/', '.');}// 如果可以迭代下去 并且是一个包if ((idx != -1) || recursive) {// 如果是一个.class文件 而且不是目录if (name.endsWith(".class") && !entry.isDirectory()) {// 去掉后面的".class" 获取真正的类名String className = name.substring(packageName.length() + 1, name.length() - 6);try {// 添加到classesclasses.add(Class.forName(packageName + '.' + className));} catch (ClassNotFoundException e) {e.printStackTrace();}}}}}} catch (IOException e) {e.printStackTrace();}}}} catch (IOException e) {e.printStackTrace();}return classes;}/*** 以文件的形式来获取包下的所有Class** @param packageName* @param packagePath* @param recursive* @param classes*/public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,List<Class<?>> classes) {// 获取此包的目录 建立一个FileFile dir = new File(packagePath);// 如果不存在或者 也不是目录就直接返回if (!dir.exists() || !dir.isDirectory()) {return;}// 如果存在 就获取包下的所有文件 包括目录File[] dirfiles = dir.listFiles(new FileFilter() {// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)@Overridepublic boolean accept(File file) {return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));}});// 循环所有文件for (File file : dirfiles) {// 如果是目录 则继续扫描if (file.isDirectory()) {findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,classes);} else {// 如果是java类文件 去掉后面的.class 只留下类名String className = file.getName().substring(0, file.getName().length() - 6);try {// 添加到集合中去classes.add(Class.forName(packageName + '.' + className));} catch (ClassNotFoundException e) {e.printStackTrace();}}}}
}

未完待续。。。。

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

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

相关文章

用python做简单的地理聚类分析案例_用Python做一个简单的翻译工具

编程本身是跟年龄无关的一件事&#xff0c;不论你现在是十四五岁&#xff0c;还是四五十岁&#xff0c;如果你热爱它&#xff0c;并且愿意持续投入其中&#xff0c;必定会有所收获。本文就来自编程教室一位“小”读者的投稿&#xff08;互助学习1群里的同学应该对作者的名字很熟…

echarts柱图根据值显示不同颜色_视频 | Origin画3D柱图,这篇讲透了!

视频教程东华大学的汪博士提出一个问题&#xff1a;怎样画三维柱状图。汪博士提供了一篇王中林院士的文献图&#xff0c;画一个只有四根柱子的三维柱图。画了一个草图&#xff0c;A0、A1安排在第二行&#xff0c;A3、A2安排在第一行。相信很多同学在画3D柱图时&#xff0c;都很…

javaScript基本操作

反向字符串 const stringReverse str > str.split("").reverse( ).join("");stringReverse(hello world); /*dlrow olleh*/ 滚动到页面顶部 const scrollToTop ( ) > window.scrollTo(0, 0);scrollToTop( ); 删除数组中的重复项 const remo…

按钮点击打开新页面_PDF怎么打开?如何制作一个PDF格式的文档?

不知你是否也一样&#xff0c;无论是在网上下载资料还是其他人发送的文件都是PDF格式的。但是应该如何打开PDF文件呢&#xff1f;如何自己制作一个PDF格式的文档呢&#xff1f;首先说一下如何打开PDF格式的文件&#xff0c;电脑端就比较简单的&#xff0c;直接下载PDF阅读器后&…

YOLOv8 如何进行多任务合并:分割与检测合并进行自动驾驶

文章大纲 多任务的合并:分割与检测合并进行自动驾驶Update:The Illustration of A-YOLOMContributionsResultsParameters and speedTraffic Object Detection ResultDrivable Area Segmentation ResultLane Detection Result:Ablation Studies 1: Adaptive concatenation modu…

android 根据bounds坐标进行点击操作_炫酷的Android时钟UI控件,隔壁产品都馋哭了...

废话不多说&#xff0c;先上效果效果酷炫&#xff0c;动画丰富&#xff0c;效果爆炸boom&#xff5e;设计思路看腻了市面上各种丑陋难看的时钟控件&#xff0c;是时候整点新活&#xff01;将现实生活中的摆钟圆形表盘设计、电子手表的数显表盘设计抽象出来&#xff0c;提取出“…

打了断点为直接运行完_BBC主持人多次打断,香港大律师忍不住发飙

来源:环球网7月7日&#xff0c;香港资深大律师汤家骅就香港《国安法》相关内容接受BBC栏目《唇枪舌剑》(HARDtalk)采访。节目主持人斯蒂芬•萨克在整个访问过程中频频打断汤家骅&#xff0c;根本不让汤家骅回答完问题。视频显示&#xff0c;汤家骅多次试图向萨克解释《国安法》…

指针 是否相同_算法一招鲜——双指针问题

什么是双指针&#xff08;对撞指针、快慢指针&#xff09;双指针&#xff0c;指的是在遍历对象的过程中&#xff0c;不是普通的使用单个指针进行访问&#xff0c;而是使用两个相同方向&#xff08;快慢指针&#xff09;或者相反方向&#xff08;对撞指针&#xff09;的指针进行…

python字符串添加成员_Python - 字符串的操作方法

字符串操作方法生成字符串str Python string Function studysequence类型都支持的一些通用操作&#xff1a;成员检查&#xff1a;in、not in Py in str python not in str连接&#xff1a; str_new str1 str2复制&#xff1a;* str * 2下标取值&#xff1a;s[i] str[3]切片&…

作为神经网络的输入_MATLAB实战|基于神经网络河南省降水量预测

1 BP神经网络结构神经网络旨在通过模仿动物的神经系统利用神经元作为连接结点的新型智能算法&#xff0c;神经网络本身包含三层结构&#xff0c;输入层&#xff0c;隐含层&#xff0c;输出层&#xff0c;每一层都有自己的特殊功能&#xff0c;输入层进行因子的输入与处理。由于…

docker kafka互通有问题_Docker搭建kafka集群

拉取镜像docker pull wurstmeister/kafka docker pull wurstmeister/zookeeper启动镜像docker run --name zookeeper -p 12181:2181 -d wurstmeister/zookeeper:latestdocker run -p 19092:9092 --name kafka1 -d -e KAFKA_BROKER_ID0 -e KAFKA_ZOOKEEPER_CONNECT宿主机ip:1218…

示坡线高程判断_一步一步教你识别地形图

(五)地貌的表示方法地球表面是起伏不平的&#xff0c;有高山&#xff0c;有深海&#xff0c;有丘陵和平原&#xff0c;有沙漠和草原&#xff0c;还有江河和湖泊等等&#xff0c;这些高低不平&#xff0c;形状各异的地貌是怎样表示在平面图纸上的呢?地貌的表示方法&#xff0c;…

html 图片 高度无效_HTML笔记(详细)

HTML的标签分类双标签&#xff1a;如<strong>标签的内容</strong> 语法&#xff1a;<开始标签> 标签内容 </结束标签><strong>我要变粗</strong> <head></head>...... 单标签&#xff1a;<br/>单标签都是功能性的标签&…

elementui 上传七牛_element ui使用上传组件上传文件到七牛(qiniu-js)

博主正在重构博客中&#xff0c;刚开始时静态资源都是上传到本地服务器的&#xff0c;但这个项目博主最后打算真正上线运营的。索性就改进了下&#xff0c;把静态资源尽量放到云存储中&#xff0c;方便后续开发。这里把方法和遇到坑给记录下。1.使用前提注册七牛云并创建存储空…

bool类型0和1真假_MySQL整理5—数据类型和运算符

数据科学探路者&#xff1a;MySQL整理4—数据表的基本操作2​zhuanlan.zhihu.com一、数据类型数据科学探路者&#xff1a;MySQL知识整理1—数据库基础​zhuanlan.zhihu.com在上面链接的部分内容中&#xff0c;介绍了以下数据类型&#xff1a;整数类型&#xff1a;BIT、BOOL、TI…

mysql 消息队列_MYSQL模拟消息队列(转载) | 学步园

《PHP核心技术与最佳实践》第5章中的内容&#xff1a;MYSQL模拟消息队列主要用于微博&#xff0c;团购秒杀等场合&#xff0c;其用意是将大量并发的数据库操作变得缓慢可控&#xff0c;达到削峰的目地。同时实现方式也比较简单易行。比如微博某大V发布了一条微博&#xff0c;那…

mysql查询后调用mysql_free_result_怎么释放_关于mysql_free_result和mysql_close的解惑

之前用mysql的时候一直是在用短链接&#xff0c;调用mysql_store_result获取一次数据之后就直接调用&#xff1a;以下是代码片段&#xff1a; mysql_free_result(m_result); mysql_close(m_Database);但是有两个问题&#xff1a;以下是引用片段&#xff1a;1.当使用长连接时(即…

mysql查询 百万_MySQL百万级数据分页查询优化

前言当需要从数据库查询的表有上万条记录的时候&#xff0c;一次性查询所有结果会变得很慢&#xff0c;特别是随着数据量的增加特别明显&#xff0c;这时需要使用分页查询。对于数据库分页查询&#xff0c;也有很多种方法和优化的点。下面简单说一下我知道的一些方法。准备工作…

mysql mac 中文乱码_Mac mysql 解决中文乱码

Mac mysql 解决中文乱码问题出现“???”之类的无法识别的乱码到/etc目录下自己建一个my.cnf文件(需要最高权限,使用sudo su)&#xff0c;然后写入内容&#xff1a;[client]default-character-setutf8[mysqld]character-set-serverutf8保存&#xff0c;修改。关掉重启mysql&a…

tos重装mysql_云服务器(腾讯云)从零开始部署记录(3)之mysql5.7安装

1、安装yum repocentos的yum源中没有mysql(可尝试直接使用安装命令&#xff1a;yum install mysql-server尝试)&#xff0c;需要到mysql的官网下载yum repo配置文件&#xff0c;然后安装&#xff1a;#下载wget https://dev.mysql.com/get/mysql57-community-release-el7-9.noar…