1 什么是SpringMVC?
1.1 三层架构
开发架构一般基于两种形式,C/S架构和B/S架构。而在B/S架构中,系统则分为标准的三层架构:表现层、业务层和持久层。
- 表现层:即
web层。负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web层,web需要接收http请求,完成http响应。表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端。 表现层的设计一般都使用MVC模型。(MVC是表现层的设计模型,和其他层没有关系) - 业务层:即
service层。负责业务逻辑处理,web层依赖业务层,但是业务层不依赖web层。业务层在业务处理时可能会依赖持久层。 - 持久层:
dao层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中。
1.2 MVC模型
正如1.1中所说的:MVC是表现层的设计模型,和其他层没有关系。MVC是一种设计模式,应用于表现层,全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种用于设计创建 Web 应用程序表现层的模式。
Model(模型):用于封装数据。View(视图):用于展示数据(jsp或者html)。Controller(控制器):用于处理程序逻辑。
1.3 SpringMVC
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。
一张经典的三层架构的图如下: 
2 SpringMVC的入门
IDEA中创建基于Maven的Web项目的细节如下: 
2.1 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>HelloSpringMVC</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>HelloSpringMVC Maven Webapp</name>
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<spring.version>5.0.2.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>HelloSpringMVC</finalName>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
2.2 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>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:SpringMVC.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2.3 SpringMVC.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
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
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">
<context:component-scan base-package="com.hc"/>
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
2.4 控制器
package com.hc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorld {
@RequestMapping(value = "hello")
public String hello() {
System.out.println("HelloWorld!!!");
return "hello";
}
}
2.5 JSP页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<a href="/hello">登陆</a>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello</title>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
3 SpringMVC的请求响应流程及入门案例细节
3.1 SpringMVC的请求响应流程
2中的SpringMVC的请求相应流程可以分为如下5步:
- 服务器启动,应用被加载。读取到
web.xml中的配置创建Spring容器并且初始化容器中的对象。 - 浏览器发送请求,被
DispatherServlet捕获,该Servlet并不处理请求,而是把请求转发出去。转发的路径是根据请求URL,匹配@RequestMapping中的内容。 - 匹配到了后,执行对应方法。该方法有一个返回值。
- 根据方法的返回值,借助
InternalResourceViewResolver找到对应的结果视图。 - 渲染结果视图,响应浏览器。
3.2 SpringMVC组件
完整的SpringMVC的请求响应流程如下:  涉及以下组件:
DispatcherServlet:前端控制器。用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。HandlerMapping:处理器映射器。HandlerMapping负责根据用户请求找到Handler即处理器,SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等Handler:处理器。它就是我们开发中要编写的具体业务控制器。由DispatcherServlet把用户请求转发到Handler。由Handler对具体的用户请求进行处理。HandlAdapter:处理器适配器。通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。View Resolver:视图解析器。View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。View:视图。SpringMVC框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。
3.3 RequestMapping
RequestMapping注解的作用是建立请求URL和处理方法之间的对应关系。
3.3.1 RequestMapping的使用位置
 从RequestMapping的源码可以看出,RequestMapping注解可以出现在方法上或类上。
- 类上:请求
URL的第一级访问目录。 - 方法上:请求
URL的第二级访问目录。
3.3.2 RequestMapping的属性
value:用于指定请求的URL。它和path属性的作用是一样的。method:用于指定请求的方式。params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和配置的一模一样。headers:用于指定限制请求消息头的条件。
3.3.3 测试
 
