在此之前,先来学习MVC设计模式。MVC全名是(Model View Controller),是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。其实就是Controller负责接收并处理请求,响应客户端;Model包括模型数据和业务逻辑;View主要是呈现模型,与用户进行交互:

那么什么是SpringMVC呢?SpringMVC是实现了MVC设计模式的框架,同时是Spring框架的一个子模块,二者可以很好地结合使用,不需要整合。

SpringMVC核心组件

接下来通过SpringMVC的工作流程来介绍SpringMVC各核心组件的名称及用法:

1、前端控制器(DispatcherServlet):接收前端页面的请求,并响应结果到前端页面,同时中间作请求转发,相当于中央处理器;
2、处理器映射器(HandlerMapping): 根据请求的url来查找对应的处理器(将请求映射到Handle);
3、处理器适配器(HandlerAdapter): 执行符合特定规则(HandlerAdapter规则)的处理器;
4、处理器(Handler):后端控制器,完成具体的业务逻辑,由程序员开发
5、视图解析器(ViewResolver): 进行视图解析,根据逻辑视图名解析为真正的视图;
6、视图: View是一个接口,其实现类支持不同的view类型,如jsp,freemaker,pdf等,由程序员开发

也有人认为有8种核心组件,分别是:1、前端控制器(DispatcherServlet);2、处理器映射器(HandlerMapping);3、后端控制器(Handler);4、处理器拦截器(HandlerInterceptor);5、处理器执行链(HandlerExecutionChain);6、处理器适配器(HandlerAdapter);7、装载模型数据和视图信息(ModelAndView);8、视图解析器(ViewResolver);

接下来介绍SpringMVC的实现流程:

1、客户端请求被DispatcherServlet接收;2、DispatcherServlet将请求映射到Handler;3、生成的Handler以及HandlerInterceptor;4、返回HandlerExecutionChainHandler+HandlerInterceptor);5、DispatcherServlet通过HandlerAdapter执行Handler;6、返回一个ModelAndView;7、DispatcherServlet通过ViewResolver进行解析;8、返回填充了模型数据的View,响应给客户端。

简单点就是客户端向服务器发生请求,dispatcherServlet收到请求后, 依据HandlerMapping来调用相应的ControllerController将处理的结果封装成MoodelAndView对象并返回给dispatcherServletdispatcherServlet依据viewResolver解析调用相应的视图对象来生成相应的页面。

尽管上面的过程较为复杂,但是SpringMVC的使用却并不复杂,因为它大部分组件由框架提供,用户只需通过配置进行关联;其实前面也说过用户只需动手编写HandlerView即可。

SpringMVC提供了两种方式进行使用:XML配置方式和注解方式,下面将逐一进行介绍。

基于XML方式的使用

基于XML方式使用SpringMVC,只需要三个步骤:1、SpringMVC基础配置;2、XML配置ControllerHandlerMappering组件映射;3、XML配置ViewResolver组件映射.

使用Maven新建一个webapp项目,名称为springmvc_base。第一步,配置pom.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!--springmvc相关-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<!--servlet相关-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!--junit测试相关-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>

第二步,修改web.xml文件内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!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>

<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配合SpringMVC.xml文件的位置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>

<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

第三步,新建com.envy.handler包,然后在该包内新建MyHandler.java文件,里面的代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.envy.handler;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class MyHandler implements Controller {
@Override
public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
//装载模型数据和逻辑视图
ModelAndView modelAndView = new ModelAndView();
//添加模型数据
modelAndView.addObject("name","envy");
//添加逻辑视图
modelAndView.setViewName("show");
return modelAndView;
}
}

这个MyHandler需要实现Controller接口,注意是org.springframework.web.servlet.mvc.Controller包内的,这个ModelAndView中需要装载模型数据和逻辑视图。
第四步,在resources包内新建springmvc.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--配置HandleMapping,将URL请求映射到Handler-->
<bean id="handleMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<!--配置mapping-->
<property name="mappings">
<props>
<!--配置test请求对应的handler为testHandler-->
<prop key="/test">testHandler</prop>
</props>
</property>
</bean>

<!--配置handler-->
<bean id="testHandler" class="com.envy.handler.MyHandler"></bean>

<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置前缀-->
<property name="prefix" value="/"></property>
<!--配置后缀-->
<property name="suffix" value=".jsp"></property>
</bean>
</beans>

第五步,新建show.jsp页面(注意由于数据时存放在request作用域中,因此在View也就是jsp中可以通过EL表达式获取数据,但前提是设置了<%@ page isELIgnored="false"%>):

1
2
3
4
5
6
7
8
9
10
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false"%>
<html>
<head>
<title>XML方式</title>
</head>
<body>
${name}
</body>
</html>

运行该项目,在浏览器地址栏中输入http://localhost:8080/show.jsp,你会发现什么也没有,其实这是正常现象,因为你是直接访问了show.jsp页面,而不是通过SpringMVC把请求通过Handler处理了,因此正确的访问路径应该是http://localhost:8080/test,然后页面就会显示envy。

基于注解方式的使用

其实基于注解方式的使用比XML方式更便捷一些,但是针对不同的场合还是选择合适的为好。基于注解方式使用SpringMVC,也只需三个步骤:1、SpringMVC基础配置;2、ControllerHandlerMappering组件通过注解方式映射;3、XML配置ViewResolver组件映射。其实就只有第二步使用了注解,第三步依旧还是使用XML配置。

新建一个MyNewHandler.java文件(注意此时不再需要继承Controller接口,但是需要在类上使用Controller注解,在方法上面也要使用RequestMapping注解,用于告知访问哪个URL时使用MyNewHandler这个处理器):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.envy.handler;

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

