这次文章是springMVC笔记系列时隔一年之后的继续,但是我想先就其中的数据绑定写一串博文。

首先,这里做个插曲。之前的博客文章用的IDE是STS(eclipse的子子孙孙),这次springMVC数据绑定的几篇博客文章,我的示例用的是intellj idea,所以,给不熟悉这个IDE工具的同学做个把屎把尿的插曲介绍。

这里需要特别注意的是,idea和eclipse在一些概念上有区别,十分容易混淆,这里做个说明:

eclipse

idea

workspace

project

project

module

idea中的project相当于eclipse中的workspace,而idea中的module相当于eclipse中的project。但是在组织形式上也会有些许的不同,这个我们后面再说。

我们先在idea下把Maven webapp项目建好,步骤如图,文字自行脑补。

 

idea 怎么运行springmvc项目 idea启动springmvc_spring

 

 

(这里说明一下,由于当中idea版本升过级,重新建了个项目所以这里截图看到的是ideaSpringMVC,其实应该是ideaSpringMVC2。)

idea新建maven webapp没有src,要自己建,T T。

idea 怎么运行springmvc项目 idea启动springmvc_测试_02

 

idea建立的文件夹可以做用处标记,源文件文件夹、资源文件夹等等。不然右击文件夹新建的时候会找不到相应的模板。

方便还是挺方便的。

 

idea 怎么运行springmvc项目 idea启动springmvc_ViewUI_03

 

打住,要开始建立我们在idea下的第一个springMVC了,你还记得我一年多前本系列的前面的十几篇文章吗?好吧,如果你已经忘得差不多了,也别急着翻了,我将关键的原理在这里重新整理复习一下,做个插叙。

这是Spring团队贡献给我们的一种MVC的实现方式——前端控制器。

Front controller前端控制器 负责 分发、调度

Controller控制器 负责 业务数据抽取

View Template 负责页面呈现

首先,http请求由客户端发送给服务端的前端控制器,前端控制器知道该请求应该由谁来进行处理,将该请求分发给相应的控制器;之后,控制器知道业务逻辑的细节,调用相关的业务逻辑,生成业务数据即model,并将业务数据返回给前端控制器;此时,前端控制器再将控制器返回的业务数据发送给业务视图;然后,业务视图来呈现最后的业务页面,将该呈现页面返回给前端控制器;最后,前端控制器将呈现页面返回给浏览器端。

那为什么叫“前端控制器”(调度器)呢?打个比方,以上场景与医院前台类似,接受求医信息,分发挂号到不同的科室,不同的科室了解不同的业务细节。

MVC的核心思想是 业务数据抽取 同 业务数据呈现 分离!

MVC是一种架构模式:程序分层,分工合作;既相互独立,又协同工作。

MVC是一种思考方式:需要将什么信息展现给用户(C)?如何布局(V)?调用哪些业务逻辑(M)?

 

好,我们回到SpringMVC框架,这里需要提到它的几个概念。首先我们来看九大静态概念。

springMVC的九大静态概念

首先是DispatcherServlet,它正式SpringMVC对前端控制器的实现。

用户的请求正式通过springMVC的DispatcherServlet进行了分发,到达相应的Controller;Controller进而生成我们需要的业务数据model;这个model由Controller返回给DispatcherServlet进行传递,传递给相应的View,来完成最终的页面呈现。

第二个概念是Controller。

这个不多讲,它调用业务逻辑,生成相应的model

idea 怎么运行springmvc项目 idea启动springmvc_java_04

第三个概念是HandlerAdapter。首先,什么是Handler?Handler是在DispatcherServlet内部使用的一个类,它其实就是Controller的一个表现形式。在SpringMVC中并没有一个inteface叫做Controller,而是只有一个annotation(注解),@Controller,用以springMVC识别Controller。

既然没有Controller这样一个interface或者abstract class,那么springMVC的DispatcherServlet如何来知道各个Controller呢?那就是Handler。在springMVC的DispatcherServlet调用的各个控制器都是以Handler形式出现的。

