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>(); /** * 容器启动时 */ @Override public 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()); /** * 到上面容器就就初始化完成,可以对外提供服务了。 * 现在可以每个方法断点调试看运行过程。 */ } } } } } /** * 处理用户请求 */ @Override protected 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); } @Override protected 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. */ @Override public 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. */ @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取请求方式例如 get / post String 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 set ifModifiedSince = -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( ) 方法来处理请求的。
@Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } /** * Delegate POST requests to {@link #processRequest}. * @see #doService */ @Override protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } /** * Delegate PUT requests to {@link #processRequest}. * @see #doService */ @Override protected 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; } // 得到modelandview mv = 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 afterCompletion if (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); } }