一、MVC设计模式
1、概念: MVC设计模式是软件工程中的一种软件架构模式。它强制性的使软件系统的输入、处理、输出分开,把软件系统分为三个基本部分:
- 视图(View): 负责格式化数据并把它们呈现给用户,包括数据展示、用户交互、数据验证、界面设计等功能。对应组件JSP页面或HTML文件。
- 模型(Model): 模型对应拥有最多的处理任务,是应用程序的主体部分,它负责数据逻辑(业务规则)的处理和实现数据操作(即在数据库中存取数据)。对应组件:JavaBean(如处理业务逻辑的Service层、与操作数据库相关的DAO层;贯穿于各层之间的数据模型,即数据实体POJO)。
- 控制器(Controller): 负责接收并转发请求,对请求进行处理后指派视图并将响应结果发送给客户端。对应组件:Servlet。
2、MVC的两种模式
-
JSP Model1
当业务流程比较简单时,可以把控制器的功能交给视图来实现,这种模式称为JSP Model1。故Model1模型只有视图和模型,没有控制器(即JSP+JavaBean)。 -
JSP Model2
业务流程复杂时,则使用JSP Model2,(JSP+JavaBean+Controller)。
优点:结构清晰,提高效率,并且也是一个松耦合的架构模式。
3、小结
-
MVC处理过程
首先视图提供系统与用户交互的界面,并发送用户输入给控制器,控制器接收用户的输入,并决定应该调用哪个模型来进行处理模型根据用户请求进行响应的业务逻辑处理,并返回处理结果(数据)控制器根据返回的处理结果,调用响应的视图格式化模型返回的数据,并通过视图呈现给用户结果。 -
MVC优缺点
优点:
多视图共享一个模型,大大提高代码的可重用性
MVC三个模块相互独立,松耦合架构
控制器提高了应用程序的灵活性和可配置性
有利于软件工程化管理
松耦合+高重用性+高可适用性缺点:
原理复杂
增加了系统结构和实现的复杂性
视图对模型数据的低效率访问
不适用于中小型规模的项目
二、Spring MVC框架介绍
1、Spring MVC框架介绍
-
Spring MVC是Spring框架中用于Web应用开发的一个模块,是Spring提供的一个基于MVC设计模式的优秀Web开发框架,它本质相当于Servlet。在MVC设计模式中,Spring MVC作为控制器(Controller)来建立模型与视图的数据交互,是结构最清晰的MVC Model2实现,可称为一个典型的MVC框架。
-
Spring MVC框架采用松耦合可拔插的组件结构,具有高度可配置性,比起其它MVC框架更具扩展性和灵活性。此外,Spring MVC的注解驱动和对REST风格的支持,也是它最具特色的功能,无论是在框架设计,还是扩展性、灵活性等方面都已经全面超越了Struts2等MVC框架,而且它本身就是Spring框架的一部分,与Spring框架整合可以说是无缝集成,性能方面具有天生的优越性,对于开发者来说开发效率也高于其他MVC框架,在企业中的应用越来越广泛,称为主流的MVC框架。
三、Spring MVC环境搭建
1、下载需要的jar文件
- spring-web-3.2.13.RELEASE.jar
- spring-webmvc-3.2.13.RELEASE.jar
2、Spring MVC配置
- 创建web.xml,配置Spring MVC的核心控制器DispatcherServlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<!-- 配置Spring MVC的核心控制器DispatcherServlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<!--初始化参数 -->
<init-param>
<!-- 通过设置contextConfigLocatin参数来指定Spring MVC配置文件
的位置,此处使用Spring资源的方式进行指定 -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 标记容器在启动的时候就加载此DispatcherServlet,即自动启动 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- 表示DispatcherServlet需要截获并处理该项目的所有url请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 创建Spring MVC的配置文件 springmvc-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:mvc="http://www.springframework.org/schema/mvc"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置处理器映射 -->
<bean name="/index.html" class="cn.smbms.controller.IndexController"></bean>
<!-- 配置视图解析器(完成视图的对应) -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
解析:
(1)在web.xml中配置了DispatcherServlet,并配置了哪些请求需要通过此Servlet进行相应的处理,而DispatcherServlet需要将请求交给一个特定的Controller处理,但在此之前需要通过一个Bean才能知道交给哪个Controller处理,而这个Bean就是HandlerMapping,它的作用就是把一个url请求指定给一个Controller处理。Spring 提供了多种处理器映射(HandlerMapping)的支持,可以根据需求选择处理器映射,默认使用BeanNameUrlHandlerMapping,即在Spring 容器中查找与请求url同名的Bean。这个映射器不需要配置,根据请求的url路径映射到控制器Bean的名称。代码如下:
<!-- name 表示指定的url请求,class表示处理该url请求的控制器 -->
<bean name="/index.html" class="cn.smbms.controller.IndexController"/>
(2)处理请求后需要进行渲染输出,这个任务由视图实现。而指定请求需要用哪个视图进行请求结果的渲染输出?DispatcherServlet会查找到一个视图解析器,将控制器返回的逻辑视图名称转换成渲染结果的实际视图。Spring 也提供了多种视图解析器,例:
org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.view.ContentNegotiatingViewResolve
而在上面springmvc-servlet.xml 文件的代码中,使用的是InternalResourceViewResolver
定义该视图解析器,通过配置prefix与suffix,
将视图逻辑名称解析为 /WEBI-INF/jsp/.jsp。
(3)Spring MVC配置文件的命名,需要注意必须和在web.xml中配置DispatcherServlet时所指定的配置文件名称一致,一般命名为-servlet.xml,如springmvc-servlet.xml。
3、创建Controller(处理请求的控制器)
IndexController.java
package cn.smbms.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
//控制器:IndexController
public class IndexController extends AbstractController {
/*
* 该方法返回值是ModelAndView对象,该对象既包含视图信息,也包含模型数据信息
* 由于Java一次只能返回一个对象,所以ModelAndView的作用就是封装这两个对象,
* 以方便一次返回我们所需要的View和Model;若模型中没有任何数据,那么只返回视图即可,
* 或者只返回模型。
*/
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
System.out.println("hello,SpringMVC!");
return new ModelAndView("index");
}
}
4、创建View(JSP)
index.jsp 主要代码如下
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
</head>
<body>
<h1>hello,SpringMVC!</h1>
</body>
</html>
5、部署运行
在浏览器地址栏中输入
http://localhost:8080/projectName/index
运行结果如下:
补充:一键式配置处理器映射(HandlerMapping)
- 修改springmvc-servlet.xml文件
<!-- 对指定包进行扫描,实现注解驱动Bean的定义,同时将Bean自动注入容器中使用。
即:使标注了Spring MVC注解(如@Controller等)的Bean生效若没有配置此标签,那么
标注了@Controller的Bean仅仅是一个普通Bean,而不是一个可以处理请求控制器。 -->
<context:component-scan base-package="cn.smbms.controller"/>
<!-- 自动注册DefaultAnnotationHandlerMapping(处理器映射)
与AnnotationMethodHandlerAdapter(处理器适配器),
SpringMVC需要通过这两个Bean实例来完成对@Controller和@RequestMapping等注解的支持 -->
<mvc:annotation-driven/>
- 修改IndexController.java文件
package cn.smbms.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
//控制器:IndexController
@Controller
public class IndexController{
private Logger logger=Logger.getLogger(IndexController.class);
@RequestMapping("/index")
public String index(){
logger.info("hello,SpringMVC!");
return "index";
}
}
注: 使用@Controller对IndexController类进行标注,使其可以成为一个可处理HTTP请求的控制器,再使用@RequestMapping对IndexController的index()方法进行标注,确定index()方法对应的请求URL。后续会对@RequestMapping进行详解,这里简单了解。
四、Spring MVC框架的请求处理流程及体系结构
1、Spring MVC框架的请求处理流程
(1) 用户发送请求至前端控制器DispatcherServlet
(2) DispatcherServlet收到请求调用HandlerMapping处理器映射器。
(3) 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
(4) DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
(5) 执行处理器(Controller,也叫后端控制器)
(6) Controller执行完成返回ModelAndView
(7) HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
(8) DispatcherServlet将ModelAndView传给ViewReslover视图解析器
(9) ViewReslover解析后返回具体View
(10) DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
(11) DispatcherServlet响应用户
2、Spring MVC常用组件
-
DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。 -
HandlerMapping:处理器映射器
HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。 -
Handler:处理器
Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。
由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。 -
HandlAdapter:处理器适配器
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。 -
View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
-
View:视图
springmvc框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp
注: 在springmvc的各个组件中,处理器映射器(HandlerMapping)、处理器适配器(HandlerAdapter)、视图解析器(ViewResolver) 称为springmvc的三大组件。需要用户开放的组件有handler、view。
3、Spring MVC框架的特点
- 清晰的角色划分。Spring MVC在Model、View、Controller方面提供了一个非常清晰的角色划分,这三个方面真正的各司其职,各负各责。
- 灵活的配置功能。因为Spring的核心是IoC,同样在实现MVC上,也可以把各种类当作Bean来通过XML进行配置。
- 提供了大量的控制器接口和实现类。开发者可以使用Spring提供的控制器实现类,也可以自己实现控制器接口。
- 真正做到与View层的实现无关。它不会强制开发者使用JSP,也可以根据项目需求使用Velocity、XSTL等技术,使用起来更灵活。
- 国际化支持
- 面向接口编程
- Spring提供了Web应用开发的一整套流程,不仅仅是MVC,它们之间可以很方便的结合一起。
五、参数的传递
1、参数传递:View to Controller
我们先使用普通方式实现参数传递
package cn.smbms.controller;、
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class UserController {
private Logger logger=Logger.getLogger(UserController.class);
@RequestMapping("/welcome")
public String welcome(String username){
logger.info("welcome,username:"+username);
return "index";
}
}
index.jsp不做修改。在地址栏输入
控制台运行结果
页面运行结果
从地址栏中的URL请求可以看出请求参数为username,并且与控制器的处理方法的参数名都一一对应,所以结果运行正确;而如果修改
请求URL中的参数名称,如下
再次运行程序,页面就会报以下错误
这是因为URL请求参数名与控制器处理方法入参参数名不一致,所以要解决此问题需要使用@RequestParam。
@RequestMapping:负责将不同请求映射到对应的控制器方法上。
HTTP请求信息除了请求的URL地址之外,还包括很多其他信息:请求方法(GET、POST)、HTTP协议及版本、HTTP的报文头、HTTP的报文体。使用@RequestMapping,除了可以使用URL映射请求之外,还可以使用请求方法、请求参数等映射请求。
使用@RequestMapping来完成映射,具体包括4个方面的信息项:请求URL、请求参数、请求方法、请求头
使用方式
(1)通过请求URL进行映射
package cn.smbms.controller;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/user")
public class UserController {
private Logger logger=Logger.getLogger(IndexController.class);
@RequestMapping("/welcome") //此方式为简写
//@RequestMapping(value="/welcome") //使用value来指定请求的URL
public String welcome(@RequestParam String username){
logger.info("welcome,username:"+username);
return "index";
}
}
解析1:
以上代码中在类定义处标注@Controller使一个JavaBean成为一个可处理HTTP请求的控制器,使用@RequestMapping注解在UserController的类定义处标注URL "/user"
,表示相对于Web应用的部署路径,而在welcome()方法处指定URL则是相对于类定义处所指定的URL,那么访问路径为:
http://localhost:8080/projectName/user/welcome?username=admin
若在类定义处未标注@RequestMapping,此时,方法处指定的URL则相对于Web应用的部署路径,访问路径为 :
http:localhost:8080/projectName/welcome?username=admin
此处需要注意,@RequestMapping 映射的请求信息必须保证全局唯一,而如果在方法上存在相同的URL,则类定义处所指定的URL必须不一样。例如:"/user/add"
、"/provider/add"
。
解析2: 对于@RequestMapping的value,通过源码可以看出它的返回值是String[],故也可以写为以下格式:
@RequestMapping({"/index","/"})
这段代码表示请求的url为http://localhost:8080/projectName/index
或http://localhost:8080/projectName/
都可以进入该处理方法。
(2)通过请求参数、请求方法进行映射
package cn.smbms.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
//控制器:IndexController
@Controller
public class IndexController{
private Logger logger=Logger.getLogger(IndexController.class);
/**
* value表示请求的url,
* method表示请求方法(此处为GET请求,若是POST请求则无法进入当前处理方法),
* @param username 表示请求参数,此处参数名为username
* @return
*/
@RequestMapping(value="/welcome",method=RequestMethod.GET,params="username")
public String welcome(@RequestParam String username){
logger.info("welcome:"+username);
return "index";
}
解析: 在上述代码中,@RequestMapping的value表示请求的URL;method表示请求方法,此处设置为GET请求,若是POST请求则无法进入该处理方法中;params表示请求参数,此处参数名为username。
在地址栏中输入URL:
http://localhost:8080/projectName/welcome?username=admin
运行结果正确,成功进入IndexController的welcome()处理方法中。
再次在地址栏中输入URL
http://localhost:8080/projectName/welcome?usercode=admin
,运行程序会发现页面报400错误,控制台也报出异常,如下
若修改welcome方法入参为usercode,则后台同样取不到值,代码如下
再次运行程序,页面与控制台均为报错,并且根据请求信息,成功进入welcome()处理方法中,但是在该方法中却没有得到参数值,控制台输出welcome,null ,由此可见若选择方法直接入参,方法参数名必须与请求参数名称一致。
补充:
@RequestParam注解有三个参数
value ----------参数名
required-------是否必须,默认为true,表示请求中必须包含对应的参数 名,若不存在则抛出异常
defaultValue–默认参数名,不推荐使用
demo如下:
/**
* value表示请求的url,
* method表示请求方法(此处为GET请求,若是POST请求则无法进入当前处理方法),
* @param username 表示请求参数,此处参数名为username
* @return
*/
@RequestMapping(value="/welcome",method=RequestMethod.GET,params="username")
public String welcome(@RequestParam(value="username",required=false) String username){
logger.info("welcome"+username);
return "index";
}
在地址栏中输入URLhttp://localhost:8080/chap09/welcome
运行结果如图
2、参数传递:Controller to View
(1)ModelAndView方式
/**
* 参数传递 Controller to View-(ModelAndView)
* @param username
* @return
*/
@RequestMapping("/index1")
public ModelAndView index(String username){
logger.info("welcome!username:"+username);
ModelAndView mView=new ModelAndView();
mView.addObject("username"+username); //通过addObject()添加模型数据
mView.setViewName("index"); //通过setViewName()设置逻辑视图名
return mView;
}
解析1: 添加模型数据
ModelAndView addObject(String attributeName,Object attributeValue);
该方法的第一个参数为key值,第二参数为key对应的value。key可以随意指定(保证在Model的作用域内唯一即可)。
ModelAndView addAllObjects(Map<String,?> modelMap);
从此方法可以看出,模型数据也可以是一个Map对象,可以添加Map对象到Model中。
解析2: 设置视图
void setView(View view)
:指定一个具体的视图对象void setViewName(String viewName)
:指定一个视图逻辑名
(2)Model方式
/**
* 参数传递:Controller to View-(Model)
* @param username
* @param model Model对象也是一个Map类型的数据结构
* @return
*/
@RequestMapping("/index2")
public String index(String username,Model model){
//示例13-14 Model中存放的为普通类型的对象,如String等
logger.info("hello,SpringMVC! username:"+username);
model.addAttribute("username",username);
//若不指定类型,则会默认使用(value)对象的类型作为key,此处为string
model.addAttribute(username);
User user=new User();
user.setUserName(username);
//Model中存放JavaBean
model.addAttribute("currentUser",user);
model.addAttribute(user);
return "index";
}
index.jsp
<body>
<h1>username(key:username)----${username}</h1>
<h1>username(key:string)---${username}</h1>
<h1>username(key:currentUser)---${username}</h1>
<h1>username(key:user)---${username}</h1>
</body>
(3)Map方式
/**
* 参数传递:controller to view -(Map<String,Object>)
* @param username
* @param model
* @return
*/
@RequestMapping("/index3")
public String index(String username,Map<String,Object>model){
logger.info("hello,SpringMVC! username:"+username);
model.put("username", username);
return "index";
}
注:Spring MVC控制器的处理方法若有Map或者Model类型的入参,就会将请求内的隐含模型对象传递给这些入参,因此在方法体内可以通过这个入参对象模型中的数据进行读写操作,当然,作为Spring MVC的标准用法,推荐使用ModelAndView。
(4)@ModelAttribute
(5)@SessionAttributes
注:以上两种方式为注解方式配置,在后面博文会详细讲解。
六、视图解析器
- 请求处理方法执行完后,最终返回一个ModelAndView对象。对于那些返回String等类型的处理方法,Spring MVC也会在内部将它们装配成一个ModelAndView对象,它包含了逻辑视图名和数据模型,那么此时Spring MVC就需要借助ViewResolver。
- ViewResolver是Spring MVC处理视图的重要接口,通过它可以将控制器返回的逻辑视图名解析为一个真正的视图对象。常见的视图对象有:JSP视图、FreeMarker、JSON、XML、PDF格式的视图等。
- Spring MVC默认提供了多种视图解析器,所有的视图解析器都实现了ViewResolver接口。InternalResourceViewResolver是最常用的视图解析器,通常用于查找JSP和JSTL等视图。它是URLBasedViewResolver的子类,它会把返回的视图名称都解析为InternalResourceView对象,该对象会把Controller的处理方法返回的模型属性都放在对应的请求作用域中,然后通过RequestDispatcher在服务器端把请求震转发到目标URL。
配置代码如下:
<!-- 配置视图解析器(完成视图的对应) -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
关注一下吧~【Pretty Kathy】