那什么是HandlerAdapter呢?好吧,听名字就应该知道,这是一个适配器模式。它的作用就是将各种类型的Handler,是配成springMVC的DispatcherServlet可以使用的Handler。

这样,springMVC的DispatcherServlet就可以轻松调用控制器。

idea 怎么运行springmvc项目 idea启动springmvc_测试_05

 

第四个概念是HandlerIntercepter。HandlerIntercepter是一个拦截器。(Intercepter是拦截器的意思)它在需要被拦截对象的两侧加入一些料。HandlerIntercepter是一个interface。

以下是HandlerIntercepter接口中的三个方法。

HandlerInterceptor 接口中定义了三个方法,我们就是通过这三个方法来对用户的请求进行拦截处理的。

   (1 )preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法。该方法将在请求处理之前进行调用。SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean 类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。

   (2 )postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,由preHandle 方法的解释我们知道这个方法包括后面要说到的afterCompletion 方法都只能是在当前所属的Interceptor 的preHandle 方法的返回值为true 时才能被调用。postHandle 方法,顾名思义就是在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行,这和Struts2 里面的Interceptor 的执行过程有点类型。Struts2 里面的Interceptor 的执行过程也是链式的,只是在Struts2 里面需要手动调用ActionInvocation 的invoke 方法来触发对下一个Interceptor 或者是Action 的调用,然后每一个Interceptor 中在invoke 方法调用之前的内容都是按照声明顺序执行的,而invoke 方法之后的内容就是反向的。

   (3 )afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。 我们的系统日志的拦截在这个方法中,可以记录日志的相关的参数,检测方法的执行。

如果我们定义了实现HandlerIntercepter接口的类,就可以在我们真正调用Controller之前,调用Controller之后,以及最终我们的Mdoel发送到了页面或者说View完成了呈现之后可以做很多事情。

第五个概念是HandlerMapping。Hanldler是DispatcherServlet调用Controller的一个中间过渡对象。mapping则是前端控制器DispatcherServlet与各个控制器之间的映射。所以,HandlerMapping的第一个作用就是,当一个请求到达前端控制器DispatcherServlet时,告诉前端控制器DispatcherServlet这个请求应该由哪个控制器来响应。

HandlerMapping的第二个作用是,用HandlerIntercepter来包裹Controller。

在HandlerMapping执行完之后,它会给DispatcherServlet一个HandlerAdapter。这个HandlerAdapter里面就包含了(1)某个具体的Controller的实例以及(2)这个Controller需要被包裹的HandlerIntercepter。它们会构成一个执行的链条往下走。

第六个概念是HandlerExecutionChain。前面我们说了,HandlerMapping执行完之后返回的HandlerAdapter中包含了Controller和HandlerIntercepter,它们之间构成了一个执行链条。首选,要执行的是所有HandlerIntercepter里面的preHandle,然后再调用Controller里面的某个业务方法,之后执行HandlerIntercepter里面的postHandle,最后是afterCompletion。

 

这里,我插一句。这个看似复杂的机制在springMVC中实现时其实就是利用反射进行实现的。

 

第七个概念是 ModelAndView。它是springMVC中对Model的一种表现形式。当然,springMVC中还有Model类,另外也可以用Java中的Map类来实现Model的功能。如果你在Controller中见过有人用Map、Model这样的类,springMVC都会讲它们统统转换为ModelAndView。因此,这里我们把ModelAndView认为是一个Model的具体表现。

第八个概念是ViewResolver(视图解析器)。它负责告诉DispatcherServlet需要用哪个视图来进行视图的呈现。因此ViewResolver的作用就是根据我们的配置找出那一个视图对象。比如JSTL、JSP等等。

第九个概念是View。负责呈现页面

 

好吧,我们把这些概念串联一起,梳理一下springMVC的基本概念的原理过程:

DispatcherServlet处于核心的地位,它是整个springMVC的MVC实现的桥梁。Controller是springMVC的C;View是用户想看到的东西,即V。访问的过程如下:

