专栏目录
- 1-Spring架构源码分析-Spring源码搭建
- 2-Spring架构源码分析-SSM框架说明
- 3-Spring架构源码分析-spring体系
- 4-Spring架构源码分析-Spring IOC机制设计思想和源码解读
- 5-Spring架构源码分析-Spring IOC之 Spring 统一资源加载策略
- 6-Spring架构源码分析-IoC 之加载 BeanDefinition
- 7-Spring架构源码分析-IoC 之注册 BeanDefinitions
- 8-Spring架构源码分析-IoC 之解析Bean:解析 import 标签
- 9-Spring架构源码分析-IoC 之解析 bean 标签:开启解析进程
- 10-Spring架构源码分析-IoC 之解析 bean标签:BeanDefinition
- 11-Spring架构源码分析-IoC 之注册解析的 BeanDefinitions
- 12-Spring架构源码分析-IoC 之装载 BeanDefinitions 总结
- 13-Spring架构源码分析-IoC 之开启 Bean 的加载
- 14-Spring架构源码分析-IoC 之加载 Bean:总结
- 15-Spring架构源码分析-Spring代理与AOP
- 16-Spring AOP源码分析-@EnableAspectJAutoProxy和AspectJAutoProxyRegistrar
- 17-Spring AOP源码分析-AnnotationAwareAspectJAutoProxyCreator
- 18-Spring AOP源码分析-AOP与BeanPostProcessor处理器
- 19-Spring AOP源码分析-代理对象调用目标方法
- 20-spring mvc设计思想和源码解读-spring mvc 功能特性
- 21-mvc 体系结构源码详解
- 22-Spring MVC源码跟踪
- 23-Spring事务源码分析
mvc 体系结构详解
spring mvc 框架解决的问题
从技术角度去思考 任何一个现存的框架都有其存在理由,而这个理由就是解决实际的问题。或者提供更好的解决问题的方案。spring mvc 它解决了什么问题呢?
- URL映射
- 表单参数映射
- 调用目标Control
- 数据模型映射
- 视图解析
- 异常处理
上术解决在spring mvc 中都体现在如下组件当中
- **HandlerMapping **'hændlə 'mæpɪŋ
- url与控制器的映射
- HandlerAdapter 'hændlə ə’dæptə
- 控制器执行适配器
- **ViewResolver **vjuː riː’zɒlvə
- 视图仓库
- view
- 具体解析视图
- **HandlerExceptionResolver **'hændlə ɪk’sepʃ(ə)n riː’zɒlvə
- 异常捕捕捉器
- HandlerInterceptor 'hændlə ɪntə’septə
- 拦截器
- 拦截器
mvc 各组件执行流程
HandlerMapping 详解
其为mvc 中url路径与Control对像的映射,DispatcherServlet 就是基于此组件来寻找对应的Control,如果找不到就会报 No mapping found for HTTP request with URI的异常。
HandlerMapping 接口结构分析:
HandlerMapping 作用是通过url找到对应的Handler ,但其HandlerMapping.getHandler()方法并不会直接返回Handler 对象,而是返回 HandlerExecutionChain 对象在通过 HandlerExecutionChain.getHandler() 返回最终的handler
常用实现类:
目前主流的三种mapping 如下:
- SimpleUrlHandlerMapping:基于手动配置 url 与control 映射
- BeanNameUrlHandlerMapping: 基于ioc name 中已 “/” 开头的Bean时行 注册至映射.
- RequestMappingHandlerMapping:基于@RequestMapping注解配置对应映射
SimpleUrlHandlerMapping
演示基于 SimpleUrlHandlerMapping配置映射。
编写mvc 文件
<!--需要放在前面<mvc:default-servlet-handler/>,不然不起作用-->
<!--SimpleUrlHandlerMapping只支持实现了Controller接口的bean,且还需要在其属性里进行url路径==>bean的映射,不支持@RequestMapping注解-->
<bean id="urlMappingController" class="com.naixue.web.UrlMappingController"/>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/urlMapping">urlMappingController</prop>
</props>
</property>
</bean>
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 映射。
结构图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f6mYEdSd-1662071432790)(https://gitee.com/onlycreator/draw/raw/master/img/image-20201020204620343.png)]
RequestMappingHandlerMapping
其基于注解实现,在后续章节讲解注解映射的时候在详细讲。
Handler 类型
在 AbstractUrlHandlerMapping 我们可以看到存储handler 的Map 值类型是Object ,是否意味着所有的类都可以做来Handler 来使用?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c3dTJTZ9-1662071432791)(https://gitee.com/onlycreator/draw/raw/master/img/image-20201021145001157.png)]
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
// Eagerly resolve handler if referencing singleton via name.
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
ApplicationContext applicationContext = obtainApplicationContext();
if (applicationContext.isSingleton(handlerName)) {
//可以看出实际是put了一个bean为handler
resolvedHandler = applicationContext.getBean(handlerName);
}
}
......
this.handlerMap.put(urlPath, resolvedHandler);
...
}
}
Handler 对应类型如下如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C686kT9E-1662071432792)(https://gitee.com/onlycreator/draw/raw/master/img/image-20201021112638107.png)]
- 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对应方法处理 |
HandlerAdapter 接口方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pqj1TVga-1662071432793)(https://gitee.com/onlycreator/draw/raw/master/img/image-20201020205308786.png)]
HandlerAdapter 接口结构图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tjEkzfDP-1662071432794)(https://gitee.com/onlycreator/draw/raw/master/img/image-20201020205312249.png)]
- 演示基于Servlet 处理 SimpleServletHandlerAdapter
<!-- 6 配置Servlet适配器 -->
<bean class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter"/>
<!-- 6.演示servlet -->
<bean id="/servlet" class="com.naixue.web.servlet.OneServlet"/>
public class OneServlet extends HttpServlet {
private static final long serialVersionUID = 46587649263984732L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("this is a servlet!");
}
}
上述例子中当IOC 中实例化这些类之后 DispatcherServlet 就会通过
org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter() 方法查找对应handler的适配器 ,如果找不到就会报 如下异常 。
javax.servlet.ServletException: No adapter for handler [com.tuling.control.SimpleControl@3c06b5d5]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler
org.springframework.web.servlet.DispatcherServlet.getHandlerAdapter(DispatcherServlet.java:1198)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
ViewResolver 与View 详解
找到应的Adapter 之后就会基于适配器调用业务处理,处理完之后业务方会返回一个ModelAndView ,在去查找对应的视图进行处理。其在org.springframework.web.servlet.DispatcherServlet#resolveViewName() 中遍历 viewResolvers 列表查找,如果找不到就会报一个 Could not resolve view with name 异常。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NxkEpiZF-1662071432795)(https://gitee.com/onlycreator/draw/raw/master/img/image-20201020213549824.png)]
BeanNameViewREsolver示例:
添加自定义视图:
public class MyView implements View {
@Override
public String getContentType() {
return "text/html; charset=utf-8";
}
@Override
public void render(Map<String, ?> model, HttpServletRequest
request, HttpServletResponse response) throws Exception {
Integer num = Integer.valueOf((String) model.get("num"));
response.setHeader("Content-Type","text/html; charset=utf-8");
response.getWriter().print("10进制num:"+num+"\n转\n16进制为:"+Integer.toHexString(num));
}
}
视图解析器:
public class NXViewResolver implements ViewResolver, Ordered {
private Integer order;
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
//根据视图名返回视图对象
if (viewName.startsWith("nx:")) {
return new MyView();
}
return null;
}
//实现Orderd接口,可以修改视图解析器的优先级
@Override
public int getOrder() {
return this.order;
}
//改变视图解析器的优先级
public void setOrder(Integer i) {
this.order = i;
}
}
配置视图解析器:
<bean name= "myView" class="com.naixue.web.view.MyView"/>
<!--自定义的视图解析器 value="1"数字越小优先级越高-->
<bean class="com.naixue.web.view.NXViewResolver">
<property name="order" value="1"></property>
</bean>
修改视图跳转方法 :
public class ViewController implements Controller {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
String num = request.getParameter("num");
if (!StringUtils.isNumeric(num)) {
throw new HttpServerErrorException(HttpStatus.BAD_REQUEST,"请输入num");
}
ModelAndView mv = new ModelAndView();
mv.setViewName("nx:view");
mv.addObject("num", num);
return mv;
}
}
配置Controller的bean
<bean name="/view" class="com.naixue.web.ViewController"/>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H2XDH1Y7-1662071432796)(https://gitee.com/onlycreator/draw/raw/master/img/image-20201021111403734.png)]
专栏目录
- 1-Spring架构源码分析-Spring源码搭建
- 2-Spring架构源码分析-SSM框架说明
- 3-Spring架构源码分析-spring体系
- 4-Spring架构源码分析-Spring IOC机制设计思想和源码解读
- 5-Spring架构源码分析-Spring IOC之 Spring 统一资源加载策略
- 6-Spring架构源码分析-IoC 之加载 BeanDefinition
- 7-Spring架构源码分析-IoC 之注册 BeanDefinitions
- 8-Spring架构源码分析-IoC 之解析Bean:解析 import 标签
- 9-Spring架构源码分析-IoC 之解析 bean 标签:开启解析进程
- 10-Spring架构源码分析-IoC 之解析 bean标签:BeanDefinition
- 11-Spring架构源码分析-IoC 之注册解析的 BeanDefinitions
- 12-Spring架构源码分析-IoC 之装载 BeanDefinitions 总结
- 13-Spring架构源码分析-IoC 之开启 Bean 的加载
- 14-Spring架构源码分析-IoC 之加载 Bean:总结
- 15-Spring架构源码分析-Spring代理与AOP
- 16-Spring AOP源码分析-@EnableAspectJAutoProxy和AspectJAutoProxyRegistrar
- 17-Spring AOP源码分析-AnnotationAwareAspectJAutoProxyCreator
- 18-Spring AOP源码分析-AOP与BeanPostProcessor处理器
- 19-Spring AOP源码分析-代理对象调用目标方法
- 20-spring mvc设计思想和源码解读-spring mvc 功能特性
- 21-mvc 体系结构源码详解
- 22-Spring MVC源码跟踪
- 23-Spring事务源码分析