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 方法会不会执行? 会执行!!!
|