SpringMVC详解

一.搭建项目

1.新建项目 选中Spring ->Spring MVC 模块 点击next 和 Finish


注意:将生产的lib文件移动到WEB-INF目录下

2.配置 web.xml和SpringMVCxml文件


<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"

        version="4.0">

    <context-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>/WEB-INF/applicationContext.xml</param-value>

    </context-param>

    <listener>

        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

    </listener>

    <servlet>

        <servlet-name>dispatcher</servlet-name>

        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <init-param>

            <param-name>contextConfigLocation</param-name>

            <param-value>/WEB-INF/springmvc.xml</param-value>

        </init-param>

        <load-on-startup>1</load-on-startup>

    </servlet>

    <servlet-mapping>

        <servlet-name>dispatcher</servlet-name>

        <url-pattern>/</url-pattern>

    </servlet-mapping>

</web-app>


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

      xmlns:context="http://www.springframework.org/schema/context"

      xmlns:mvc="http://www.springframework.org/schema/mvc"

      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd

        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

    <!-- 配置包扫描 -->

    <context:component-scan base-package="com.example.*"></context:component-scan>

    <!-- 配置视图解析器 -->

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">

        <property name = "prefix" value="/WEB-INF/views/"></property>

        <property name = "suffix" value = ".jsp"></property>

    </bean>

</beans>


3.添加index.jsp文件 在WEB-INF/views目录下


<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>

  <head>

    <title>$Title$</title>

  </head>

  <body>

  ${message}

  </body>

</html>


4.添加handler处理器


@Controller

class HelloController {

    @RequestMapping("/hello")

    public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {

        ModelAndView mav = new ModelAndView("index");

        mav.addObject("message", "Hello Spring MVC");

        return mav;

    }

    @RequestMapping("/helloRes")

    @ResponseBody

    public String helloResponse() {

        return "hello SpringMVC";

    }

}

5.添加本地tomcat启动项目

1.edit configration中选中local tomcat


2.选中项目war包


注意 application context 可以理解是项目的对外名称,端口后必须和配置的一致

6.启动tomcat



二执行流程

1.时序图


2.执行流程

1、前端请求到前端控制器DispatcherServlet

2、DispatcherServlet收到请求调用处理映射器HandlerMapping

3、处理映射器根据请求url找到具体的处理器(ApplicationContext初始化时用Map保存所有的url和controller对应关系),生成处理器执行链HandlerExecutionChain(包含处理器对象和处理器拦截器)返回给DispatcherServlet

4、DispatcherServlet根据处理器Handler获取对应的适配器

5、HandlerAdapter调用处理器Handler找到具体的Controller

6、Controller 执行完成后返回ModelAndView

7、HandlerAdapter返回ModelAndView

8、DispatcherServlet统一将返回的ModelAndView派送到ViewResolve(视图解析器)解析

9,视图解析器解析之后返回 View

10、对View进行渲染

11、回显页面

3.容器的初始化

1.DispatcherServlet的父类HttpServletBean中找到初始化的init()方法;

2.在init()方法中又会调用FrameworkServlet类的initServletBean()方法,最终会调用refresh()方法;

3.IOC容器初始化完成之后会调用DispatcherServlet类的onRefresh()方法,完成SpringMVC九大组件的初始化。

三.源码分析(5.2.3)

一.将请求和url与Controller绑定

1.在spring容器启动时,在实例化Controller bean后,调用afterPropertiesSet() ->initHandlerMethods()->

processCandidateBean() ->detectHandlerMethods 解析bean的方法

```java

protected void detectHandlerMethods(Object handler) {

        Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();

        if (handlerType != null) {

            Class<?> userType = ClassUtils.getUserClass(handlerType);

            Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> {

                try {

                    return this.getMappingForMethod(method, userType);

                } catch (Throwable var4) {

                    throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);

                }

            });

            if (this.logger.isTraceEnabled()) {

                this.logger.trace(this.formatMappings(userType, methods));

            }

            methods.forEach((method, mapping) -> {

                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);

                // 为每个方法绑定url

                this.registerHandlerMethod(handler, invocableMethod, mapping);

            });

        }

    }

```