Request从浏览器端过来,首选到达DispatcherServlet。因为它是一个servlet,所有Request都可以拦截得到。DispatcherServlet拦截到一个Request之后,它回去找相应的Controller,怎么找呢?通过HandlerMapping。因此,DispatcherServlet就把这个功能代理给了HandlerMapping。HandlerMapping根据自己的配置,这可以用注解来做。通过注解的方式告诉HandlerMapping,那一个Controller是我们要的。因此HandlerMapping可以很容易地找到相应的Controller,对了,还能找到HandlerInterceptor。

HandlerMapping找到了我们的Controller和HandlerInterceptor,把它们形成了我们的HandlerExecutionChain——一个执行链条。就这样,一个Handler或者说HandlerAdapter(一个适配器)返回给我们的DispatcherServlet。接下来,DispatcherServlet将会调用这个返回的一般化的处理器Handler或者HandlerAdapter。

写Controller的目的是什么?当然是生成我们的模型——ModelAndView。ModelAndView为我们的页面呈现提供一些显示的“料”。模型ModelAndView生成之后,被返回给DispatcherServlet。接着需要显示,但DispatcherServlet可不回去自己管显示的事情。这个时候,视图解析器出现了,视图解析器ViewResolver告诉我们哪个视图是用来解析当前这种场景的。因此,DispatcherServlet通过调用ViewResolver的方法返回我们的View对象。 然后,ModelAndView我们模型数据被传递给我们的View,完成了我们的呈现。DispatcherServlet返回给用户,这样用户就可以在浏览器端看到用户界面了。

idea 怎么运行springmvc项目 idea启动springmvc_前端_06

 

好吧,也许你还是觉得过程好复杂。但是,SpringMVC已经为我们做了许多。DispatcherServlet好重要啊!但是SpringMVC框架已经为我们实现了,我们要做的仅仅是配置一下就可以了。HandlerMapping复杂的映射过程也不需要我们知道,我们只要用注解配置就可以了。Controller,这个还是自己得写点东西吧,springMVC才不知道你的业务逻辑是卖皮鞋,还是卖保险呢。HandlerInterceptor是一个接口,需要就实现相应的接口方法,不需要就不写了,毕竟不是所有的应用场景都需要在Controller的前后做点什么。Handler/HandlerAdapter如此紧密的联系需要你过问吗,springMVC自己搞定。ModelAndView仅仅是个类,用来存存数据,甚至可以用Map相关的类对象来存储,你别说你不会,一个key一个数可以了,springMVC或者说DispatcherServlet会自己把这个Map转换成ModelAndView。最后,视图解析器都是现成的,跟HandlerMapping差不多,你只需要知道是哪一种类型就可以了。

所以总结下来,我们所需要写的仅仅是Controller;HandlerInterceptor根据应用场景可以写也可以不写;ModelAndView不是必需的,可以给也可以不给;View等、HandlerMapping等配置的问题而已。Handler/HandlerAdapter不需要管。所以,我们只须要shixianController,其他配置一下就OK了。

下面这个图是从另外一个角度对上述概念进行了模块划分。我们只需要 注意的是,DispatcherServlet处于核心位置,相当于一个懂得放权的好老板,它只需要调度就可以了。

老板DispatcherServlet对员工HandlerMapping说:“你告诉我须要调哪个Controller啊”

老板DispatcherServlet对员工Handler说:“你让Controller工作吧”

老板DispatcherServlet对员工ViewResolver说:“你告诉我谁是我需要的View啊”

老板DispatcherServlet对员工View说:“你去把数据呈现以下,把页面做出来吧”

。。。 

 

 

好吧好吧,springMVC原理要点回顾完毕之后,还是回到intellj idea的本文示例上吧。

 

我们在com.happyBKs.controller下定义一个类TestController,用@Controller注解标记为一个控制器。我

package com.happyBKs.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * Created by happyBKs on 2016/5/24.
 */

@Controller
@RequestMapping("/hello")
public class TestController {

    //localhost:8080/home/mvc或localhost:8080/web应用名称/home/mvc
    @RequestMapping("/world")
    public String helloMVC(){
        return "first";
    }

}

