SpringMVC源码解析
一:springmvc运行过程:
1. dispatcherServlet 通过 HandlerMapping 找到controller
2. controller经过后台逻辑处理得到结果集modelandview
3. 视图解析器解析model,渲染view展示页面。
二:springmvc容器是什么:
很多人喜欢把spring和springmvc混为一谈, 其实它们是完全不同的两个概念。spring主要是创建对像管理依赖的; 而springmvc是一种mvc分层思想将功能模块化,处理用户请求的。而且spring和springmvc是有父子容器关系存在的。子容器可以获取父容器中的对象(可以理解成面向对象中的继承),例如controller通过@Autowired注入spring中的对象, 而如果把controller交给spring来管理,就会出现404异常,因为spring初始化只管理bean,并不会把url和method关联起来。
三:springmvc案例解析:
3.1 自定义springmvc注解@MyController和@MyRequestMapping
3.2 实现过程
public class MyDispatcherServlet extends HttpServlet {// springmvc 容器对象 key:类名id ,value bean对象private ConcurrentHashMap<String, Object> springmvcBeans = new ConcurrentHashMap<String, Object>();// springmvc 容器对象 keya:请求地址 ,vlue 类对象private ConcurrentHashMap<String, Object> urlBeans = new ConcurrentHashMap<String, Object>();// springmvc 容器对象 key:请求地址 ,value 方法名称private ConcurrentHashMap<String, String> handlermapping = new ConcurrentHashMap<String, String>();/*** 容器启动时*/@Overridepublic void init() throws ServletException {// 1. 扫描当前包(及子包)下面的所有类List<Class<?>> classes = ClassUtil.getClasses("org.wulei.controller");try {// 将这些类放到mvc容器this.findClassMVCAnnotation(classes);} catch (Exception e) {}// 将url和方法绑定起来this.headlerMapping();}/* 初始化有controller注解的bean,放入mvc容器。*/public void findClassMVCAnnotation(List<Class<?>> classes)throws ClassNotFoundException, InstantiationException, IllegalAccessException {for (Class<?> classInfo : classes) {// 2. 判断类上是否有加上controller注解MytController myController = classInfo.getDeclaredAnnotation(MytController.class);if (myController != null) {// 默认bean名称是类名小写String beanName = ClassUtil.toLowerCaseFirstOne(classInfo.getSimpleName());// 3. 实例化对象 , 放入mvc容器Object object = ClassUtil.newInstance(classInfo);springmvcBeans.put(beanName, object);}}}/* 通过handlerMapping将url和method关联起来 */public void headlerMapping() {// 4. 遍历mvc的bean容器所有的类for(Map.Entry<String, Object> mvcBean : springmvcBeans.entrySet()) {Object object = mvcBean.getValue();Class<? extends Object> classInfo = object.getClass();// 5. 判断类上面是否有url映射(即:@RequestMapping注解)MyRequestMapping requestMapping = classInfo.getDeclaredAnnotation(MyRequestMapping.class);// 6. 获取类上的url地址String baseUrl = "";if(requestMapping != null) {baseUrl = requestMapping.value();}// 7. 获取方法上的url映射路径Method[] methods = classInfo.getDeclaredMethods();if(methods.length>0) {for(Method method : methods) {MyRequestMapping methodRequestMapping = method.getDeclaredAnnotation(MyRequestMapping.class);if(methodRequestMapping != null) {// 8. 将 classUrl 和 methodUrl 拼接起来,得到完整url路径。String methodUrl = baseUrl+methodRequestMapping.value();// 9. 将url和method绑定起来,存入容器。 urlBeans.put(methodUrl, object);handlermapping.put(methodUrl, method.getName());/*** 到上面容器就就初始化完成,可以对外提供服务了。* 现在可以每个方法断点调试看运行过程。*/}}}}}/*** 处理用户请求*/@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 1. 获取请求url地址String url = req.getRequestURI();if(StringUtils.isEmpty(url)) return;// 2. 通过url获取容器中的controller对象Object object = urlBeans.get(url);if(object == null) {resp.getWriter().println("ERROR: not found 404 url");return;}// 3. 使用url获取方法.String methodName = handlermapping.get(url);if(StringUtils.isEmpty(methodName)) resp.getWriter().println("ERROR: not found method");// 4. java反射获取被调用的方法String resultPage = null;try {Method method = object.getClass().getMethod(methodName);resultPage = (String) method.invoke(object);resp.getWriter().println(resultPage);} catch (Exception e) {e.printStackTrace();}// 5.调用视图转换器渲染给页面展示this.myResourceViewResolver(resultPage, req, resp);}/*** 视图解析器*/private void myResourceViewResolver(String pageName, HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException {// 根路径String prefix = "/";String suffix = ".jsp";req.getRequestDispatcher(prefix + pageName + suffix).forward(req, res);}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);} }
3.3 测试 (正确的url会跳转到相应页面,错误的url会找不到路径)
四:源码解析
4.1 springmvc初始化阶段
4.1.1 我们在用springmvc的时候, 首先会配置 dispatcherServlet,它会拦截符合规则的url请求。
<servlet><servlet-name>WEB</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 指定springmvc扫描文件 --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:config/spring-*.xml</param-value></init-param><!-- 优先级 --><load-on-startup>1</load-on-startup></servlet><servlet-mapping><!-- 拦截规则 --><servlet-name>WEB</servlet-name><url-pattern>*.do</url-pattern></servlet-mapping>
4.1.2 可以看出 dispatcherServlet 最终还是继承于httpservlet, 说明它也会满足servlet的工作原理。
Servlet 生命周期:
Servlet 加载—>实例化—>服务—>销毁。
init():在Servlet的生命周期中,仅执行一次init()方法。负责初始化Servlet对象。
service():负责响应客户的请求。每次请求都会调用相应的doGet/doPost功能。
destroy():仅执行一次,在服务器端停止且卸载Servlet时执行该方法,负责释放占用的资源。
4.1.3 HttpServletBean 重写了 httpservlet 的 init()方法, 通过initServletBean() 方法来初始化bean, 具体实现在子类FrameworkServlet中。
/*** Map config parameters onto bean properties of this servlet, and* invoke subclass initialization.* @throws ServletException if bean properties are invalid (or required* properties are missing), or if subclass initialization fails.*/@Overridepublic final void init() throws ServletException {if (logger.isDebugEnabled()) {logger.debug("Initializing servlet '" + getServletName() + "'");}// 初始化DespatcherService的配置参数try {PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));initBeanWrapper(bw);bw.setPropertyValues(pvs, true);}catch (BeansException ex) {logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);throw ex;}// 让子类来初始化bean initServletBean();if (logger.isDebugEnabled()) {logger.debug("Servlet '" + getServletName() + "' configured successfully");}}
4.1.4 FrameworkServlet重写了initServletBean(),并在这个方法中调用了initWebApplicationContext()方法
protected WebApplicationContext initWebApplicationContext() {// 获取 ContextLoaderListener 初始化WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;if (this.webApplicationContext != null) {wac = this.webApplicationContext;if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;if (!cwac.isActive()) {if (cwac.getParent() == null) {// 将 Spring 的容器设为 SpringMVC 容器的父容器 cwac.setParent(rootContext);}configureAndRefreshWebApplicationContext(cwac);}}}if (wac == null) {wac = findWebApplicationContext();}if (wac == null) {wac = createWebApplicationContext(rootContext);}if (!this.refreshEventReceived) {// dispatcherServlet 重写这个方法 onRefresh(wac);}if (this.publishContext) {// 发布这个容器到 ServletContext 中String attrName = getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);}return wac; }
4.1.5 dispatcherServlet 重写onRefresh(),调用initHandlerMappings将url和method关联起来
/*** Initialize the HandlerMappings used by this class.* <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,* we default to BeanNameUrlHandlerMapping.*/private void initHandlerMappings(ApplicationContext context) {this.handlerMappings = null;if (this.detectAllHandlerMappings) {// 获取HandlerMapping实例Map<String, HandlerMapping> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());// 排序OrderComparator.sort(this.handlerMappings);}}else {try {HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);this.handlerMappings = Collections.singletonList(hm);}catch (NoSuchBeanDefinitionException ex) {// Ignore, we'll add a default HandlerMapping later. }}// Ensure we have at least one HandlerMapping, by registering// a default HandlerMapping if no other mappings are found.if (this.handlerMappings == null) {this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);if (logger.isDebugEnabled()) {logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");}}}
4.2 springmvc运行阶段
4.2.1 上面已经说过每次请求都会执行servlet的service()方法,FrameworkServlet重写了这个方法。
/*** Override the parent class implementation in order to intercept PATCH* requests.*/@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 获取请求方式例如 get / postString method = request.getMethod();if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {processRequest(request, response);}else {super.service(request, response);}}
4.2.2 然后会根据请求方式去httpsrevlet中调用具体的方法。
protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {String method = req.getMethod();// get请求if (method.equals(METHOD_GET)) {long lastModified = getLastModified(req);if (lastModified == -1) {// servlet doesn't support if-modified-since, no reason// to go through further expensive logic doGet(req, resp);} else {long ifModifiedSince;try {ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);} catch (IllegalArgumentException iae) {// Invalid date header - proceed as if none was setifModifiedSince = -1;}if (ifModifiedSince < (lastModified / 1000 * 1000)) {maybeSetLastModified(resp, lastModified);doGet(req, resp);} else {resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);}}// head请求} else if (method.equals(METHOD_HEAD)) {long lastModified = getLastModified(req);maybeSetLastModified(resp, lastModified);doHead(req, resp);// post请求} else if (method.equals(METHOD_POST)) {doPost(req, resp);// put请求} else if (method.equals(METHOD_PUT)) {doPut(req, resp);} else if (method.equals(METHOD_DELETE)) {doDelete(req, resp);} else if (method.equals(METHOD_OPTIONS)) {doOptions(req,resp);} else if (method.equals(METHOD_TRACE)) {doTrace(req,resp);} else {// 以上都不满足, 说明请求方式有问题。String errMsg = lStrings.getString("http.method_not_implemented");Object[] errArgs = new Object[1];errArgs[0] = method;errMsg = MessageFormat.format(errMsg, errArgs);resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);}}
4.2.3 FrameworkServlet重写了这些方法,并最终都是通过 processRequest( ) 方法来处理请求的。
@Overrideprotected final void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {processRequest(request, response);}/*** Delegate POST requests to {@link #processRequest}.* @see #doService*/@Overrideprotected final void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {processRequest(request, response);}/*** Delegate PUT requests to {@link #processRequest}.* @see #doService*/@Overrideprotected final void doPut(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {processRequest(request, response);}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {long startTime = System.currentTimeMillis();Throwable failureCause = null;// 获取当前请求的相关信息LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();LocaleContext localeContext = buildLocaleContext(request);RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());// 初始化 ContextHolders initContextHolders(request, localeContext, requestAttributes);try {// 最终处理是dispatcherServlet 中的doService()方法调用 doDispatch()来处理的 doService(request, response);}
4.2.4 doDispatch() 方法的主要过程是通过 HandlerMapping 获取 Handler,再找到用于执行它的 HandlerAdapter,执行 Handler 后得到 ModelAndView
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 获取处理请求的具体方法mappedHandler = getHandler(processedRequest);if (mappedHandler == null || mappedHandler.getHandler() == null) {noHandlerFound(processedRequest, response);return;}// 获取处理请求的适配器HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (logger.isDebugEnabled()) {logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);}if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}// 检查请求路径if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 得到modelandviewmv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(request, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}// 处理请求结果 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Error err) {triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}
4.2.5 跳转页面
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {boolean errorView = false;// 有异常就直接抛出异常if (exception != null) {if (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException) exception).getModelAndView();}else {Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}// 所有条件都符合就跳转页面if (mv != null && !mv.wasCleared()) {render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}
转载于:https://www.cnblogs.com/wlwl/p/10136814.html
SpringMVC源码解析相关推荐
- 筑基期第一式:SpringMVC源码解析
文章目录 SpringMVC源码解析 SPI机制 案例 SpringMVC中SPI的使用 初始化IOC容器与九大组件 初始化容器 初始化九大组件 小结 SpringMVC如何处理一个请求 doDisp ...
- SpringMVC源码解析(四)——请求处理
2019独角兽企业重金招聘Python工程师标准>>> 前言 这一篇,将着手介绍一次请求的处理.用到了 HandlerMapping.HandlerAdapter 知识,如果遇到不是 ...
- SpringMVC源码解析与思考
首先要知道servletContext与servletConfig servletContext是web应用级别,是jvm进程级别:servletConfig是servlet服务级别,是线程级别. 定 ...
- SpringMVC源码解析HandlerMethod
被 RequestMapping 注解封印的方法模型类. 封装了关于处理器方法信息的方法和bean类 . 提供了对方法参数,方法返回值,方法注释等方便地访问入口. 该类可以使用bean实例或具有bea ...
- SpringMVC源码解析之Last-Modified缓存机制
Spring MVC 支持HTTP协议的 Last-Modified 缓存机制. 支持上次修改的HTTP请求,以方便内容缓存. 相同的合同作为Servlet API中的getLastModified方 ...
- SpringMVC源码解析 - HandlerAdapter - HandlerMethodArgumentResolver
HandlerMethodArgumentResolver主要负责执行handler前参数准备工作. 看个例子,红色部分的id初始化,填充值就是它干的活: 1 @RequestMapping(valu ...
- clickhouse原理解析与开发实战 pdf_重识SSM,“超高频面试点+源码解析+实战PDF”,一次性干掉全拿走...
重识SSM,"超高频面试点"+"源码解析"+"实战PDF",一次性干掉全拿走!! 01 超高频面试点知识篇 1.1 Spring超高频面试点 ...
- yolov3之pytorch源码解析_springmvc源码架构解析之view
说在前面 前期回顾 sharding-jdbc源码解析 更新完毕 spring源码解析 更新完毕 spring-mvc源码解析 更新完毕 spring-tx源码解析 更新完毕 spring-boot源 ...
- tns03505 无法解析名称_SpringBootWeb源码解析SpringMVC自动配置
SpringMVC自动配置 在 Spring Boot 中引入了 spring-boot-starter-web 依赖,并完成了 DispatcherServlet 的自动配置之后,便会通过 WebM ...
最新文章
- linux sed 小数点,每天进步一点点——linux——sed
- 安装GitLab,Jenkins,及自动化上线
- Node Express4.x 片段视图 partials
- Windows 8操作技巧之快捷键大全
- 4.设计包(design package)
- flv怎么转换成html5,怎么转asf格式 如何将flv格式转换成asf格式?
- java 实现 excel normsdist_Excel函数NormSDist和NormSInv的VB实现
- win11/ win10 C盘扩容教程
- 数学用计算机开方,开方(数学术语)_百度百科
- 【5 于博士Cadence SPB15.7 快速入门视频】allegro的测量工具的使用
- Android 架构组件之 Room
- RabbitMQ的安装与配置
- L1-054 福到了 (15 分)C语言
- EHIGH恒高:大话UWB技术之TDOA与TOF两种技术方案对比
- PHP判断ip是否在指定IP段内(微信支付回调IP段验证)
- 直坐标机械手的优缺点都有哪些?
- LayUI可选择可输入下拉框
- 【华为机试真题 Python】素数之积
- 不知道什么时候开始,35 岁突然成了 IT 人职业生涯的终结年龄
- Himall商城插件内部成员\获取已安装的插件信息\获取指定的插件信息