SpringMVC

SpringMVC就是Spring的一个模块,它专用来做web开发。

SpringMVC项目必须有的一个servlet对象:DispatcherServlet(中央调度器,继承自HttpServlet,它也被称为【Front Controller】),它负责【接收】用户所有的请求,并将该请求【转发】给与该请求对应的被我们定义@controller注解的对象进行处理,所有controllers的处理结果会返回给中央调度器,中央调度器会将这些结果转发给View,View将结果数据添加到response报文中,最后由中央调度器将response报文回应给用户。

1. 开发步骤

先把依赖加好。

1.1 配置文件

  1. 在web服务器启动后就要创建DispatcherServlet对象的实例,因为该对象实例化(调用servlet对象的init()方法)过程中会创建springmvc容器对象,并读取springmvc配置文件,将该配置文件中所有的bean对象都创建好并放到全局作用域中。

  2. springmvc创建容器对象时,读取的配置文件默认是/WEB-INF/<servlet-name>-servlet.xml,不太灵活,因此一般都会自定义springmvc配置文件的路径。

在web.xml中配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<servlet>
<servlet-name>springmvc</servlet-name>

<!--web服务器启动后创建DispatcherServlet对象-->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!--自定义springmvc读取的配置文件路径-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value><!--自定义的配置文件名,放在resources目录下-->
</init-param>

<load-on-startup>1</load-on-startup> <!--web服务器启动时加载该servlet-->
</servlet>

1.2 配置mapping

也是在web.xml中配置,设置对哪些资源发起的请求交给哪个中央调度器处理

1
2
3
4
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

代表所有对以.do结尾的资源发起的请求都会转发给上一小节定义的中央调度器servlet。

1.2.1 url pattern详细

*.do代表的是所有动态文件,<url-pattern>*.do</url-pattern> 代表中央调度器只处理所有对动态资源的请求,对静态资源的请求依然由web服务器来完成(web服务器比如tomcat可以处理对静态资源的请求)。

如果更改为<url-pattern>/</url-pattern> ,这就会使tomcat能够处理静态资源请求的能力【失效】,客户端对所有资源文件的请求都会被发送到中央调度器,如果采用了这种配置,也不需要区分动静态资源了(是否加后缀do)。

【让动静态资源请求都经由中央调度器处理的配置方法】

在spring配置文件中加入<mvc:default-servlet-handler/>

为什么不让tomcat处理静态资源文件?

1.2.2 另外一种处理静态资源请求的方式(主要)

在spring的配置文件中加入

1
2
3
4
5
<!--
mapping代表访问静态资源的url,可以使用通配符,比如staticSource/**,**代表目录下所有文件和文件夹
location代表静态资源在本项目中的路径
-->
<mvc:resources mapping="/staticSource/**" location="/localStaticSource/"/> <!--localStaticSource目录下放项目中的所有静态资源-->

配置完成后框架会创建ResourceHttpRequestHandler来处理所有对localStaticSource目录下静态资源的请求。

1.3 创建controller类

在类的上面用@Controller注解

@RequestMapping可以放在类上,也可以放在方法上。

在方法上面用@RequestMapping注解,它的作用是将一个请求地址和一个方法绑定在一起,代表所有对某请求地址发起的请求都由该被注解的方法处理,它属性中的value为String类型,表示请求的url地址,该值必须唯一,一般以"/"开头。另外,被注解的方法有返回值,类型为ModelAndView。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Controller
public class MyController {

@RequestMapping(value = {"/somePage1.do", "somePage2.do"}) //value的值是个数组,即一个方法可以绑定多个资源
public ModelAndView func(){
//此处调用业务层方法处理somePage.do页面接收到的用户请求用户请求
service();

//业务层将用户请求处理完毕后,将结果数据(model)填充到ModelAndView中
ModelAndView mav = new ModelAndView();
mav.addObject("msg","hello springmvc");

//业务层处理完用户请求,将结果视图(view)填充到ModelAndView中
mav.setViewName("/show.jsp");

/**
* 返回mav,框架会自动帮我们:
*1. request.setAttribute("msg", "hello springmvc");
*2. request.getReqeustDispatcher("/show.jsp").forward(...);
*/
return mav;
}
}