4 请求参数绑定
4.1 绑定机制说明
SpringMVC的参数绑定过程是把表单提交的请求参数作为控制器中方法的参数进行绑定的- 表单提交的数据都是
key=value格式 - 提交表单的
name属性和参数的名称是相同的
4.2 基本数据类型和字符串类型
要求参数名称必须和控制器中方法的形参名称保持一致。(严格区分大小写)
package com.hc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.lang.reflect.Method;
@Controller
public class HelloWorld {
@RequestMapping(value = "hello", params = {"username"}, method = RequestMethod.POST)
public String hello(String username, String password) {
System.out.println(username + " " + password);
return "hello";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<head>
<title>index</title>
</head>
<body>
<form action="hello" method="post">
<input type="text" name="username">
<p/>
<input type="password" name="password">
<p/>
<input type="submit" value="DL">
</form>
</body>
</html>
4.3 POJO类型
要求表单中参数名称和POJO类的属性名称保持一致。并且控制器方法的参数类型是POJO类型。
package com.hc.controller;
import com.hc.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.lang.reflect.Method;
@Controller
public class HelloWorld {
@RequestMapping(value = "hello", params = {"username"}, method = RequestMethod.POST)
public String hello(User user) {
System.out.println(user.toString());
return "hello";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<head>
<title>index</title>
</head>
<body>
<form action="hello" method="post">
<input type="text" name="username">
<p/>
<input type="password" name="password">
<p/>
<input type="text" name="account.id">
<p/>
<input type="text" name="account.uid">
<p/>
<input type="text" name="account.money">
<p/>
<input type="submit" value="DL">
</form>
</body>
</html>
4.4 POJO中包含集合类型参数
- 第一种: 要求集合类型的请求参数必须在
POJO中。在表单中请求参数名称要和POJO中集合属性名称相同。 给List集合中的元素赋值,使用下标。 给Map集合中的元素赋值,使用键值对。 - 第二种: 接收的请求参数是
json格式数据。需要借助一个注解实现。
package com.hc.controller;
import com.hc.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.lang.reflect.Method;
@Controller
public class HelloWorld {
@RequestMapping(value = "hello", params = {"username"}, method = RequestMethod.POST)
public String hello(User user) {
System.out.println(user.toString());
return "hello";
}
}
<%--
Created by IntelliJ IDEA.
User: HCong
Date: 2022/5/9
Time: 11:21
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<head>
<title>index</title>
</head>
<body>
<form action="hello" method="post">
<input type="text" name="username">
<p/>
<input type="password" name="password">
<p/>
<input type="text" name="accounts[0].id">
<p/>
<input type="text" name="accounts[0].uid">
<p/>
<input type="text" name="accounts[0].money">
<p/>
<input type="text" name="accounts[1].id">
<p/>
<input type="text" name="accounts[1].uid">
<p/>
<input type="text" name="accounts[1].money">
<p/>
<input type="submit" value="DL">
</form>
</body>
</html>
4.5 请求参数乱码问题
 当是POST请求时,会出现中文乱码问题。配置过滤器处理中文乱码:
<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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
当然,静态资源不过滤时可以在SpringMVC.xml中配置:
<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/images/" mapping="/images/**"/>
<mvc:resources location="/scripts/" mapping="/javascript/**"/>
4.6 自定义类型转换器
如果页面传入2020/1/1这样的日期字符串SpringMVC会直接将其解析为Date类型的对象,而当字符串是2020-1-1这样的,则会解析失败,需要自定义类型转换器,并将其加入到SpringMVC得自动转换中。
package com.hc.utils;
import org.springframework.core.convert.converter.Converter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
try {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
return df.parse(source);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
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
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">
<context:component-scan base-package="com.hc"/>
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<array>
<bean class="com.hc.utils.StringToDateConverter"></bean>
</array>
</property>
</bean>
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"></mvc:annotation-driven>
</beans>
5 常用注解
5.1 RequestParam注解
- 作用:把请求中指定名称的参数给控制器中的形参赋值。
- 属性:
value表示请求参数中的名称。 required表示请求参数中是否必须提供此参数。默认值:true,表示必须提供,如果不提供将报错。  上图表示,将提交的url中的name映射为username。
5.2 RequestBody注解
- 作用:用于获取请求体内容。直接使用得到是
key=value&key=value这样的结构的数据。 get请求方式不适用。 - 属性:
equired表示是否必须有请求体。默认值是true。当取值为true时,get请求方式会报错。如果取值为false,get请求得到是null。 
5.3 PathVaribale注解
- 作用:用于绑定
url中的占位符。例如,请求url中 /delete/{id},这个{id}就是url占位符。 url支持占位符是Spring3.0之后加入的,是SpringMVC支持rest风格URL的一个重要标志。RESTFUL风格见此。 - 属性:
value表示用于指定url中占位符名称。 required表示否必须提供占位符。  
该注解与RESUFUL编程风格息息相关。
5.4 RequestHeader注解
- 作用:用于获取请求消息头。
- 属性:
value表示提供消息头名称。required表示否必须有此消息头。
5.5 CookieValue注解
- 作用:用于把指定
cookie名称的值传入控制器方法参数。 - 属性:
value表示指定cookie的名称。required表示是否必须有此cookie。 
5.6 ModelAttribute注解
- 作用:该注解是
SpringMVC4.3版本以后新加入的。它可以用于修饰方法和参数。 ①出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。② 出现在参数上,获取指定的数据给参数赋值。 - 属性:
value表示用于获取数据的key。key可以是POJO的属性名称,也可以是map结构的key。
5.6.1 作用于方法之上,方法带返回值

5.6.2 作用于参数上,方法不带返回值

5.7 SessionAttribute注解
- 作用: 用于多次执行控制器方法间的参数共享。
- 属性:
value表示用于指定存入的属性名称 。type表示用于指定存入的数据类型。 
6 响应数据与结果视图
6.1 返回值分类
6.1.1 String
 controller方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。
6.1.2 void
- 如果
controller方法没有返回值,会通过视图解析器访问以方法名作为页面名称的页面。 

@RequestMapping(value = "/testReturnVoid")
public void testReturnVoid(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("TestReturnVoid is running!");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("testReturnVoid!!!");
return;
}
6.1.3 ModelAndView
ModelAndView是SpringMVC提供的一个对象,该对象也可以用作控制器方法的返回值。
@RequestMapping(value = "/testReturnModelAndView")
public ModelAndView testReturnModelAndView() {
System.out.println("TestReturnString is running!");
ModelAndView mv = new ModelAndView();
mv.addObject("str","Hello World!");
mv.setViewName("hello");
return mv;
}
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Hello</title>
</head>
<body>
<h1>Hello World!</h1>
<hr>
${pageContext.request.getAttribute("str")}
</body>
</html>
结果: 
6.2 请求转发和重定向
6.2.1 forward转发
controller方法在提供了String类型的返回值之后,默认就是请求转发。使用请求转发,既可以转发到jsp,也可以转发到其他的控制器方法。
6.2.2 Redirect重定向
contrller方法提供了一个String类型返回值之后,它需要在返回值里使用:redirect:,例如:  注:如果是重定向到jsp页面,则jsp页面不能写在WEB-INF目录中,否则无法找到。而且,在redirect:也不需要加项目的全路径(request.getContextPath())。
6.3 返回json数据
- 传入
json格式参数:使用RequestBody注解,具体见5.2。 - 返回
json格式参数:使用ResponseBody注解,该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据如json,xml等,通过Response响应给客户端。 通过AJAX异步请求发送JSON数据,使用RequestBody解析为JavaBean对象,修改其中数据后,使用ResponseBody返回JSON数据。
@RequestMapping(value = "/testJson")
public @ResponseBody
User testJson(@RequestBody User user) {
System.out.println("testJson is running!");
System.out.println(user);
user.setUsername("HP");
return user;
}
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<head>
<title>index</title>
<script src="${pageContext.request.contextPath}/js/jquery.min.js"></script>
<script>
$(function () {
$("#btn").click(function () {
$.ajax({
url:"User/testJson",
contentType:"application/json;charset=UTF-8",
data:'{"username":"HC","password":"123456","birthday":"1998-09-02"}',
dataType:"json",
type:"post",
success:function (data) {
alert(data);
}
})
})
})
</script>
</head>
<body>
<button id="btn">修改</button>
</body>
</html>
注:json字符串和JavaBean对象互相转换的过程中,需要使用jackson的jar包如下:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
7 文件上传
7.1 SpringMVC结合第三方组件实现文件上传
文件上传的前提
form表单的enctype属性取值必须是:multipart/form-data (默认值是:application/x-www-form-urlencoded)method属性取值必须是Post- 提供一个文件选择域
<input type=”file” /> 第三方jar包依赖:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
实现如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<head>
<title>index</title>
</head>
<body>
<form action="/User/fileUpload01" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="uploadFile"><br/>
<input type="submit" value="上传">
</form>
</body>
</html>
@Controller
@RequestMapping(value = "/User")
public class HelloWorld {
@RequestMapping(value = "/fileUpload01")
public String fileUpload01(HttpServletRequest request) throws Exception {
System.out.println("FileUpload01 is running!!!");
String path = request.getSession().getServletContext().getRealPath("/uploads");
File file = new File(path);
if (!file.exists())
file.mkdirs();
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> fileItems = upload.parseRequest(request);
for (FileItem fileItem : fileItems) {
if (fileItem.isFormField()) {
} else {
String fileName = fileItem.getName();
String uuid = UUID.randomUUID().toString().replace("-", "");
fileName = uuid + "_" + fileName;
fileItem.write(new File(path, fileName));
fileItem.delete();
}
}
return "hello";
}
}
7.2 SpringMVC实现文件上传
SpringMVC本身提供了一个文件解析器用来解析request,需要在SpringMVC.xml中配置如下:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize">
<value>1048576</value>
</property>
</bean>
在配置了SpringMVC自带的文件解析器后就不需要用第三方jar包进行request中的文件解析。如下:
@Controller
@RequestMapping(value = "/User")
public class HelloWorld {
@RequestMapping(value = "/fileUpload02")
public String fileUpload02(MultipartFile upload, HttpServletRequest request) throws Exception {
System.out.println("FileUpload02 is running!!!");
String path = request.getSession().getServletContext().getRealPath("/uploads");
File file = new File(path);
if (!file.exists())
file.mkdirs();
String fileName = upload.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replace("-", "");
fileName = uuid + "_" + fileName;
upload.transferTo(new File(path, fileName));
return "hello";
}
}
注:public String fileUpload02(MultipartFile upload, HttpServletRequest request)中的upload与下面前端文件上传的<input type="file" name="upload">的name属性要一致。
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<head>
<title>index</title>
</head>
<body>
<form action="/User/fileUpload02" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"><br/>
<input type="submit" value="上传">
</form>
</body>
</html>
7.3 SpringMVC实现跨服务器文件上传
7.3.1 分服务器
 不同的服务器处理不同的额功能需求。
7.3.2 文件服务器的搭建
  
- 配置Tomcat服务器
 
7.3.3 应用服务器向文件服务器存储文件
@RequestMapping(value = "/fileUpload03")
public String fileUpload03(MultipartFile upload) throws Exception {
System.out.println("FileUpload03 is running!!!");
String path = "http://localhost:9090/uploads/";
String fileName = upload.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replace("-", "");
fileName = uuid + "_" + fileName;
Client client = Client.create();
WebResource resource = client.resource(path + fileName);
resource.put(upload.getBytes());
return "hello";
}
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<head>
<title>index</title>
</head>
<body>
<form action="/User/fileUpload03" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"><br/>
<input type="submit" value="上传">
</form>
</body>
</html>
注: ①需要在文件服务器下创建相应的文件夹  ②在应用服务器的conf/web.xml中配置 
8 异常处理
8.1 异常处理流程
 dao层、service层和controller层出现异常都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理。
8.2 异常处理
8.2.1 自定义异常类
package com.hc.exception;
public class SystemException extends Exception {
private String message;
public SystemException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
8.2.2 自定义异常处理器
package com.hc.exception;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SystemExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
SystemException systemException = null;
if (e instanceof SystemException) {
systemException = (SystemException) e;
} else {
systemException = new SystemException("系统错误!!!");
}
ModelAndView mv = new ModelAndView();
mv.addObject("message", systemException.getMessage());
mv.setViewName("error");
return mv;
}
}
8.2.3 配置异常处理器
<bean id="systemExceptionResolver" class="com.hc.exception.SystemExceptionResolver"/>
8.2.4 配置错误页面
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Error</title>
</head>
<body>
<h1>${pageContext.request.getAttribute("message")}</h1>
</body>
</html>
8.2.5 编写控制器
@RequestMapping(value = "testException")
public String testException() throws SystemException {
System.out.println("TestException is running!");
try {
int num = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
throw new SystemException("查询失败!!!");
}
return "hello";
}
8.2.6 测试

9 SpringMVC拦截器
9.1 什么是SpringMVC的拦截器
-
SpringMVC框架中的拦截器用于对处理器进行预处理和后处理的技术。 -
SpringMVC可以定义拦截器链,连接器链就是将拦截器按着一定的顺序结成一条链,在访问被拦截的方法时,拦截器链中的拦截器会按着定义的顺序执行。 -
拦截器和过滤器的功能比较: ①过滤器是Servlet规范的一部分,任何框架都可以使用过滤器技术。而拦截器是SpringMVC框架独有的。 ②过滤器配置了/*,可以拦截任何资源。而拦截器只会对控制器中的方法进行拦截。 -
自定义拦截器需要实现HandlerInterceptor接口。
9.2 SpringMVC拦截器配置
9.2.1 配置控制器方法
@RequestMapping(value = "testInterceptor")
public String testInterceptor() {
System.out.println("TestInterceptor is running!");
return "hello";
}
9.2.2 自定义拦截器
package com.hc.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SystemInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("SystemInterceptor preHandle is running!");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("SystemInterceptor postHandle is running!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("SystemInterceptor afterCompletion is running!");
}
}
9.2.3 配置自定义拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/User/*"/>
<bean id="systemInterceptor" class="com.hc.interceptor.SystemInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
9.2.4 测试

9.3 多个SpringMVC拦截器执行顺序
9.3.1 再次自定义一个拦截器
package com.hc.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SystemInterceptor02 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("SystemInterceptor02 preHandle is running!");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("SystemInterceptor02 postHandle is running!");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("SystemInterceptor02 afterCompletion is running!");
}
}
9.3.2 配置自定义拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/User/*"/>
<bean id="systemInterceptor" class="com.hc.interceptor.SystemInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/User/*"/>
<bean id="systemInterceptor02" class="com.hc.interceptor.SystemInterceptor02"/>
</mvc:interceptor>
</mvc:interceptors>
9.3.3 测试

9.4 测试afterCompletion方法
如果有多个拦截器,这时拦截器1的preHandle方法返回true,但是拦截器2的preHandle方法返回false,而此时拦截器1的afterCompletion方法会不会执行?  会执行!!!
|