当然,springMVC在搜索控制器的时候需要定义搜索包的范围,所以,我们需要定义一个springMVC的配置文件。而定义springMVC的配置文件之前,需要在中定义好前端控制器DispatcherServlet,以及相应的配置文件路径。

web.xml如下:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <!-- DispatcherServlet, Spring MVC的核心 -->
  <servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class> org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- DispatcherServlet对应的上下文配置, 默认为/WEB-INF/$servlet-name$-servlet.xml
     -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/configs/spring/mvc-dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <!-- mvc-dispatcher拦截所有的请求-->
    <url-pattern>/</url-pattern>
  </servlet-mapping>

</web-app>

注意:虽然springMVC的配置文件(其实是定义的相应的DispatcherServlet的对应的配置文件)是可以通过初始化参数contextConfigLocation来指定的,如果不配置指定,那就按照约定:DispatcherServlet对应的上下文配置, 默认为/WEB-INF/$servlet-name$-servlet.xml

DispatcherServlet对应的上下文配置



/WEB-INF/configs/spring/mvc-dispatcher-servlet.xml



<?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.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 本配置文件是工名为mvc-dispatcher的DispatcherServlet使用, 提供其相关的Spring MVC配置 -->

    <!-- 启用Spring基于annotation的DI, 使用户可以在Spring MVC中使用Spring的强大功能。 激活 @Required
        @Autowired,JSR 250's @PostConstruct, @PreDestroy and @Resource 等标注 -->
    <context:annotation-config />

    <!-- DispatcherServlet上下文, 只管理@Controller类型的bean, 忽略其他型的bean, 如@Service -->
    <context:component-scan base-package="com.happyBKs.controller">
        <context:include-filter type="annotation"  expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <!-- HandlerMapping, 无需配置, Spring MVC可以默认启动。 DefaultAnnotationHandlerMapping
        annotation-driven HandlerMapping -->

    <!-- 扩充了注解驱动,可以将请求参数绑定到控制器参数 -->
    <mvc:annotation-driven />

    <!-- 静态资源处理, css, js, imgs -->
    <!--<mvc:resources mapping="/resources/**" location="/resources/" />-->


    <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsps/" />
        <property name="suffix" value=".jsp" />
    </bean>


</beans>

这里注意,该配置文件其实归结起来做了四件事情:

1. 激活一些注解

2. 定义DispatcherServlet上下文,即刚才所说的搜索哪些包

3. 激活springMVC的几个注解

4. 定义请求和视图View资源之间映射

 

接下来,定义视图View,我们在\WEB-INF\jsps\下定义了first.jsp

\WEB-INF\jsps\first.jsp如下:

<html>
<body>
<h2>Hello! I am developing a springMVC project with intellj idea!</h2>
<h2>HappyBKs</h2>
</body>
</html>

最后,我们在idea下配置如何将webapp发布到tomcat上运行:

点击【Run | Edit Configurations...】

idea 怎么运行springmvc项目 idea启动springmvc_spring_07

点击左上角的绿色加号。选择Tomcat server 的 local

然后配置Server

然后选择选项卡Deployment,配置部署方式

idea 怎么运行springmvc项目 idea启动springmvc_前端_08

点加号,选择Artifact。

注意,这里我们还可以为应用配置一个请求中的web应用名称,默认是 "/"

如果我们不配置,请求的是localhost:8080/【Controller映射路径】

这里,我们为应用去个名字叫/mvc。一台服务器毕竟一般会部署很多个应用嘛!

请求的url则是localhost:8080/【web应用配置的名称】/【Controller映射路径】,即localhost:8080/mvc/hello/world

好,最好保存,点击运行。

webapp的首页会自动在默认浏览器中打开(用哪个浏览器,idea也可以配置的)。

之后我们请求http://localhost:8080/mvc/hello/world

 

好了,这篇博文虽然是承接springMC笔记系列,但是毕竟时隔一年了,我将springMVC的原理性的东西,尤其是前端控制器等重新整理了一遍。还有便是,算是把在intellj idea下如何配置你的第一个springMVC方法一步一步讲清楚了,用惯了eclipse的idea小白们不妨试试吧。