在类上面用@RequestMapping注解,代表该类中用@RequestMapping注解的请求地址所属的公共模块,比如在类上注解了@RequestMapping("/user"),那么该类中方法上的注解@RequestMapping(value = {"/somePage1.do", "somePage2.do"}) 中somePage1.do和somPage2.do就都属于/user目录下的了。

1.3.1 指定浏览器提交数据的方式

通过配置@RequestMapping的method参数可以指定当浏览器以何种方式提交数据时用被注解的方法进行处理。

@RequestMapping(value = "/somePage.do", method = RequestMethod.POST)

即用户浏览器向somePage.do发起请求时,只有当请求是以post方法提交时才会调用被注解的函数对请求进行处理。

1.3.2 获取request、response和session对象

它们会作为形参传入被@RequestMapping注解的方法。

1
2
3
4
5
6
7
8
@RequestMapping(value = "/somePage.do", method = RequestMethod.POST)
public ModelAndView func(HttpServletRequest request, HttpServletResponse response){

ModelAndView mav = new ModelAndView();
mav.addObject("msg",request.getParameter("user")); //通过request获取到前端传来的user参数
mav.setViewName("/show.jsp");
return mav;
}

1.3.3 获取用户提交的参数

【逐个接收】

用户提交的key会作为形参传入被@RequestMapping注解的方法(key的名称必须一致)

比如用户提交了name=tom&age=12

我们可以通过定义方法参数获取这些数据,注意参数名必须和用户提交数据的key名称一致

1
2
3
4
5
6
7
8
9
10
@RequestMapping(value = "/somePage.do", method = RequestMethod.POST)
public ModelAndView func(String name, int age){
ModelAndView mav = new ModelAndView();
mav.addObject("myName",name); //第一个参数是结果页面用来获取第二个参数值的key,比如可通过${myName}获取到name
mav.addObject("myAge",age);

mav.setViewName("/show.jsp");

return mav;
}

这里会碰到两个问题:

  1. 用户输入的int类型参数为空时,报400错误

框架内部实现上述结果的原理是:先调用request.getParameter("name")获取到name对应的值(String类型),然后调用其valueOf方法将其转为我们定义的被@RequestMapping注解的方法的参数类型。本例中age的值会被转为int,那如果用户输入的age为空呢?就会出现400错误,因为空字符串不能被转为整数。因此,方法参数必须用【包装类型】,【本例中int应该改为Integer】来避免异常。

这种接收方法的好处:如果用户传入的参数类型不对,那么绑定的方法根本就不会执行,减少我们判断输入合法性的代码。

  1. post接收中文乱码

用过滤器处理,框架给我们提供了一个CharacterEncodingFilter,去web.xml下加入如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<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>

<!--强制request对象使用encoding的编码方式-->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>

<!--强制response对象使用encoding的编码方式-->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern> <!--所有请求都先被过滤器处理-->
</filter-mapping>

【对象接收】

对象的属性名必须与客户端提交的key名称相等,框架会自动创建对象并调用set方法为对象赋值。

假如对象定义如下,get和set方法已写好

1
2
3
public class Student {
private String name;
private Integer age;

被@RequestMapping注解的方法的参数列表中加入Student类型参数,框架会自动帮我们创建Student类型对象并将客户端传来的值赋给Student对应的属性。

1
2
3
4
5
6
7
8
9
@RequestMapping(value = "/somePage.do")
public ModelAndView func(Student student){
ModelAndView mav = new ModelAndView();
mav.addObject("name",student.toString());

mav.setViewName("/show.jsp");

return mav;
}

1.3.4 返回值类型

前面一直用ModelAndView做返回值,这是当请求处理后有数据产生,并且要做页面跳转时用的。如果请求处理后无数据产生(仅做页面跳转),或不需要做页面跳转,用ModelAndView就不合适了。

1.3.4.1 String

