SpringMVC源码阅读
MVC模式阅读
Model1模式: 早期的时候就是JSP和javaBean俩个。
Model2 模型:是在 Model1 的基础上进行改良,它是 MVC 模型的一个经典应用。它把处理请求 和展示数据进行分离,让每个部分各司其职。
此时的 JSP 已经就是纯粹的展示数据了,而处理请求的事情交由控制器来完成,使 每个组件充分独立,提高了代码可重用性和易维护性。下图展示的就是 Model2 模型:
SpringMVC执行流程图
这个图就是我们分析源码的重点
SpringMVC不同方式的源码阅读
实现Controller接口的形式
结合上面的图我们跑1遍源码,先看没有注解的形式
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = new ModelAndView();
mv.setViewName("success");
mv.addObject("hello","Mr.天");
return mv;
}
}
前端发请求必然经过dispatcherServlet前端控制器,
可以看到
我们在doService上打个断点,那么为什么是doService,前端控制器本质就是个Servlet
HttpServlet:doGet方法—
子类FrameworkServlet->processRequest(request, response);
processRequest->doService->
dispatcherServlet->doService
进入断点
用了mappedHandler来接收,他是个什么东西呢?
HandlerExecutionChain mappedHandler = null;
这个就是处理器执行链,
进入getHandler方法
根据用户请求,传进去的是request,此时的HandlerMapping
是BeanNameUrlHandlerMapping
返回的是处理器和拦截器
谁来调度呢?
处理器适配器上场了
传入的是处理器HelloController,这个值是上面的getHandler获取的
进入getHandlerAdapter
方法
这个处理器适配器也有几个不同的类型,现在的是SimpleControllerHandlerAdapter
是否支持,是的话通过SimpleControllerHandlerAdapter
调用。
然后继续往下执行
执行处理器
通过适配器调用处理器
把handler转成了Controller,因为我们自己的HelloController实现了Controller接口
可以看见handleRequest
方法是个接口
我们自己的controller实现了他
进入我们的接口,返回modelView
然后就是视图解析了
进入这个方法processDispatchResult
,
进入render方法,解析视图名字,传入model,拿到view对象
拿到view对象后,把model填充到view中去,进行渲染
开始进行输出的操作renderMergedOutputModel
最后到前端页面
注解形式的controller
/**
* 控制器
* @author
*/
@Controller
public class ControllerDemo1 {
@RequestMapping("/hello")
public String sayHello(String name){
System.out.println("Demo1控制器执行了。。。name:"+name);
return "success";
}
@RequestMapping("/hello2")
public String sayHello2(){
System.out.println("Demo1控制器的sayHell2方法执行了。。。");
return "success";
}
}
还是和上边一样,这次我们直接进入找适配器断点
这里和之前的适配器不一样,是RequestMappingHandlerAdapter
找到适配器,调handler
可以看到这个接口并没有RequestMappingHandlerAdapter
这个适配器
那就需要找第一个抽象父类AbstractHandlerMethodAdapter
的这个方法
这个方法调用完毕也是返回modelAndView
那么进入
执行处理器
获取参数
因为地址栏没有输入参数,这里是没有参数的
这个getBean就是controller实例
执行controller里的方法
this.getBridgedMethod().invoke(this.getBean(), args);
这个returnValue就是上边的
之后的流程就和上边的实现Controller是一样的了
HttpRequestHandler方式
还有一种方式用的很少就是实现HttpRequestHandler,这种方式是没有返回值的
仅仅作为了解,这就是传统的Servlet方式
public class ControllerDemo3 implements HttpRequestHandler {
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("=====进入ControllerDemo3==handleRequest方法===");
request.getRequestDispatcher("/WEB-INF/pages/success3.jsp").forward(request,response);
return;
}
}
SpringMVC处理器映射器和处理器适配器什么时候封装好的
在DispatcherServlet的initStrategies方法里
我们打个断点看下
使用注解的地址映射就在这里
SpringMvc参数封装
这个name是怎么注入的?
在这里
进入
invokeForRequest
方法
这里调用链很深,我们没必要全部记住。最后还是用的Servlet的API获取到了参数
String[] paramValues = request.getParameterValues(name);
@Controller
public class ParameterController2 {
/**
* 请求参数封装的分析
* @param name
* @param age
*/
@RequestMapping("testParam2")
public String testParam(@RequestParam("username") String name, Integer age){
System.out.println(name+","+age);
return "success";
}
}
这种形式的参数封装其实最后也是
request.getParameterValues(name);
有兴趣的可以自己跟一下
SpringMVC拦截器怎么执行
俩个拦截器
/**
* 定义拦截器,用于判断用户是否登录,如果用户已经登录则放行,否则用户未登录则跳转至登录页面
*/
public class LoginFilter extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("==============LoginFilter===preHandle=========");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("==============LoginFilter===postHandle=========");
}
}
/**
* 定义拦截器,用于判断用户是否登录,如果用户已经登录则放行,否则用户未登录则跳转至登录页面
*/
public class LoginFilter2 extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("==============LoginFilter2===preHandle=========");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("==============LoginFilter2===postHandle=========");
}
}
可以看见这两个拦截器都去继承了HandlerInterceptorAdapter
。
我们进入DispatcherServlet类的doService方法,在handle之上
进入这个if,处理器执行链HandlerExecutionChain
,可以看见拿到了3个拦截器,其中1个为默认,其他俩个是我们自己定义的,遍历这些
preHandle方法的特点就是返回true,true代表放行,false不放行
进入我们自己的拦截器
因为是true放行了
因此下面这个就不会走return,就放行了
然后走控制器,控制器走完走后置的拦截器
postHandle调用和preHandle是一样的,就是顺序不一样,原因上边解释过了。
最后总结一下拦截器,大家顺带着可以看一下,接口是可以写方法实现的
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
拦截器到底做了什么:
- preHandle方法
在请求到达Handler之前。先执行这个前置处理方法。当该方法返回false时,直接返回。不会执行拦截器链中的下一个拦截器 - postHandle
控制器方法执行后执行,视图渲染前执行 - afterCompletion
视图渲染之后执行(处理Controller异常信息。记录操作日志,清理资源等)