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>
<!-- FIXME change it to the project's website -->
<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>
<!--Spring核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--SpringMVC核心依赖-->
<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>
<!--Servlet和JSP依赖-->
<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>
<!--JUnit依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>HelloSpringMVC</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<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>
<!-- 配置SpringMVC的核心控制器 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置初始化参数,用于读取SpringMVC的配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:SpringMVC.xml</param-value>
</init-param>
<!-- 配置servlet的对象的创建时间点:应用加载时创建。 取值只能是非0正整数,表示启动顺序 -->
<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">
<!-- 配置创建Spring容器的注解支持 -->
<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>
<!-- 配置spring开启注解mvc的支持 -->
<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 {
/**
* 映射url
*
* @return
*/
@RequestMapping(value = "hello")
public String hello() {
System.out.println("HelloWorld!!!");
// 返回的页面名称
return "hello";
}
}
2.5 JSP页面
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index</title>
</head>
<body>
<a href="/hello">登陆</a>
</body>
</html>
hello.jsp
<%@ 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
:处理器,即Controller
。它就是我们开发中要编写的具体业务控制器。由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
:用于指定限制请求消息头的条件。produces
:设置返回数据的类型以及编码,可以是json
、xml
或html
等。例如,RequestMapping(value = "/toList", produces = "text/html;charset=utf-8")
。注意,produces
必须要和@ResponseBody
注解一起使用才可以,不加@ResponseBody
注解相当于按照和返回String
同名jsp
页面解析自然就会报错。当然,如果反过来,不加produces
属性,只有@ResponseBody
注解的话也是没有问题的,只是在浏览器中直接访问的时候有区别。
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 {
/**
* 映射url
*
* @return
*/
@RequestMapping(value = "hello", params = {"username"}, method = RequestMethod.POST)
public String hello(String username, String password) {
System.out.println(username + " " + password);
// 返回的页面名称
return "hello";
}
}
JSP
页面代码
<%@ 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 {
/**
* 映射url
*
* @return
*/
@RequestMapping(value = "hello", params = {"username"}, method = RequestMethod.POST)
public String hello(User user) {
System.out.println(user.toString());
// 返回的页面名称
return "hello";
}
}
JSP
代码
<%@ 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 {
/**
* 映射url
*
* @return
*/
@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
请求时,会出现中文乱码问题。配置过滤器处理中文乱码:
<!-- 配置SpringMVC编码过滤器 -->
<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
中配置:
<!--
在springmvc的配置文件中可以配置,静态资源不过滤:
location表示路径,mapping表示文件,**表示该目录下的文件以及子目录的文件
-->
<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);
}
}
}
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">
<!-- 配置创建Spring容器的注解支持 -->
<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>
<!--
配置spring开启注解mvc的支持
在annotation-driven标签中引用配置的类型转换服务
-->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"></mvc:annotation-driven>
</beans>
5 常用注解
5.1 RequestParam注解
- 作用:把请求中指定名称的参数给控制器中的形参赋值。
- 属性:
value
表示请求参数中的名称。required
表示请求参数中是否必须提供此参数。默认值:true
,表示必须提供,如果不提供将报错。
上图表示,将提交的url
中的name
映射为username
。
5.2 RequestBody注解
- 作用:(1)用于获取请求体内容。直接使用得到是
key=value&key=value
这样的结构的数据。get
请求方式不适用。(2)放在方法上返回不同格式的数据,例如返回xml
格式的页面数据。 - 属性:
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
方法没有返回值,会通过视图解析器访问以方法名作为页面名称的页面。
- 利用
Servlet
原始API
进行页面跳转
@RequestMapping(value = "/testReturnVoid")
public void testReturnVoid(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("TestReturnVoid is running!");
// 1.请求转发,一次请求,需要将页面的路径补全,但是不需要项目路径
// request.getRequestDispatcher("/WEB-INF/pages/hello.jsp").forward(request, response);
// 2.重定向,两次请求,例如可以重定向至前面的String返回,仅需要写低一级的 @RequestMapping即可
// response.sendRedirect("testReturnString");
// 3.使用response直接返回结果
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();
// 将键值对封装放入request域中
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 // 使用@ResponseBody注解把JavaBean对象转换成json字符串,直接响应
User testJson(@RequestBody User user) { // 使用@RequestBody注解把json的字符串转换成JavaBean的对象
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>
实现如下:
JSP
页面
<%@ 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!!!");
// 1、设置上传的位置,如果该文件夹不存在则创建
String path = request.getSession().getServletContext().getRealPath("/uploads");
File file = new File(path);
if (!file.exists())
file.mkdirs();
// 2、解析request对象,获取上传文件项
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> fileItems = upload.parseRequest(request);
for (FileItem fileItem : fileItems) {
// 进行判断,当前item对象是否是上传文件项
if (fileItem.isFormField()) {
} else {
// 获取上传文件的名称,并依据uuid把文件的名称设置唯一值
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
中配置如下:
<!--
配置文件上传解析器,id的值是固定的,只能是multipartResolver
-->
<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!!!");
// 1、设置上传的位置,如果该文件夹不存在则创建
String path = request.getSession().getServletContext().getRealPath("/uploads");
File file = new File(path);
if (!file.exists())
file.mkdirs();
// 2、获取上传文件的名称,并依据uuid把文件的名称设置唯一值
String fileName = upload.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replace("-", "");
fileName = uuid + "_" + fileName;
// 3、完成文件上传
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!!!");
// 1、设置上传的位置
String path = "http://localhost:9090/uploads/";
// 2、获取上传文件的名称,并依据uuid把文件的名称设置唯一值
String fileName = upload.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replace("-", "");
fileName = uuid + "_" + fileName;
// 3、完成跨服务器文件上传
// 创建客户端的对象
Client client = Client.create();
// 和图片服务器进行连接
WebResource resource = client.resource(path + fileName);
// 上传文件
resource.put(upload.getBytes());
return "hello";
}
JSP
页面
<%@ 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 {
/**
* 如何调用:按拦截器定义顺序调用
* 何时调用:只要配置了都会调用
* 有什么用:如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true。
* 如果程序员决定不需要再调用其他的组件去处理请求,则返回false。
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("SystemInterceptor preHandle is running!");
return true;
}
/**
* 如何调用:按拦截器定义逆序调用
* 何时调用:在拦截器链内所有拦截器返成功调用
* 有什么用:在业务处理器处理完请求后,但是DispatcherServlet向客户端返回响应前被调用,在该方法中对用户请求request进行处理。
*
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("SystemInterceptor postHandle is running!");
}
/**
* 如何调用:按拦截器定义逆序调用
* 何时调用:只有preHandle返回true才调用
* 有什么用:可以在该方法中进行一些资源清理的操作。
* 注:如果有多个拦截器,这时拦截器1的preHandle方法返回true,但是拦截器2的preHandle方法返回false,而此时拦截器1的afterCompletion方法是执行的
*
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@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>
<!--用于指定对拦截的url-->
<mvc:mapping path="/User/*"/>
<!--用于指定排除的url-->
<!-- <mvc:exclude-mapping path=""/>-->
<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 {
/**
* 如何调用:按拦截器定义顺序调用
* 何时调用:只要配置了都会调用
* 有什么用:如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true。
* 如果程序员决定不需要再调用其他的组件去处理请求,则返回false。
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("SystemInterceptor02 preHandle is running!");
return true;
}
/**
* 如何调用:按拦截器定义逆序调用
* 何时调用:在拦截器链内所有拦截器返成功调用
* 有什么用:在业务处理器处理完请求后,但是DispatcherServlet向客户端返回响应前被调用,在该方法中对用户请求request进行处理。
*
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("SystemInterceptor02 postHandle is running!");
}
/**
* 如何调用:按拦截器定义逆序调用
* 何时调用:只有preHandle返回true才调用
* 有什么用:可以在该方法中进行一些资源清理的操作。
* 注:如果有多个拦截器,这时拦截器1的preHandle方法返回true,但是拦截器2的preHandle方法返回false,而此时拦截器1的afterCompletion方法是执行的
*
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@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>
<!--用于指定对拦截的url-->
<mvc:mapping path="/User/*"/>
<!--用于指定排除的url-->
<!-- <mvc:exclude-mapping path=""/>-->
<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
方法会不会执行?
会执行!!!