文章图片
文章目录
- 前言
- 遇上问题
- 问题分析
- 遇上新问题
- 拨开云雾见月明
- 总结
- 参考文章
前言 这两天在折腾SSM,在捣鼓
Spring MVC
的时候,我想让Spring MVC的前端控制器(DispatcherServlet
)给用户返回的是HTML类型的视图而不是JSP类型的视图,于是我按照常规的思路,把Spring MVC
配置文件里面的视图解析器配置修改成HTML后缀的,然后就遇上了各种问题了…当然这些问题也都是我对Spring MVC不够了解才导致的,接下来详细说一下我遇到的问题以及解决过程。遇上问题 为了将返回给用户的视图从
JSP
改成HTML
嘛,我就寻思着不就是把Spring MVC
配置文件的视图配置改一下,把.jsp
改成.html
嘛。文章图片
原来返回JSP的配置 Spring-MVC.xml:
因为我的JSP文件就是放在
web
根目录下,所以这里prefix
就留空了。修改成返回HTML的配置 Spring-MVC.xml:
然后写
Controller
将视图返回给前端控制器DispatchServlet
看看能不能将HTML类型的视图返回给用户package com.nChat.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class UserController {@RequestMapping(value = "https://www.it610.com/login")
public String login(){
return "/register";
//返回web根目录下的register.html
}
}
emmmm配置文件修改好了,按照我的想法,运行肯定没“问题”吧,可现实总是打我脸…可能是我太年轻了吧,运行之后报错,页面显示404
24-Dec-2018 21:57:35.769 警告 [http-nio-8080-exec-3] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /
24-Dec-2018 21:57:35.778 警告 [http-nio-8080-exec-2] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /
24-Dec-2018 21:57:35.854 警告 [http-nio-8080-exec-1] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /
24-Dec-2018 21:57:38.542 警告 [http-nio-8080-exec-4] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /register.html
错误的意思大概是前端控制器
DispatchServlet
找不到请求相对应的mapping
,所以抛出noHandlerFound
的异常文章图片
问题分析 我们来分析看看为啥出现这个问题,首先贴出我们前端控制器
DispatchServlet
的工作流程先文章图片
流程用文字说明大概如下:
- 用户发送请求,被 SpringMVC 的前端控制器
DispatcherServlet
拦截。 - DispatcherServlet 收到请求后自己不进行处理,而是将请求转发给处理器映射器
HandlerMapping
。 - 处理器映射器根据请求的URL确定映射关系找出相应的处理器适配器,并且返回HandlerExecutionChain对象给前端控制器。
处理器映射器找到具体的处理器适配器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。 - DispatcherServlet根据3返回的
HandlerExecutionChain
调用相应的处理器适配器HandlerAdapter
。 - 经过处理器适配器
HandlerAdapter
调用具体的处理器(Controller
,也叫后端控制器)。 - Controller将结果封装到ModelAndView返回给HandlerAdapter。
- HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
- DispatcherServlet将ModelAndView传给视图解析器
ViewReslover
,查询到相应的视图View。 - ViewReslover解析后返回具体的View。
- DispatcherServlet把Model交给View进行渲染(即将模型数据填充至视图中)。
- DispatcherServlet响应用户。
org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /
这个报错是出在了流程的第3
步中,也就是DispatchServlet
将请求转发给HandlerMapping
后,HandlerMapping
根据用户的请求找不到相应处理器映射器,所以就报了这个错误。那造成这个问题的原因会不会是我们没定义相应的Controller
,导致也没有相应的处理器适配器,但是我们的Controller
确实已经写好了,而且用返回JSP类型视图的代码测试数是正常的,没任何问题。那我们一步步排错,我们在
Controller
中打印输出个字符,判断看看请求有没有进Controller
先@RequestMapping(value = "https://www.it610.com/login")
public String login(){
System.out.println("coming");
return "/register";
//返回web根目录下的register.html
}
简单粗暴,添加个
System.out.println("coming");
如果请求进来了我们就可以看到打印coming
的内容,我们再重新运行项目测试看看文章图片
coming
24-Dec-2018 23:07:38.974 警告 [http-nio-8080-exec-4] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /register.html
我们可以看到确实进来了,既然进来了,也就是说用户的请求至少已经执行到第
5
步了,那和前面说错误出现在第3
步不是矛盾了吗??莫急,继续往下分析,既然肯定用户的请求前5
步都没问题的,那打印完coming
后为什么又出现了本该出现在第2
的错误呢?org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /register.html
,莫不是从第5
步又跳到第2
步了?为了继续排查下去,我们继续添加个
Controller
对应register.html
,看看他还报错不@Controller
public class UserController {@RequestMapping(value = "https://www.it610.com/login")
public String login(){
System.out.println("coming");
return "/register";
//返回web根目录下的register.html
}@RequestMapping(value = "https://www.it610.com/register.html")
public Stringaa(){
System.out.println("coming aa");
return "/aa";
}
}
重新启动项目运行看看,输出如下
coming
coming aa
24-Dec-2018 23:29:00.759 警告 [http-nio-8080-exec-4] org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /aa.html
输出表明既进到了
/login
又进到了/register.html
,然而还是继续报错org.springframework.web.servlet.DispatcherServlet.noHandlerFound No mapping for GET /aa.html
,一样的错误但是造成的错误原因不一样,上面的错误是因为找不到/register.html
相应的mapping,然后我们把/register.html
的Controller加上并且返回/aa.html
,所以导致了这次错误找不到/aa.html
相应的mapping。看到这里是不是有点头绪了?我们在
Controller
返回ModelAndView
给HandlerAdapter
,HandlerAdapter
再把ModelAndView
返回给DispatchServlet
,然后DispatchServlet
再把ModelAndView
传给视图解析器ViewReslover
解析,也就是图中对应的第6
到第8
步,到这里之前都是没问题的,问题就出现在了第9
步身上了,ViewReslover
返回的视图名给DispatchServlet
,重点来了!!! DispatchServlet
又把这个视图名当做一个新的请求,去交给HandlerMapping
处理!!也就是图中的第2
步,然后无限死循环下去…文章图片
遇上新问题 那问题又来了,为什么
DispatchServlet
会把它当成一个新的请求去处理呢?是不是我们Servlet配置的匹配规则写的不对,把返回的视图也拦截上了?我们来看看我们的Servlet配置项目的web.xml
Spring-MVC
org.springframework.web.servlet.DispatcherServlet
contextConfigLocationclasspath:*.xml
1 Spring-MVC
/
匹配规则我写的是
/
,网上的很多文章也都说匹配规则/
是不会拦截.jsp、html
等格式的URL的,只会拦截/login
这样的,而/*
的匹配规则才会拦截所有请求包括/login、.jsp、html
等,实践证明网上的这些观点都是错误的! 在这里我要更正一下,匹配规则/
和/*
都是会拦截所有请求(包括/login、.jsp、.html、.css等)也就是说他们俩的作用是一样的,既然说.jsp、.html
都会拦截那为什么配置/
规则的时候.jsp的能正常而.html的却被再次拦截导致死循环和配置/*
不管是.jsp还是.html都再次被拦截从而进入死循环?拨开云雾见月明 既然配置
/
和/*
的作用一样,为啥结果不一样呢?玄机就在Tomcat
上,在Tomcat
的conf/
目录下,有个web.xml
的文件。文章图片
这个
web.xml
在Tomcat启动的时候就被加载进来,对所有webapp都有效,至于Tomcat下的web.xml和我们自己项目下的web.xml的区别和联系请自行到参考文章查看。这里我们详细分析一下Tomcat下的web.xml里面有啥,打开conf/web.xml
看到他里面定义了个拦截.jsp
和.jspx
的Servlet文章图片
Tomcat的conf/web.xml
jsp
*.jsp
*.jspx
这个拦截规则也就是把所有的
.jsp
、.jspx
URL请求都拦截在servlet-name
为jsp的servlet中,我们搜索jsp
查找一下servlet使用的类文章图片
Tomcat的conf/web.xml
jsp
org.apache.jasper.servlet.JspServlet
forkfalse
xpoweredByfalse
3
可以看到这个拦截
.jsp
、.jspx
的servlet使用的类是org.apache.jasper.servlet.JspServlet
,也就是使用的类和我们项目下web.xml的servlet使用的org.springframework.web.servlet.DispatcherServlet
类不一样,使用的类不一样也导致.jsp
、.jspx
的URL请求都不会走上图中DispatchServlet的流程,而是走它使用的类的具体流程,想了解的可以查询这个类的相关资料。到这里我们可以明确一点的就是我们项目下web.xml的servlet规则定义成/
或者/*
的时候.jsp
正常来说应该是会被我们的项目下web.xml的servlet拦截的,但是根据servlet-mapping的匹配规则,.jsp
、.jspx
的URL都会先被Tomcat下web.xml里面的servlet拦截,而导致.jsp
、.jspx
不会被我们项目的web.xml里面的servlet拦截。**这也就是为什么匹配规则写成/
的时候.jsp
会被忽略不进行拦截的原因,那问题又来了匹配规则写成/*
的时候.jsp
没有被忽略仍然进行拦截的呢?原因是规则/*
会覆盖所有默认的servlet,从而将所有请求都拦截了下来,接下来我们可以修改Tomcat的web.xml下的servlet配置,让项目的web.xml下的servlet配置规则为/
时候也支持返回html
类型的视图Tomcat的conf/web.xml
jsp
*.jsp
*.jspx
*.html
添加个规则
*.html
,即URL是.html类型的话就走Tomcat的jsp
这个servlet而不走我们项目的servlet,然后重新运行项目测试一下文章图片
文章图片
文章图片
文章图片
(请忽略乱码问题o(╥﹏╥)o) 看到了吧,确实可以通过
Controller
返回html类型的视图了吧,也就是在执行完第9
步后返回的视图不再是被org.springframework.web.servlet.DispatcherServlet
拦截,而是被org.apache.jasper.servlet.JspServlet
拦截,从而跳出了DispatchServlet
的魔抓不会再死循环了。那以后想要返回.html类型的视图是不是都要去修改Tomcat的web.xml?也不用这样,我们只要在我们的项目web.xml中配置一个相同名的servlet即可,它会自动覆盖Tomcat的web.xml的,如
jsp
.html
这样配,但是会报错
Cannot resolve Servlet 'jsp'
,我也不懂为啥,知道的朋友可以补充下。Tomcat下的web.xml除了有名为jsp
的servlet,还有一个大家应该都很熟悉的,就是名为default
的servlet,它的作用和jsp
的大概一样,大家知道它是拿来配置静态资源的,却很少了解它怎么来的Tomcat的conf/web.xml
文章图片
文章图片
default
org.apache.catalina.servlets.DefaultServlet
【我是如何一步步解决问题 让Spring MVC返回HTML类型的视图】debug0
listingsfalse
1 default
/
我们也可以用它来代替
jsp
的servlet来达到不拦截.html类型的视图,同样我们在项目的web.xml下配置名为default
的servlet
default
*.html
这样即可让.html的请求都不会被前端控制器DispatchServlet拦截到。
总结 当然要返回.html类型的视图也不是只有这种方法,也可以通过更换视图解析器,但是我觉得这样没必要,因为
InternalResourceViewResolver
视图解析器本身就是支持.html的,只是我们没处理正确而已。通过这次出现的问题,引发我了对Spring MVC的进一步了解,也把分析、解决问题的过程记录下,希望能让自己印象更深刻点,也希望能帮助到大家。参考文章 servlet-mapping url-pattern / 和 /*区别
Tomcat文件夹下的context.xml和web.xml
web.xml中出现default是什么意思?
转自:https://ddnd.cn/2018/12/24/springmvc-view-webxml/
推荐阅读
- Java|Java基础——数组
- 人工智能|干货!人体姿态估计与运动预测
- java简介|Java是什么(Java能用来干什么?)
- Java|规范的打印日志
- Linux|109 个实用 shell 脚本
- 程序员|【高级Java架构师系统学习】毕业一年萌新的Java大厂面经,最新整理
- Spring注解驱动第十讲--@Autowired使用
- SqlServer|sql server的UPDLOCK、HOLDLOCK试验
- jvm|【JVM】JVM08(java内存模型解析[JMM])
- 技术|为参加2021年蓝桥杯Java软件开发大学B组细心整理常见基础知识、搜索和常用算法解析例题(持续更新...)