给每个controller中的方法绑定url

```java

public void register(T mapping, Object handler, Method method) {

            if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && AbstractHandlerMethodMapping.KotlinDelegate.isSuspend(method)) {

                throw new IllegalStateException("Unsupported suspending handler method detected: " + method);

            } else {

                this.readWriteLock.writeLock().lock();

                try {

                    HandlerMethod handlerMethod = AbstractHandlerMethodMapping.this.createHandlerMethod(handler, method);

                    this.validateMethodMapping(handlerMethod, mapping);

                    // 映射关系放入map中

                    this.mappingLookup.put(mapping, handlerMethod);

                    List<String> directUrls = this.getDirectUrls(mapping);

                    Iterator var6 = directUrls.iterator();

                    while(var6.hasNext()) {

                        String url = (String)var6.next();

                        this.urlLookup.add(url, mapping);

                    }

                    String name = null;

                    if (AbstractHandlerMethodMapping.this.getNamingStrategy() != null) {

                        name = AbstractHandlerMethodMapping.this.getNamingStrategy().getName(handlerMethod, mapping);

                        this.addMappingName(name, handlerMethod);

                    }

                    CorsConfiguration corsConfig = AbstractHandlerMethodMapping.this.initCorsConfiguration(handler, method, mapping);

                    if (corsConfig != null) {

                        this.corsLookup.put(handlerMethod, corsConfig);

                    }

                    this.registry.put(mapping, new AbstractHandlerMethodMapping.MappingRegistration(mapping, handlerMethod, directUrls, name));

                } finally {

                    this.readWriteLock.writeLock().unlock();

                }

            }

```

二.前端请求找到对应的Controller

根据servlet的 service()方法->doService()->doDispatch()

```java

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

        HttpServletRequest processedRequest = request;

        HandlerExecutionChain mappedHandler = null;

        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {

            try {

                ModelAndView mv = null;

                Object dispatchException = null;

                try {

                    processedRequest = this.checkMultipart(request);

                    multipartRequestParsed = processedRequest != request;

                    // 获取handle

                    mappedHandler = this.getHandler(processedRequest);

                    if (mappedHandler == null) {

                        this.noHandlerFound(processedRequest, response);

                        return;

                    }

// 获取adapter

                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());

                    String method = request.getMethod();

                    boolean isGet = "GET".equals(method);

                    if (isGet || "HEAD".equals(method)) {

                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());

                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {

                            return;

                        }

                    }

                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {

                        return;

                    }

// 调用handler的具体方法

                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                    if (asyncManager.isConcurrentHandlingStarted()) {

                        return;

                    }

                    this.applyDefaultViewName(processedRequest, mv);

                    mappedHandler.applyPostHandle(processedRequest, response, mv);

                } catch (Exception var20) {

                    dispatchException = var20;

                } catch (Throwable var21) {

                    dispatchException = new NestedServletException("Handler dispatch failed", var21);

                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);

            } catch (Exception var22) {

                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);

            } catch (Throwable var23) {

                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));

            }

        } finally {

            if (asyncManager.isConcurrentHandlingStarted()) {

                if (mappedHandler != null) {

                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);

                }

            } else if (multipartRequestParsed) {

                this.cleanupMultipart(processedRequest);

            }

        }

    }

```

handleInternal

```java

protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        this.checkRequest(request);

        ModelAndView mav;

        if (this.synchronizeOnSession) {

            HttpSession session = request.getSession(false);

            if (session != null) {

                Object mutex = WebUtils.getSessionMutex(session);

                synchronized(mutex) {

                    mav = this.invokeHandlerMethod(request, response, handlerMethod);

                }

            } else {

                mav = this.invokeHandlerMethod(request, response, handlerMethod);

            }

        } else {

            mav = this.invokeHandlerMethod(request, response, handlerMethod);

        }

        if (!response.containsHeader("Cache-Control")) {

            if (this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {

                this.applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);

            } else {

                this.prepareResponse(response);

            }

        }

        return mav;

    }

```