  1. 返回值String代表页面的名称,可直接跳转到对应页面。
1
2
3
4
5
6
@RequestMapping(value = "/somePage.do")
public String func(HttpServletRequest request, HttpServletResponse response){
request.setAttribute("name", "tom"); //这时也可以手动往request作用域中添加数据

return "/show.jsp";
}
  1. 返回值String是数据

区分String代表页面还是数据,就看方法有没有被@ResponseBody注解(在1.3.4.3中介绍),如果有就是数据,否则就是页面。

1.3.4.2 void(了解)

处理ajax时会使用void返回值,通过把数据写入HttpServletResponse响应ajax请求(ajax只请求数据,与视图无关)。这时写法与之前学习的servlet相同。

该方法的缺点在于其响应给客户端的数据不是json格式,如果要将其改为json格式又需要很多步骤,因此框架帮我们封装了将java数据改为json格式的操作。

1.3.4.3 Object

用Object返回可以解决void返回值的缺点,我们可以直接返回json类型的数据。

  1. 加jackson依赖

  2. 开启注解驱动,在spring配置文件中加入(注意看好右侧选择网址以mvc结尾的那个)

1
<mvc:annotation-driven/>
  1. 在需要返回json类型的方法上用@ResponseBody注解
1
2
3
4
5
6
@RequestMapping(value = "/somePage.do")
@ResponseBody
public Student returnJson(String name, Integer age){
Student stu = new Student("tom", 18);
return stu; //该stu会被转为json类型数据返回给请求方
}

上面是返回一个json对象,当然也可以返回一个json类型的list。回忆之前servlet中返回一个json类型数据的繁琐步骤,现在可以一步完成。

1.4 配置视图解析器

通过mav.setViewName("/WEB-INF/a/b/c/d/show.jsp"); 可以将视图填充到ModelAndView中,但是如果视图所在目录很深呢?不用每次都写全路径。

在springmvc的配置文件中添加:

1
2
3
4
5
6
7
8
<!--声明springmvc框架中的视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀是视图文件的路径-->
<property name="prefix" value="/WEB-INF/a/b/c/d/(注意前后都有斜杠)"/> <!--value中的第一个“/”代表webapp这个目录-->
<!--后缀是视图文件的扩展名-->
<property name="suffix" value=".jsp"/>
</bean>

现在指定路径就很方便了:mav.setViewName("show");

2. SSM整合开发

Spring负责业务层工作,它作为一个容器管理service、dao以及工具类对象。

SpringMVC负责视图层工作,接收请求以及显示处理结果,它作为Spring的子容器管理所有controller对象。SpringMVC和Spring类似于继承关系,SpringMVC这个子类可以访问其父类Spring中的对象,这样就可以在controller对象中调用service对象执行业务层逻辑。

MyBatis负责持久层工作,访问数据库。

【开发步骤】

2.1. 准备工作

