spring mvc原理深度解析
回顾servlet 与jsp 执行过程
流程说明:
1.请求Servlet
2.处理业务逻辑
3.设置业务Model
4.forward jsp Servlet
5. jsp Servlet 解析封装html 返回
spring mvc请求处理流程
spring mvc功能特性
spring mvc本质上还是使用Servlet处理,并在其基础上进行了封装简化了开发流程,提高易用性、并使用程序逻辑结构变得更清晰
a.基于注解的URL映谢
b.表单参数映射
c.缓存处理
d.全局统一异常处理
e.拦截器的实现
f.下载处理
spring mvc案例
第一步:引入spring mvc依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies>
第二步:创建web.xml文件
<?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" xmlns:web="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_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>springMVC</display-name>
<!-- 部署 DispatcherServlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/spring-mvc.xml</param-value>
</init-param>
<!-- 表示容器再启动时立即加载servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- 处理所有URL -->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
第三步:创建spring-mvc.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<bean name="/hello.do" class="com.yemuxia.mvc01.controller.SimpleController"/>
</beans>
第四步:创建SimpleController类
package com.yemuxia.mvc01.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SimpleController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = new ModelAndView("/WEB-INF/page/userView.jsp");
mv.addObject("name","hhh");
return mv;
}
}
第五步:创建userView.jsp文件
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
测试
${name}
</body>
</html>
spring mvc体系结构
spring mvc解决了什么问题
- URL映射
- 表单参数映射
- 调用目标Controller
- 数据模型映射
- 视图解析
- 异常处理
HandlerMapping url与控制器的映谢
HandlerAdapter 控制器执行适配器
ViewResolver 视图仓库
view 具体解析视图
HandlerExceptionResolver 异常捕捕捉器
HandlerInterceptor 拦截器
spring mvc各组件执行流程
HandlerMapping
HandlerMapping作用是通过url找到对应的Handler ,但其HandlerMapping.getHandler()方法并不会直接返回Handler 对像,而是返回 HandlerExecutionChain 对像,再通过 HandlerExecutionChain.getHandler() 返回最终的handler。
实现类
1.SimpleUrlHandlerMapping:基于手动配置 url 与control 映谢
2.BeanNameUrlHandlerMapping: 基于ioc name 中以 “/” 开头的Bean时行 注册至映射.
3.RequestMappingHandlerMapping:基于@RequestMapping注解配置对应映射
上述案例基于第二种。
基于SimpleUrlHandlerMapping配置映射
案例
SimpleUrlHandlerMapping体系结构
初始化(容器启动时调用)
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#setUrlMap
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#initApplicationContext
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#registerHandlers
// /表示根路径 /* 表示默认路径
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#registerHandler()
获取 Handler流程关键源码
org.springframework.web.servlet.DispatcherServlet#doService
org.springframework.web.servlet.DispatcherServlet#doDispatch
org.springframework.web.servlet.DispatcherServlet#getHandler
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#getHandlerInternal
// 获取URL路径
org.springframework.web.util.UrlPathHelper#getPathWithinApplication
// 查找handler
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#lookupHandler
// 封装执行链
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandlerExecutionChain
BeanNameUrlHandlerMapping体系结构
BeanNameUrlHandlerMapping 实现上与 SimpleUrlHandlerMapping 一致,唯一区别在于继承自AbstractDetectingUrlHandlerMapping ,通过对应detectHandlers 可以在无配置的情况下发现url 与handler 映射。
RequestMappingHandlerMapping体系结构
最常用 基于注解。
Handler
对应的类型如下
- Controller 接口:
- HttpRequestHandler 接口:
- HttpServlet 接口:
- @RequestMapping方法注解
可以看出 Handler 没有统一的接口,当dispatchServlet获取当对应的Handler之后如何调用呢?调用其哪个方法?这里有两种解决办法,一是用instanceof 判断Handler 类型然后调用相关方法 。二是通过引入适配器实现,每个适配器实现对指定Handler的调用。spring 采用后者。
HandlerAdapter详解
这里spring mvc 采用适配器模式来适配调用指定Handler,根据Handler的不同种类采用不同的Adapter,其Handler与 HandlerAdapter 对应关系如下
Handler类别 | 对应适配器 | 描述 |
---|---|---|
Controller | SimpleControllerHandlerAdapter | 标准控制器,返回ModelAndView |
HttpRequestHandler | HttpRequestHandlerAdapter | 业务自行处理请求,不需要通过modelAndView 转到视图 |
Servlet | SimpleServletHandlerAdapter | 基于标准的servlet 处理 |
HandlerMethod | RequestMappingHandlerAdapter | 基于@requestMapping对应方法处理 |
基于Servlet 处理
案例
package com.yemuxia.mvc01.controller;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SimpleServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("wo shi ge hao ren");
}
}
ViewResolver 与View 详解
找到对应的Adapter 之后就会基于适配器调用业务处理,处理完之后业务方会返回一个ModelAndView ,在去查找对应的视图进行处理。其在org.springframework.web.servlet.DispatcherServlet#resolveViewName() 中遍历 viewResolvers 列表查找,如果找不到就会报一个 Could not resolve view with name 异常。
基于BeanNameViewResolver
案例
添加自定义视图
package com.yemuxia.mvc01.controller;
import org.springframework.web.servlet.View;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* 自定义的视图解析器
*/
public class MyView implements View {
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
response.getWriter().println("hello dajia shi ge hao ren");
}
@Override
public String getContentType() {
return null;
}
}