SpringMVC与传统MVC

传统MVC

MVC的全称为Model View Controller,是一种软件架构模式,将web应用程序分为模型、视图、控制器三部分。

最典型的MVC就是JSP + servlet + javabean的模式。

  • Model
    模型层,指工程中的JavaBean(entity、service、dao),作用是处理数据。
  • View
    视图层,指工程中的html或jsp等页面,作用是与用户进行交互、展示数据。
  • Controller
    控制层,指工程中的servlet,作用是接受请求和响应浏览器。

Spring MVC

Spring MVC是Spring在Spring Container Core和AOP等技术基础上,遵循上述Web MVC的规范推出的web开发框架,目的是为了简化Java栈的web开发。

Spring Web MVC 框架也是一个基于请求驱动的Web 框架,并且也使用了前端控制器模式来进行设计,再根据请求映射规则分发给相应的页面控制器(动作/处理器)进行处理。

SpringMVC的工作流程

  1. 首先用户发送请求 —> DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;
  2. DispatcherServlet —> HandlerMapping,HandlerMapping 把请求映射为 HandlerExecutionChain 处理器执行链对象(包含一个Handler 处理器(页面控制器)、多个HandlerInterceptor 拦截器),通过这种策略模式,很容易添加新的映射策略;
  3. DispatcherServlet —> HandlerAdapter,HandlerAdapter 将会把处理器包装为适配器,从而支持多种类型的处理器, 即适配器设计模式的应用,从而很容易支持很多类型的处理器;
  4. HandlerAdapter —> Handler(具体的handler或者业务逻辑Controller),HandlerAdapter 将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理,并返回一个ModelAndView 对象(包含模型数据、逻辑视图名);
  5. ModelAndView 的逻辑视图名 —> ViewResolver,ViewResolver 将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;
  6. View —> 渲染,View 会根据传进来的Model 模型数据进行渲染,此处的Model 实际是一个Map 数据结构,因此 很容易支持其他视图技术;
  7. 返回控制权给DispatcherServlet,由DispatcherServlet 返回响应给用户,到此一个流程结束。

SpringMVC的核心组件

DispatcherServlet

HandlerMapping

HandlerExecutionChain

Handler

HandlerInterceptor

HandlerAdapter

ModelAndView

Resolver

ViewResolver
LocaleResolver
ThemeResolver
MultipartResolver

Filter vs HandlerInterceptor

  1. Filter 是 Servlet 规范中的,而 HandlerInterceptor 是 Spring 中的一个概念
  2. 拦截器位置相对于过滤器更靠后,过滤器的生命周期比拦截器更长

过滤器 Filter

过滤器是被Servlet容器执行的类。请求在进入容器时、响应在离开容器时,会经过一个个的过滤器。过滤器的实例,在容器中是以过滤器链的形式执行的。
如果在应用中,我们定义了多个过滤器,那么执行的先后顺序,可以通过 @Order 注解来指定。

Filter 接口的核心方法,也是其生命周期方法:

  • init(FilterConfig config)
    此方法只调用一次,用于初始化过滤器
  • doFilter(HttpServletRequest request, HttpServletResponse response, FilterChian chian)
    此方法在每一个请求打到映射的资源上时都会调用,比如定义一个 Filter 拦截 /path/* ,那么每一个匹配 /path/* 访问资源的请求进来时,都会执行此方法。这个方法中就是拦截器的具体逻辑
  • destroy()
    此方法也只执行一次,用于销毁过滤器

拦截器 HandlerInterceptor

  1. 接口HandlerInterceptor定义在org.springframework.web.servlet包中。
  2. 核心方法
    • preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
      该方法在请求到达 Controller 之前执行操作,返回一个布尔值。
      返回 true 时,继续往下执行;当返回 false 时,不再执行对应的 handler。
    • postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
      该方法在响应返回客户端之前执行
    • afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception)
      该方法在请求和响应流程完成之后执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
// 链式调用,按拦截器的添加顺序依次往下执行
// HandlerInterceptor的preHandle方法若返回true 继续执行下一个拦截器
// 若为false 则停止

registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/**");
// .excludePathPatterns("/**/i_scheduler/**");
registry.addInterceptor(new SchedulerInterceptor())
.addPathPatterns("/**");
}
}

@Component
@Slf4j
public class AuthInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String userAccount = request.getHeader("X-USER");
if (userAccount == null || userAccount.equals("")) {
throw new Exception("user not login");
}
log.info("AuthInterceptor x-user:{}", userAccount);
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}

@Component
@Slf4j
public class SchedulerInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String servletPath = request.getServletPath();
// 非任务请求,直接通过
if (!servletPath.startsWith("/i_scheduler")) {
log.info("SchedulerInterceptor not i_scheduler");
return true;
}

String path = servletPath.replaceAll("/i_scheduler/", "");
// 心跳探测
if ("ping".equals(path)) {
response.setStatus(200);
log.info("SchedulerInterceptor ping");
return false;
}

log.info("SchedulerInterceptor do i_scheduler");
return false;
}
}

SpringMVC实现原理之DispatcherServlet

DispatcherServlet是SpringMVC中唯一的Servlet,Servlet容器(Tomcat)把所有的请求都转发到DispatcherServlet,然后通过HandlerMapping把请求路由到具体的Controller中。

DispatcherServlet的初始化过程

  • DispatcherServlet和ApplicationContext有何关系?
  • DispatcherServlet是如何初始化的?

DispatcherServlet处理请求的过程

一个请求发出,经过DispatcherServlet进行了什么样的处理,最后将内容返回的呢?
以一个GET请求为例,请求URL是http://localhost:8080/api/v1/test

doGet 入口

doDispatch 请求分发

映射和适配器处理

视图渲染