Spring-MVC运行原理
一、 Spring-MVC的对象初始化,即 bean放入context的beanFactory中。
1. 对象的初始化工作主要在org.springframework.web.servlet.FrameworkServlet类中的initServletBean方法中完成,initServletBean方法最终会调用到
org.springframework.context.support.AbstractApplicationContext类的refresh方法,refresh方法是主要的bean的初始化方法。refresh方法又调用类里面的obtainFreshBeanFactory方法。
2. org.springframework.beans.factory.support.DefaultListableBeanFactory为默认的BeanFactory,
DefaultListableBeanFactory 类中的registerBeanDefinition方法为保存对象列表信息的主要方法,beanFactory中的对象存放到成员变量Map<String, BeanDefinition> beanDefinitionMap中。
- private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
- //---------------------------------------------------------------------
- // Implementation of BeanDefinitionRegistry interface
- //---------------------------------------------------------------------
- public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
- throws BeanDefinitionStoreException {
- Assert.hasText(beanName, "Bean name must not be empty");
- Assert.notNull(beanDefinition, "BeanDefinition must not be null");
- if (beanDefinition instanceof AbstractBeanDefinition) {
- try {
- ((AbstractBeanDefinition) beanDefinition).validate();
- }
- catch (BeanDefinitionValidationException ex) {
- throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
- "Validation of bean definition failed", ex);
- }
- }
- synchronized (this.beanDefinitionMap) {
- Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
- if (oldBeanDefinition != null) {
- if (!this.allowBeanDefinitionOverriding) {
- throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
- "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
- "': There is already [" + oldBeanDefinition + "] bound.");
- }
- else {
- if (this.logger.isInfoEnabled()) {
- this.logger.info("Overriding bean definition for bean '" + beanName +
- "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
- }
- }
- }
- else {
- this.beanDefinitionNames.add(beanName);
- this.frozenBeanDefinitionNames = null;
- }
- this.beanDefinitionMap.put(beanName, beanDefinition);
- }
- resetBeanDefinition(beanName);
- }
二、 Spring-MVC中Controller中的method与 RequestMappingURL的初始化
1. Controller中的method与 RequestMappingURL的映射关系绑定初始化工作主要在spring-webmvc.jar中完成。这个jar 文件包含Spring MVC 框架相关的所有类,
包括框架的Servlets,Web MVC框架,控制器和视图支持。
2. SpringMVC在容器初始化时,绑定请求URL映射到相应的Controller中的方法的工作主要在org.springframework.web.servlet.handler.AbstractHandlerMethodMapping类中的initHandlerMethods方法完成,
AbstractHandlerMethodMapping实现了Spring的org.springframework.beans.factory.InitializingBean接口,在InitializingBean的afterPropertiesSet即调用了initHandlerMethods。
MappingURL与Controller对应方法的映射关系在servlet容器初始化时保存到 AbstractHandlerMethodMapping中的成员变量urlMap中。
AbstractHandlerMethodMapping类的initHandlerMethods为protected修饰 ,可被子类重写。
三、 请求URL到达映射处理的Controller的Method前的逻辑。
执行业务方法的逻辑主要在org.springframework.web.servlet.DispatcherServlet类的doDispatch方法中。
以下为doDispatch方法的代码:
- /**
- * Process the actual dispatching to the handler.
- * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
- * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
- * to find the first that supports the handler class.
- * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
- * themselves to decide which methods are acceptable.
- * @param request current HTTP request
- * @param response current HTTP response
- * @throws Exception in case of any kind of processing failure
- */
- protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
- HttpServletRequest processedRequest = request;
- HandlerExecutionChain mappedHandler = null;
- int interceptorIndex = -1;
- try {
- ModelAndView mv;
- boolean errorView = false;
- try {
- processedRequest = checkMultipart(request);
- // Determine handler for the current request.
- mappedHandler = getHandler(processedRequest, false);
- if (mappedHandler == null || mappedHandler.getHandler() == null) {
- noHandlerFound(processedRequest, response);
- return;
- }
- // Determine handler adapter for the current request.
- 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()) {
- String requestUri = urlPathHelper.getRequestUri(request);
- logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
- }
- if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
- return;
- }
- }
- // Apply preHandle methods of registered interceptors.
- HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
- if (interceptors != null) {
- for (int i = 0; i < interceptors.length; i++) {
- HandlerInterceptor interceptor = interceptors[i];
- if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
- triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
- return;
- }
- interceptorIndex = i;
- }
- }
- // Actually invoke the handler.
- mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
- // Do we need view name translation?
- if (mv != null && !mv.hasView()) {
- mv.setViewName(getDefaultViewName(request));
- }
- // Apply postHandle methods of registered interceptors.
- if (interceptors != null) {
- for (int i = interceptors.length - 1; i >= 0; i--) {
- HandlerInterceptor interceptor = interceptors[i];
- interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
- }
- }
- }
- catch (ModelAndViewDefiningException ex) {
- logger.debug("ModelAndViewDefiningException encountered", ex);
- mv = ex.getModelAndView();
- }
- catch (Exception ex) {
- Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
- mv = processHandlerException(processedRequest, response, handler, ex);
- errorView = (mv != null);
- }
- // Did the handler return a view to render?
- if (mv != null && !mv.wasCleared()) {
- render(mv, processedRequest, response);
- if (errorView) {
- WebUtils.clearErrorRequestAttributes(request);
- }
- }
- else {
- if (logger.isDebugEnabled()) {
- logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
- "': assuming HandlerAdapter completed request handling");
- }
- }
- // Trigger after-completion for successful outcome.
- triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
- }
- catch (Exception ex) {
- // Trigger after-completion for thrown exception.
- triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
- throw ex;
- }
- catch (Error err) {
- ServletException ex = new NestedServletException("Handler processing failed", err);
- // Trigger after-completion for thrown exception.
- triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
- throw ex;
- }
- finally {
- // Clean up any resources used by a multipart request.
- if (processedRequest != request) {
- cleanupMultipart(processedRequest);
- }
- }
- }
主要执行的过程有四个步骤,如下所示:
1. 通过调用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping类的getHandlerInternal方法来获得相应的请求url的映射的controller和method的HandlerMethod,
AbstractHandlerMethodMapping中的成员变量urlMap保存的即为servlet容器启动时初始化的RequestMapping映射的Controller和Method的信息。
getHandlerInternal方法代码如下:
- /**
- * Look up the best-matching handler method for the current request.
- * If multiple matches are found, the best match is selected.
- * @param lookupPath mapping lookup path within the current servlet mapping
- * @param request the current request
- * @return the best-matching handler method, or {@code null} if no match
- * @see #handleMatch(Object, String, HttpServletRequest)
- * @see #handleNoMatch(Set, String, HttpServletRequest)
- */
- protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
- List<Match> matches = new ArrayList<Match>();
- List<T> directPathMatches = this.urlMap.get(lookupPath);
- if (directPathMatches != null) {
- addMatchingMappings(directPathMatches, matches, request);
- }
- if (matches.isEmpty()) {
- // No choice but to go through all mappings
- addMatchingMappings(this.handlerMethods.keySet(), matches, request);
- }
- if (!matches.isEmpty()) {
- Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
- Collections.sort(matches, comparator);
- if (logger.isTraceEnabled()) {
- logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
- }
- Match bestMatch = matches.get(0);
- if (matches.size() > 1) {
- Match secondBestMatch = matches.get(1);
- if (comparator.compare(bestMatch, secondBestMatch) == 0) {
- Method m1 = bestMatch.handlerMethod.getMethod();
- Method m2 = secondBestMatch.handlerMethod.getMethod();
- throw new IllegalStateException(
- "Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
- m1 + ", " + m2 + "}");
- }
- }
- handleMatch(bestMatch.mapping, lookupPath, request);
- return bestMatch.handlerMethod;
- }
- else {
- return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
- }
- }
HandlerMethod类主要包括的信息有 匹配处理的Controller ,处理方法Method,以及方法的传入参数 parameters等信息。HandlerMethod的主要成员变量代码如下:
- public class HandlerMethod {
- /** Logger that is available to subclasses */
- protected final Log logger = LogFactory.getLog(HandlerMethod.class);
- private final Object bean;
- private final Method method;
- private final BeanFactory beanFactory;
- private MethodParameter[] parameters;
- private final Method bridgedMethod;
- }
bean为url映射匹配到Controller, method为映射到的处理方法。
2. 执行url匹配的过滤器的preHandle方法。
3. 执行主要的业务过程处理方法,即执行步骤1中找到的Controller对应的Method。
执行主要的业务处理方法的是在org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter类的invokeHandlerMethod方法中调用。
- /**
- * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView} if view resolution is required.
- */
- private ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response,
- HandlerMethod handlerMethod) throws Exception {
- ServletWebRequest webRequest = new ServletWebRequest(request, response);
- WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
- ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
- ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);
- ModelAndViewContainer mavContainer = new ModelAndViewContainer();
- mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
- modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
- mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
- requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
- modelFactory.updateModel(webRequest, mavContainer);
- if (mavContainer.isRequestHandled()) {
- return null;
- }
- else {
- ModelMap model = mavContainer.getModel();
- ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
- if (!mavContainer.isViewReference()) {
- mav.setView((View) mavContainer.getView());
- }
- if (model instanceof RedirectAttributes) {
- Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
- RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
- }
- return mav;
- }
- }
业务方法调用主要在org.springframework.web.method.support.InvocableHandlerMethod类的invokeForRequest方法中,过程包括组装request的请求参数传入到handleMethod的参数数组args[]中,方法调用等。
方法通过java的反射机制执行,即java.lang.reflect.Method.invoke(Object controller, Object... args); 参数一controller为方法所属的Controller,参数二args为请求参数
- /**
- * Invoke the method after resolving its argument values in the context of the given request. <p>Argument
- * values are commonly resolved through {@link HandlerMethodArgumentResolver}s. The {@code provideArgs}
- * parameter however may supply argument values to be used directly, i.e. without argument resolution.
- * Examples of provided argument values include a {@link WebDataBinder}, a {@link SessionStatus}, or
- * a thrown exception instance. Provided argument values are checked before argument resolvers.
- *
- * @param request the current request
- * @param mavContainer the ModelAndViewContainer for this request
- * @param providedArgs "given" arguments matched by type, not resolved
- * @return the raw value returned by the invoked method
- * @exception Exception raised if no suitable argument resolver can be found, or the method raised an exception
- */
- public final Object invokeForRequest(NativeWebRequest request,
- ModelAndViewContainer mavContainer,
- Object... providedArgs) throws Exception {
- Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
- if (logger.isTraceEnabled()) {
- StringBuilder builder = new StringBuilder("Invoking [");
- builder.append(this.getMethod().getName()).append("] method with arguments ");
- builder.append(Arrays.asList(args));
- logger.trace(builder.toString());
- }
- Object returnValue = invoke(args);
- if (logger.isTraceEnabled()) {
- logger.trace("Method [" + this.getMethod().getName() + "] returned [" + returnValue + "]");
- }
- return returnValue;
- }
4. 执行url匹配的过滤器的postHandle方法。
Spring-MVC运行原理的更多相关文章
- struts1,struts2,hibernate,spring的运行原理结构图
一.struts1运行原理 1.初始化:struts框架的总控制器ActionServlet是一个Servlet,它在web.xml中配置成自动启动的Servlet,在启动时总控制器会读取配置文件(s ...
- (4.1)Spring MVC执行原理和基于Java的配置过程
一.Spring MVC执行原理和基于Java配置的配置过程 (一)Spring MVC执行过程,大致为7步. 所有的请求都会经过Spring的一个单例的DispacherServlet. Dispa ...
- Spring MVC简单原理
Spring MVC原理 针对有Java Web基础.Spring基础和Spring MVC使用经验者. 前言 目前基于Java的web后端,Spring生态应该是比较常见了.虽然现在流行前后端分离, ...
- spring Mvc 执行原理 及 xml注解配置说明 (六)
Spring MVC 执行原理 在 Spring Mvc 访问过程里,每个请求都首先经过 许多的过滤器,经 DispatcherServlet 处理; 一个Spring MVC工程里,可以配置多个的 ...
- Spring MVC执行原理和基于Java的配置过程
一.Spring MVC执行原理和基于Java配置的配置过程 (一)Spring MVC执行过程,大致为7步. 所有的请求都会经过Spring的一个单例的DispacherServlet. Dispa ...
- Spring MVC工作原理(好用版)
Spring MVC工作原理 参考: SpringMVC工作原理 - 平凡希 - 博客园https://www.cnblogs.com/xiaoxi/p/6164383.html SpringMVC的 ...
- Spring Boot运行原理
概述 本文主要写了下Spring Boot运行原理,还有一个小例子. Spring4.x提供了基于条件来配置Bean的能力,而Spring Boot的实现也是基于这一原理的. Spring Boot关 ...
- Spring MVC工作原理 及注解说明
SpringMVC框架介绍 1) spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面. Spring 框架提供了构建 Web 应用程序的全功 ...
- Spring MVC 底层原理
参考博客:http://www.cnblogs.com/xiaoxi/p/6164383.html Spring MVC处理的流程: 具体执行步骤如下: 1 首先用户发送请求给前端控制器,前端控制器根 ...
- Spring Boot 运行原理
Spring Boot并没有任何新的技术,全都是基于Spring4提供的技术,用优秀的设计,为Web开发提供了一套新的方式. 在HelloWorld中,我们没有进行任何显示的配置,但是程序还是运行起来 ...
随机推荐
- Exce 快捷键 tips
1. 填充快捷键 ctrl+R 向下填充 CTRL+D 向右填充 2. 筛选快捷键 CTRL+SHIFT+L 3. 移动到当前区域的边缘: Ctrl + shift + 方向箭头 4. 字符连接:& ...
- 如何用jquery获取form表单的值
$(function(){ $('.btn').click(function(){ alert($('#form').serialize()); }) }) 这样就获取到了 #form的值.
- Java并发编程随笔
死锁:两个线程互相等待对方释放锁才可以继续运行. 避免死锁的常见方法: 1.避免一个线程同时获取多个锁 2.避免一个线程在锁内同时占用多个资源,尽量保证一个锁只占用一个资源 3.尝试使用定时锁,使用l ...
- ReactiveX 学习笔记(25)使用 RxJS + Vue.js 调用 REST API
JSON : Placeholder JSON : Placeholder (https://jsonplaceholder.typicode.com/) 是一个用于测试的 REST API 网站. ...
- Redis 数据类型归纳
Redis的数据类型从整体上看,都是Key-Value键值对的模型,数据类型更确切地说,应该是Value的数据类型,比如string,set,list等,都是key值对应的Value的数据集合格式.不 ...
- StreamReader和StreamWriter说明
StreamReader/StreamWriter操作的是字符数据(char),而FileStream操作的是字节数据(byte) FileStream与StreamXXXX类的默认编码都是UTF8, ...
- Grafana报警--通知渠道配置
最近研究了prometheus+grafana的系统监控,使用grafana的报警功能,grafana支持很多种通知渠道,下文记录使用到的几种notification channels,分别是emai ...
- Pycharm配置支持vue语法
1. 2. 3. 4. 5.
- C++读取与保持图片
#include<iostream> using namespace std; void main(void) { //保存输入图像文件名和输出图像文件名 ]; ]; //图像数据长度 i ...
- gb2312,gbk,utf8的区别
GB2312编码大约包含6000多汉字(不包括特殊字符),编码范围为第一位b0-f7,第二位编码范围为a1-fe(第一位为cf时,第二位为a1-d3),计算一下汉字个数为6762个汉字.当然还有其他的 ...