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;都很…

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

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

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;的指针进行…

作为神经网络的输入_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…

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…

linux启动mysql1820_linux 系统下MySQL5.7重置root密码(完整版,含ERROR 1820 (HY000)解决方案)...

mysql的root密码忘记了怎么重置&#xff1f;往下看&#xff1a;1.保证你的mysql服务处于关闭状态。(用ps -ef | grep mysql 查看进程&#xff0c;有则kill -9 进程号)2.在my.cnf所在目录中执行 vim my.cnf3.在[mysqld] 后边加入skip-grant-tables如图&#xff1a;并保存。4.启动…

mysql谓词演算_MySQL基础知识

一、了解MySQL1、什么是数据库&#xff1f;数据库是一种以某种有组织的方式存储的数据集合。2、模式(schema):关于数据库和表的布局及特性的信息。3、列&#xff1a;正确的将数据分解为多个列极为重要。通过把它分解开&#xff0c;才有可能利用特定的列对数据进行排序和过滤。4…

mysql+缓冲池脏块率高_什么是数据库的 “缓存池” ?(万字干货)

原标题&#xff1a;什么是数据库的 “缓存池” &#xff1f;(万字干货)1、Buffer Pool 概述Buffer Pool 是什么&#xff1f;从字面上看是 缓存池 的意思&#xff0c;没错&#xff0c;它其实也就是 缓存池 的意思。它是 MySQL 当中至关重要的一个组件&#xff0c;可以这么说&…

mysql多实例配置安装_MySQL多实例安装配置方案

一.基本概念MySQL多实例就是&#xff0c;在一台机器上开启多个不同的服务端口(如&#xff1a;3306,3307,3308...)&#xff0c;运行多个MySQL服务进程&#xff0c;这些服务进程通过不同的socket监听不同的端口提供服务。MySQL可以共用一套安全程序&#xff0c;使用不同的my.cnf配…

java趣味_Java趣味分享:try finally

考虑以下四个测试方法&#xff0c;它们会输出什么&#xff1f;public class Test {public static void main(String\[\] args) {System.out.println(test1());System.out.println(test2());System.out.println(test3());System.out.println(test4());}private static int test1…

java 纯面向对象_Java到底是不是一种纯面向对象语言?

原标题&#xff1a;Java到底是不是一种纯面向对象语言&#xff1f;Java——是否确实的 “纯面向对象”&#xff1f;让我们深入到Java的世界&#xff0c;试图来证实它。在我刚开始学习 Java 的前面几年&#xff0c;我从书本里知道了 Java 是遵循 “面向对象编程范式(Object Orie…

java爬虫post 404_POST 后台404错误

今天在开发一个用户信息更新模块的时候遇到了两个问题&#xff1a;1、在我提交form表单的时候我希望在提交表单之后页面不刷新&#xff0c;同时返回更新数据2、向后台POST表单信息的时候&#xff0c;后台显示POST url 404的错误解决第一个问题的使用使用jquery.form的插件进行异…

java防止重复启动bat_java调用exe,及调用bat不成功的解决办法

开门见山的说&#xff0c;文件目录如下想调用123.exe,但是尝试了几次调用不到&#xff0c;然后写了个bat。初始内容如下echo offD:cd test"123.exe"双击可以运行&#xff0c;但是java代码无法调用&#xff0c;怪事。于是&#xff0c;辗转反侧&#xff0c;苦思冥想&am…

java8.0 platform图_Java Platform SE binary语言-Java编程32位/64位版(jdk-jeb)下载V8.0.2510.8官方安装版-西西软件下载...

Java编程32位/64位版(jdk-jeb)是一款功能强大的计算机编程语言工具&#xff0c;Java技术让您可以在安全的计算环境下工作和游戏。使用 Java您可以畅玩网络游戏、与世界各地的朋友聊天、计算按揭利息&#xff0c;还可以在 3D 模式下浏览图像&#xff0c;诸多功能不一而足。软件特…

java 线程由浅入深_由浅入深,Java 并发编程中的 Synchronized(一)

synchronized 作用synchronized 关键字是 Java 并发编程中线程同步的常用手段之一。1.1 作用&#xff1a;确保线程互斥的访问同步代&#xff0c;锁自动释放&#xff0c;多个线程操作同个代码块或函数必须排队获得锁&#xff0c;保证共享变量的修改能够及时可见&#xff0c;获得…