Spring MVC框架介绍
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。
Spring的模型-视图-控制器(MVC)框架是围绕一个DispatcherServlet来设计的,这个Servlet会把请求分发给各个处理器。
Spring MVC运行原理
1.客户端请求提交到DispatcherServlet
2.由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
3.DispatcherServlet将请求提交到Controller
4.Controller调用业务逻辑处理后,返回ModelAndViewDispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
5.视图负责将结果显示到客户端
Spring MVC优点
1.容易和其它View框架(Titles等)无缝集成,采用IOC便于测试。
2.它是一个典型的教科书式的mvc构架,而不像struts等都是变种或者不是完全基于mvc系统的框架,spring适用于初学者或者想了解mvc的人。
3.它和tapestry一样是一个纯正的servlet系统,这也是它和tapestry相比struts所没有的优势。而且框架本身有代码,而且看起来也不费劲比较简单可以理解。
代码实现
实例基于Spring MVC 5.0.7,JDK1.8
GitHub地址:https://github.com/The-ProgramLife/Demo/tree/master/SpringMVC
新建Java Maven WebApp Project
在pom.xml引入Spring MVC依赖与Servlet依赖
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
修改WEB-INF目录下的web.xml
配置Spring MVC的入口 DispatcherServlet,把所有的请求都提交到该Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<servlet>
<!-- 在没有显式指定的情况下映射配置文件引用此名称 -->
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 设置编码,解决中文乱码问题 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
在WEB-INF目录下创建 springmvc-servlet.xml
这是Spring MVC的映射配置文件,因为在web.xml中没有显示指定文件,默认使用与<servlet-name>相同的名称。
<?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/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 访问路径/index会交给id=indexController的bean处理 -->
<bean id="simpleUrlHandlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<props>
<prop key="index">indexController</prop>
</props>
</property>
</bean>
<bean id="indexController" class="controller.IndexController"></bean>
</beans>
新建IndexController类(控制器)
实现Controller接口,覆写handleRequest方法通过 ModelAndView 对象把模型和视图结合在一起。
public class IndexController implements Controller{
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// TODO Auto-generated method stub
//通过 ModelAndView 对象把模型和视图结合在一起
ModelAndView modelAndView = new ModelAndView("index.jsp");
modelAndView.addObject("message", "Hello Spring MVC");
return modelAndView;
}
}
修改WebContent目录下的index.jsp
使用EL表达式读出模型数据message
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>
<html>
<body>
<h2>${message}</h2>
</body>
</html>
部署在tomcat中,重启测试
Spring MVC视图解析
上面使用new ModelAndView("index.jsp");定位视图,视图解析可以做到不添加后缀定位视图
修改springmvc-servlet.xml
添加视图解析组件
<!-- 将视图定位在/WEB-INF/page/*.jsp -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/page/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
修改IndexController.handleRequest()方法
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// TODO Auto-generated method stub
//通过 ModelAndView 对象把模型和视图结合在一起
ModelAndView modelAndView = new ModelAndView("index");
modelAndView.addObject("message", "Hello Spring MVC");
return modelAndView;
}
在WEB-INF下新建目录page,把index.jsp移动到 WEB-INF/page 目录下就完成了
视图链
如果需要定位多个视图位置,就需要使用到视图链,将视图解析器串成一条链,只需再添加一个解析器即可。
修改springmvc-servlet.xml
再添加一个Excel解析器
<bean id="excelViewResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
<!--指定次序,值越大越靠后-->
<property name="order" value="1"/>
<property name="location" value="/WEB-INF/views.xml"/>
</bean>
视图重定向
修改IndexController类
添加jump()方法,这里使用到了注解,将在下面进行讲解
@RequestMapping("/jump")
public ModelAndView jump() {
ModelAndView modelAndView = new ModelAndView("redirect:/index");
return modelAndView;
}
Spring MVC注解
新建AnnotationTest类
@Controller //表示该类是一个控制器
public class AnnotationTest {
@RequestMapping("/annoindex") // 表示路径/annoindex会映射到该方法上
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
// TODO Auto-generated method stub
ModelAndView modelAndView = new ModelAndView("anno_index");
modelAndView.addObject("message", "Hello Spring MVC");
return modelAndView;
}
}
修改springmvc-servlet.xml
添加以下代码
<!-- 从包annotation下扫描有@Controller注解的类 -->
<context:component-scan base-package="annotation"></context:component-scan>
<!-- 开启注解驱动 -->
<mvc:annotation-driven></mvc:annotation-driven>
新建anno_index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
${message}
</body>
</html>
Spring MVC Session
Session在用户登录,一些特殊场合在页面间传递数据的时候会经常用到。下面利用Session记住访问次数功能。
新建SessionTest类
为方法check()提供参数HttpSession session,这样就可以在方法体中使用session了
@Controller
public class SessionTest {
@RequestMapping("/check")
public ModelAndView check(HttpSession session) {
Integer i = (Integer)session.getAttribute("count");
if (i == null)
i = 0;
i++;
session.setAttribute("count", i);
ModelAndView modelAndView = new ModelAndView("check");
return modelAndView;
}
}
新建check.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
session中记录的访问次数:${count}
</body>
</html>
Spring MVC文件上传
在pom.xml引入文件上传依赖
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
配置web.xml允许访问*.jpg
因为配置springmvc的servlet的时候,使用的路径是"/",导致静态资源在默认情况下不能访问,所以要加上这一段,允许访问jpg。 并且必须加在springmvc的servlet之前
<!-- 允许访问*.jpg -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
配置springmvc-servlet.xml
<!-- 开放对上传功能的支持 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
新建upload,jsp
上传页面,需要注意的是form 的两个属性必须提供method="post" 和 enctype="multipart/form-data" 缺一不可上传组件 增加一个属性 accept="image/*" 表示只能选择图片进行上传
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="uploadImage" method="post" enctype="multipart/form-data">
选择图片:<input type="file" name="image" accept="image/*" /> <br>
<input type="submit" value="上传">
</form>
</body>
</html>
新建UploadedImageFile类
在UploadedImageFile中封装MultipartFile类型的字段 image ,用于接受页面的注入这里的字段 image必须和上传页面upload.jsp中的image<input type="file" name="image" accept="image/*" />保持一致
public class UploadedImageFile {
private MultipartFile image;
public MultipartFile getImage() {
return image;
}
public void setImage(MultipartFile image) {
this.image = image;
}
}
新建UploadController类
@Controller
public class UploadController {
@RequestMapping("/uploadImage")
public ModelAndView upload(HttpServletRequest request, UploadedImageFile file)
throws IllegalStateException, IOException {
String name = new Random(500).toString(); //随机命名
String newFileName = name + ".jpg";
//获取到web目录下的image目录,用于存放上传后的文件
File newFile = new File(request.getServletContext().getRealPath("/image"), newFileName);
newFile.getParentFile().mkdirs();
//复制文件
file.getImage().transferTo(newFile);
ModelAndView mav = new ModelAndView("showUploadedFile");
mav.addObject("imageName", newFileName);
return mav;
}
}
新建showUploadedFile.jsp
显示上传的图片
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
《<img src="image/${imageName}">
</body>
</html>
Spring MVC拦截器
Spring的处理器映射机制包含了处理器拦截器。拦截器在你需要为特定类型的请求应用一些功能时可能很有用,比如,检查用户身份等。
新建拦截器类IndexInterceptor
public class IndexInterceptor extends HandlerInterceptorAdapter {
/**
* 在业务处理器处理请求之前被调用 如果返回false 从当前的拦截器往回执行所有拦截器的afterCompletion(),再退出拦截器链 如果返回true
* 执行下一个拦截器,直到所有的拦截器都执行完毕 再执行被拦截的Controller 然后进入拦截器链,
* 从最后一个拦截器往回执行所有的postHandle() 接着再从最后一个拦截器往回执行所有的afterCompletion()
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// TODO Auto-generated method stub
System.out.println("preHandle(), 在访问Controller之前被调用");
return true;
}
/**
* 在业务处理器处理请求执行完成后,生成视图之前执行的动作 可在modelAndView中加入数据,比如当前时间
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
System.out.println("postHandle(), 在访问Controller之后,访问视图之前被调用,这里可以注入一个时间到modelAndView中,用于后续视图显示");
modelAndView.addObject("date", "由拦截器生成的时间:" + new Date());
}
/**
* 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等
*
* 当有拦截器抛出异常时,会从当前拦截器往回执行所有的拦截器的afterCompletion()
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion(), 在访问视图之后被调用");
}
}
修改springmvc-servlet.xml
对/index路径进行拦截
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/index" />
<!-- 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 -->
<bean class="interceptor.IndexInterceptor"></bean>
</mvc:interceptor>
<!-- 当设置多个拦截器时,先按顺序调用preHandle方法,然后逆序调用每个拦截器的postHandle和afterCompletion方法 -->
</mvc:interceptors>
修改 index.jsp
打印拦截器放进去的日期
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>
<html>
<body>
<h2>${message}</h2>
<p>${date}</p>
</body>
</html>