@Controller
public class MyNewHandler{
//自己定义方法用于返回装载模型数据和逻辑视图的ModelAndView
//RequestMapping中的参数一般都与方法名称一致,但是可以不一致
@RequestMapping("/getModelAndView")
public ModelAndView getModelAndView(){
//装载模型数据和逻辑视图
ModelAndView modelAndView = new ModelAndView();
//添加模型数据
modelAndView.addObject("sex","male");
//设置逻辑视图
modelAndView.setViewName("good");
return modelAndView;
}

}

接着修改springmvc.xml文件为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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"
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">

<!--基于注解方式-->
<!--将MyNewHandler自动扫描到IOC容器-->
<context:component-scan base-package="com.envy.handler"></context:component-scan>


<!--配置视图解析器,两种方式都需要-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置前缀-->
<property name="prefix" value="/"></property>
<!--配置后缀-->
<property name="suffix" value=".jsp"></property>
</bean>
</beans>

注意基于注解方式时需要开启包扫描,也就是你所配置的Hander所在的包,让它自动被扫描到IOC容器。而后面的视图解析器是两者都需要配置的。

最后新建good.jsp页面:

1
2
3
4
5
6
7
8
9
10
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<html>
<head>
<title>注解方式</title>
</head>
<body>
${sex}
</body>
</html>

然后运行项目,在浏览器地址栏中输入http://localhost:8080/getModelAndView,你会发现页面显示male。**(注意两种方式同时只能使用一种,另一种代码需要注释,否则会出错误。)**

其实在MyNewHandler类中定义的getModelAndView方法就是返回一个ModelAndView对象,我们知道ModelAndView对象里面包含了模型数据和逻辑视图,那么可以构造一个方法,只要也能返回模型数据和逻辑视图就能实现相同的功能:

1
2
3
4
5
6
7
8
//业务方法:model传值、String用于视图解析
@RequestMapping("/ModelStringTest")
public String ModelStringTest(Model model){
//添加模型数据
model.addAttribute("sex","male");
//设置逻辑视图
return "good";
}

然后运行项目,在浏览器地址栏中输入http://localhost:8080/ModelStringTest,你会发现页面居然也显示male,那就说明这种方法是可行的。

其实还可以使用map来实现上述功能,但是和前面其实没有什么区别:

1
2
3
4
5
6
7
8
//业务方法:map传值、String用于视图解析
@RequestMapping("/MapTest")
public String MapTest(Map<String,String> map){
//添加模型数据
map.put("sex","male");
//设置逻辑视图
return "good";
}

然后运行项目,在浏览器地址栏中输入http://localhost:8080/MapTest,你会发现页面也显示male,那就说明这种方法同样是可行的。

入门小Demo

接下来通过一个小demo来巩固SpringMVC的入门学习和使用,demo的需求是这样的:前端通过form表单添加商品信息(商品信息包括名称和价格),然后在后端对其进行封装,最后在前端进行展示。

新建一个add.jsp页面,内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>add</title>
<link href="css/bootstrap.min.css" rel="stylesheet">
<style type="text/css">
body{
overflow-x:hidden;
}
</style>
</head>
<body>
<form class="form-horizontal" role="form" action="addGoods" method="post">
<div class="form-group">
<label class="col-sm-1 control-label">名称</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="name" placeholder="请输入商品名称">
</div>
</div>
<div class="form-group">
<label class="col-sm-1 control-label">价格</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="price" placeholder="请输入商品价格">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-3">
<button type="submit" class="btn btn-default">提交</button>
</div>
</div>
</form>
</body>
</html>

当用户在add.jsp页面form表单中填写信息并提交后,我们通过什么方法来获取到他提交的信息呢?在spring里面常用的就是使用request.getParameter("name属性值")方式来获取信息,而在springMVC中是通过实体类属性与控件名称来绑定的,即代码里面的name="name",name="price",也就是说Goods必须有name和price这两个属性。

新建com.envy.entity包,接着在里面新建一个Goods.java文件,里面的代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.envy.entity;

public class Goods {
private String name;
private double price;

public String getName(){
return this.name;
}
public void setName(String name){
this.name =name;
}

public double getPrice(){
return this.price;
}

public void setPrice(double price){
this.price=price;
}

public Goods(){};

public Goods(String name,double price){
this.name=name;
this.price=price;
}
}

接着在MyNewHandler类中定义一个方法,用于处理form表单中提交过来的信息,可以发现action="addGoods",即该方法为@RequestMapping("/addGoods"),定义一个方法addGoods,代码如下:

1
2
3
4
5
6
7
8
9
//添加商品并展示
@RequestMapping("/addGoods")
public ModelAndView addGoods(Goods goods){
System.out.println(goods.getName()+"---"+goods.getPrice());
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("goods",goods);
modelAndView.setViewName("goods");
return modelAndView;
}

当然需要新建一个goods.jsp页面负责将数据展示出来:

1
2
3
4
5
6
7
8
9
10
11
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<html>
<head>
<title>入门小demo</title>
</head>
<body>
<p>商品名称:${requestScope.goods.name}</p>
<p>商品价格:${requestScope.goods.price}</p>
</body>
</html>

然后运行项目,在浏览器地址栏中输入http://localhost:8080/add.jsp,输入商品信息和名称,点击提交后页面显示刚才提交的信息。

不知道你注意没有,我们在jsp页面中使用了requestScope.goods.name这种方式来获取name,其实这表明最后提交的信息都是在request作用域中,因此可以通过该方式获取。