前言
-
本篇文章基于重构后controller没有跳转到对应jsp,而是返回调用登录接口问题进行展开分析
1.1 我的原因是因为没有前后缀参数:spring.mvc.view.prefix、spring.mvc.view.suffix(百度一下就可以知道答案,但我对其中原因更感兴趣)@RequestMapping("/admin") public class MainAdminAction{ @RequestMapping("login") public ModelAndView login(ModelAndView mv) { mv.setViewName("/admin/login"); return mv; } }
-
分析之前提出几个问题?
2.1 spring的controller返回jsp路径是怎么去加载的?
DispatcherServlet#doDispatch入口分析
- 抽出和本次分析有关的代码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//会调用用户的Controller this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);//调完浏览器出现页面 }
- 继续跟踪代码,重点分析DispatcherServlet#render方法**(重点)**
2.1 红色标记1的view的url在未修复前的url为/admin/login(具体见AbstractCachingViewResolver#resolveViewName)
(1)UrlBasedViewResolver#buildView结合前缀和后缀生成最终的url
(2)UrlBasedViewResolver的前后缀属性可能会被覆盖,例如调试时发现被WebMvcAutoConfiguration#defaultViewResolver进行一次覆盖
2.2 红色标记2最终会调用InternalResourceView#renderMergedOutputModel方法(下面分析)
- InternalResourceView#renderMergedOutputModel分析
3.1 此方法主要就是生成RequestDispatcher,然后进行转发(tomcat层)
(1)注意:调节tomcat源码时,需要单独引入对应tomcat的包,譬如tomcat-catalina、tomcat-jasper
3.2 jsp页面请求最终会转发到JspServlet#service方法(下面单独分析)
JspServlet#service分析
本模块参考的文章:https://blog.ahao.moe/posts/JSP_compile_and_load.html
https://hengyunabc.github.io/spring-boot-fat-jar-jsp-sample/
- jspCompiler.removeGeneratedFiles(); //先删除上次生成的 class 文件, 后删除 java 文件
- jspCompiler.compile(); //重新生成 java 文件, 后编译为 class 文件
2.1 debug进去会发现生成java文件的位置(C:\Users\xxx.IntelliJIdea2019.3\system\tomcat\Unnamed_express-tomcat_2\work\Catalina\localhost\exp-client\org\apache\jsp\WEB_002dINF\views\admin\login_jsp.java)
接收后台参数
- 入口在tomcat-embed-jasper包下的PageContextImpl#proprietaryEvaluate方法
- 根据调用栈PageContextImpl#proprietaryEvaluate -->JasperELResolver#getValue -->PageContextImpl#findAttribute
2.1 从上图可以看出取值的优先级
后续
- 关于Jsp具体怎么生成的没有分析,因为不热门了
- 关于springboot打包分析暂未分析,后续有时间会进行分析