invokeHandlerMethod

```java

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        ServletWebRequest webRequest = new ServletWebRequest(request, response);

        ModelAndView var15;

        try {

            WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);

            ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);

            ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);

            if (this.argumentResolvers != null) {

                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);

            }

            if (this.returnValueHandlers != null) {

                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);

            }

            invocableMethod.setDataBinderFactory(binderFactory);

            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

            ModelAndViewContainer mavContainer = new ModelAndViewContainer();

            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));

            modelFactory.initModel(webRequest, mavContainer, invocableMethod);

            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);

            asyncWebRequest.setTimeout(this.asyncRequestTimeout);

            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

            asyncManager.setTaskExecutor(this.taskExecutor);

            asyncManager.setAsyncWebRequest(asyncWebRequest);

            asyncManager.registerCallableInterceptors(this.callableInterceptors);

            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

            Object result;

            if (asyncManager.hasConcurrentResult()) {

                result = asyncManager.getConcurrentResult();

                mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];

                asyncManager.clearConcurrentResult();

                LogFormatUtils.traceDebug(this.logger, (traceOn) -> {

                    String formatted = LogFormatUtils.formatValue(result, !traceOn);

                    return "Resume with async result [" + formatted + "]";

                });

                invocableMethod = invocableMethod.wrapConcurrentResult(result);

            }

            invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);

            if (asyncManager.isConcurrentHandlingStarted()) {

                result = null;

                return (ModelAndView)result;

            }

            var15 = this.getModelAndView(mavContainer, modelFactory, webRequest);

        } finally {

            webRequest.requestCompleted();

        }

        return var15;

    }

```

invokeAndHandle 反射调用方法

```java

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {

// 反射调用Controller中的具体方法

        Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);

        this.setResponseStatus(webRequest);

        if (returnValue == null) {

            if (this.isRequestNotModified(webRequest) || this.getResponseStatus() != null || mavContainer.isRequestHandled()) {

                this.disableContentCachingIfNecessary(webRequest);

                mavContainer.setRequestHandled(true);

                return;

            }

        } else if (StringUtils.hasText(this.getResponseStatusReason())) {

            mavContainer.setRequestHandled(true);

            return;

        }

        mavContainer.setRequestHandled(false);

        Assert.state(this.returnValueHandlers != null, "No return value handlers");

        try {

            this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);

        } catch (Exception var6) {

            if (this.logger.isTraceEnabled()) {

                this.logger.trace(this.formatErrorForReturnValue(returnValue), var6);

            }

            throw var6;

        }

    }

```

解析结果,渲染视图

```java

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {

        boolean errorView = false;

        if (exception != null) {

            if (exception instanceof ModelAndViewDefiningException) {

                this.logger.debug("ModelAndViewDefiningException encountered", exception);

                mv = ((ModelAndViewDefiningException)exception).getModelAndView();

            } else {

                Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;

                mv = this.processHandlerException(request, response, handler, exception);

                errorView = mv != null;

            }

        }

        if (mv != null && !mv.wasCleared()) {

        // 渲染视图

            this.render(mv, request, response);

            if (errorView) {

                WebUtils.clearErrorRequestAttributes(request);

            }

        } else if (this.logger.isTraceEnabled()) {

            this.logger.trace("No view rendering, null ModelAndView returned.");

        }

        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {

            if (mappedHandler != null) {

                mappedHandler.triggerAfterCompletion(request, response, (Exception)null);

            }

        }

    }

```

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 228,333评论 6 531
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 98,491评论 3 416
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 176,263评论 0 374
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,946评论 1 309
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,708评论 6 410
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,186评论 1 324
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,255评论 3 441
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,409评论 0 288
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,939评论 1 335
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,774评论 3 354
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,976评论 1 369
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,518评论 5 359
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,209评论 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,641评论 0 26
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,872评论 1 286
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,650评论 3 391
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,958评论 2 373

推荐阅读更多精彩内容

  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,592评论 0 11
  • 彩排完,天已黑
    刘凯书法阅读 4,260评论 1 3
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 125,537评论 2 7