  1. 创建好项目数据库
  2. 创建好包,dao、controller、domain和service等

2.2. 导入依赖并配置配置文件扫描路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
<dependencies>

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>

</dependencies>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>


<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.webjars/jquery -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.4.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.3</version>
</dependency>

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.3</version>
</dependency>



</dependencies>

<build>

<resources>
<resource>
<!--会去该目录下寻找xml配置文件(因为有些mapper文件放在entity包下)-->
<directory>src/main/java</directory>
<includes>
<include>**/*</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.0</version>
</plugin>
</plugins>
</build>

2.3. springmvc配置文件

在resources下创建conf目录,之后所有配置文件都放这里面。

springmvc配置文件习惯取名为dispatcherServlet.xml。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!--组件扫描器-->
<context:component-scan base-package="com.aaron.controller"/>

<!--视图解析器,会解析/WEB-INF/jsp/目录下的所有jsp文件-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>

<!--注解驱动(ajax和处理静态资源都要用到它),注意选择后缀带mvc的那个-->
<mvc:annotation-driven/>

</beans>

2.4. spring配置文件

也放在conf目录下,一般取名为applicationContext.xml。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--组件扫描器,扫描所有@service注解-->
<context:component-scan base-package="com.aaron.service"/>

<!--事务配置和aspectj配置,用到时再查文档加-->
<aop:aspectj-autoproxy/> <!--自动代理生成器-->


<!--
把数据库的配置信息写到独立的文件jdbc.properties中,并将该文件位置告诉spring
jdbc.properties内容如下:
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
然后在数据源声明中,声明方式如下:
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
-->
<context:property-placeholder location="classpath:conf/jdbc.properties"/>


<!--声明数据源-->
<bean id="myDataSource"
class="com.alibaba.druid.pool.DruidDataSource"
init-method="init"
destroy-method="close">
<!--使用set注入给DruidDataSource提供连接数据库信息-->
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>

<!--声明mybatis提供的SqlSessionFactory类-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="myDataSource"/>

<!--configLocation属性为Resource类型,读取value指定的位置的配置文件-->
<property name="configLocation" value="classpath:conf/mybatis.xml"/>
</bean>

<!--创建dao对象,该配置内部调用getMapper()生成每一个dao接口的代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--
指定dao接口所在包名,MapperScannerConfigurer会扫描这个包中所有接口,为它们一一
调用getMapper方法生成对应的dao对象,并将它们放到spring容器中
-->
<property name="basePackage" value="com.aaron.dao"/>
</bean>


</beans>

2.5. mybatis配置文件

在conf下创建mybatis.xml。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd"> <!--约束文件-->

<configuration>

<!--setting控制mybatis的全局行为-->
<!-- <settings>-->
<!-- &lt;!&ndash;输出mybatis日志&ndash;&gt;-->
<!-- <setting name="logImpl" value="STDOUT_LOGGING"/>-->
<!-- </settings>-->

<!--设置别名-->
<typeAliases>
<package name="com.aaron.domain"/>
</typeAliases>

<!--定位mapper文件-->
<mappers>
<!--让mybatis可以解析dao包下所有xml文件(因此mapper文件和对应的dao类必须都放在dao包下)-->
<package name="com.aaron.dao"/>
</mappers>
</configuration>

2.6. web.xml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">


<!--注册中央调度器-->
<servlet>
<servlet-name>springmvc</servlet-name>

<!--web服务器启动后创建DispatcherServlet对象(中央调度器)-->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!--自定义springmvc读取的配置文件路径-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:conf/dispatcherServlet.xml</param-value><!--自定义的配置文件名,放在resources目录下-->
</init-param>

<load-on-startup>1</load-on-startup> <!--web服务器启动时加载该servlet-->
</servlet>

<!--所有对符合url-pattern资源的访问都会由中央调度器处理-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>


<!--注册spring监听器,目的是创建spring容器对象-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:conf/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!--注册字符集过滤器,解决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>

</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern> <!--所有请求都先被过滤器处理-->
</filter-mapping>

</web-app>

3. 转发和重定向

forward和redirect都是关键字,它们都与视图解析器无关。

3.1 forward

当我们想要跳转的页面不在视图解析器的作用范围内,就可以用forward显示转发。

setViewName默认是通过forward完成页面跳转的。

1
2
3
4
5
6
7
8
9
10
11
12
@RequestMapping("/addStudent.do")
public ModelAndView addStudent(Student student){
String feedback = "regitser failed";
int num = service.addStudent(student);
if(num>0) feedback = student.getS_name()+" register succeed!";

ModelAndView mv = new ModelAndView();
mv.addObject("feedback", feedback);
mv.setViewName("forward:/WEB-INF/someDir/somePage.jsp"); //将跳转到视图解析器范围以外的somePage.jsp

return mv;
}

3.2 redirect

1
2
3
4
5
6
7
8
@RequestMapping("/testRedirect.do")
public ModelAndView testRedirect(int num){
ModelAndView mv = new ModelAndView();
mv.addObject("feedback", num);
mv.setViewName("redirect:/result.jsp"); //重定向跳转,正常情况下本次请求的数据就丢失了,不过框架在发起重定向请求时会帮我们把本次请求的参数传递过去。因此这句相当于框架会跳转到目标网址并发起get请求,还会带上参数:`?feedback=12`(假如传过来的num为12)。

return mv;
}

现在前端要通过${param.feedback}来取值,用${feedback}取不到。

重定向不能访问WEB-INF下的资源

4. 异常处理

不同方法中经常要处理相同的异常,而且一些情况下异常代码可能比本身的业务逻辑代码要多的多,很杂乱。springmvc可以帮我们处理异常,现在我们只需要把异常抛给框架即可。

springmvc采用统一,全局的异常处理,把controller中所有异常处理都集中到一个地方,其实现方法就是利用AOP来解耦合。

【步骤】

controller发生的异常统统抛出。

4.1 创建一个全局异常处理类

在类的上面加入@ControllerAdvice 注解。

在类中的方法上加入@ExceptionHandler 注解。

4.2 创建异常发生时要跳转的页面

4.3 springmvc配置文件

新增一个组件扫描器扫描@ControllerAdvice所在的包名。

5. 拦截器

拦截器和过滤器的区别在于:过滤器用来过滤请求参数,设置编码字符集等工作;拦截器拦截用户的请求,并对其做判断处理。

拦截器是全局的,一个项目中可以有多个拦截器,一个拦截器可以同时拦截多个controller接收到的请求。拦截器通常用在用于登陆处理、权限检查和记录日志等。

【拦截器的执行时间】

在controller的方法执行之前、之后以及请求处理完成后,即拦截器功能可以在三个不同的时机执行,可自行选择。

【拦截器的使用步骤】

  1. 定义类,实现HandlerInterceptor接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class MyInterception implements HandlerInterceptor {

/**
* 用户请求会先被该方法处理(即controller执行前)
* 一般用来验证用户登陆,验证其是否有权限访问某个url
* 如果验证失败,可以将请求截断并丢弃return false
* 如果验证成功,可将请求进一步递交给controller方法 return true
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}


/**
* controller方法执行完后会执行该方法
* 能够获取到controller方法的返回值ModelAndView,可以修改它
* 主要用途是对controller方法执行结果进行修改
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

}

/**
* 在请求处理完成后执行,框架将【视图被forward】视为请求处理完成
* 一般做资源回收工作
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

}
}

  1. 在springmvc配置文件中声明拦截器
1
2
3
4
5
6
7
8
9
10
<!--声明拦截器,可以有多个-->
<mvc:interceptors>
<!--逐个声明拦截器,先声明的先执行-->
<mvc:interceptor>
<!--path就是指定拦截的url,可用通配符-->
<mvc:mapping path="/user/**"/> <!--代表对student目录下任何资源发起的请求都会被拦截-->
<!--声明拦截器对象,即拦截下的请求用哪个拦截器处理-->
<bean class="com.aaron.handler.MyInterception"/>
</mvc:interceptor>
</mvc:interceptors>

5.1 拦截器和过滤器的区别

  1. 过滤器是servlet对象,拦截器是框架中的对象
  2. 过滤器实现Filter接口,拦截器实现HandlerInterceptor接口
  3. 过滤器用来设置request和response参数,侧重于数据过滤;拦截器用来验证请求。
  4. 过滤器先于拦截器执行
  5. 过滤器对象由tomcat创建,拦截器对象由springmvc容器创建
  6. 过滤器只有一个执行时机,拦截器有三个
  7. 过滤器处理静态资源,拦截器侧重拦截对动态资源发起的请求(只要请求可以被DispatcherServlet接收,就可以配置使得其被拦截器拦截)