0 、概述

服务器端分成三层架构。

三层架构

一、环境搭建

1.1 Maven环境的创建

  1. 导入坐标依赖

    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
    <!-- 版本锁定 -->
    <spring.version>5.0.2.RELEASE</spring.version>
    <!-- 配置依赖 -->
    <dependencies>
    <!-- spring IOC -->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
    </dependency>
    <!-- spring web -->
    <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>
    <scope>provided</scope>
    </dependency>
    <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.0</version>
    <scope>provided</scope>
    </dependency>
    </dependencies>
  2. 配置核心的控制器(类似servlet类 – dispatcherServlet)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <!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>
    </servlet>

    <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>
    </web-app>

<url-pattern>/</url-pattern>时,默认就是拦截所有路径连静态资源也不能访问。但是Controller中配置@RequestMapping的路径是不会被拦截的,配置了@RequestMapping就相当于在web.xml中注<servlet>

1.2 第一个执行程序

1.2.1 引入SpringMVC配置文件

resources下新建springMVC.XML配置文件:

1
2
3
4
5
6
7
8
<?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">
</beans>

1.2.2 编写控制器

1
2
3
4
5
6
7
8
9
10
// 控制器
@Controller
public class HelloController {

@RequestMapping("/hello") // 请求的资源路径
public String sayHello() {
System.out.println("Hello SpringMVC");
return "success"; // 需要跳转的页面,默认jsp文件名
}
}
  1. 控制器类配置@Controller注解,表明是控制器。
  2. 在具体的方法上配置@RequestMapping注解,参数即为访问时的资源路径
  3. return中的字符串,方法执行完需要跳转的页面

1.2.3 完善springMVC.xml配置文件

  1. 控制器中加入注解,那么就需要配置需要扫描的包;
  2. 配置文件解析器,创建IOC容器对象,由Tomcat负责调用;
  3. 配置spring开启注解MVC的支持
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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="cn.lizhi"></context:component-scan>
<!-- 配置视图解析器 Ioc容器对象,由tomcat调用-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 文件具体所在的目录 -->
<property name="prefix" value="/WEB-INF/pages/"></property>
<!-- 文件解析的类型(后缀名) -->
<property name="suffix" value=".jsp"></property>
</bean>

<!-- 配置spring开启注解mvc的支持 -->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>

WEB-INF目录下的内容是对客户端不可见,只对服务端可见,即客户端不能直接对其进行访问。

1.2.4 完善web.xml配置文件

由于上方的springMVC.XML配置文件时在resources目录下的,我们启动的是web项目,就需要将该配置文件加载进Tomcat服务器进行读取。

即:配置Servlet的初始化参数,读取springMVC的配置文件,创建spring容器,用于加载配置文件。

MVC配置文件加载成功,那么其中的扫描就能够成功,继而将控制器中的类加载成对象。

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
<!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>
<!-- 配置Servlet的初始化参数,读取springMVC的配置文件,创建spring容器,用于加载配置文件
MVC配置文件加载成功,那么其中的扫描就成功,继而到控制器中类加载成对象
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<!-- 服务器在启动时,就加载资源
配置servlet的对象的创建时间点:应用加载时创建。-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!-- 配置资源路径/表明所有的资源皆可被访问到 -->
<url-pattern>/</url-pattern>
<!-- 表明只有hello的资源路径可以被访问到
<url-pattern>/hello</url-pattern>
-->
</servlet-mapping>
</web-app>

二、Spring MVC 详解

2.1 执行过程及组件分析

2.1.1 执行过程

  1. 当启动Tomcat服务器的时候,因为配置了load-on-startup标签,所以会创建DispatcherServlet对象,就会加载springmvc.xml配置文件。这里是服务器启动,应用被加载。读取到web.xml中的配置创建spring容器并且初始化容器中的对象。
  2. springmvc.xml配置文件中开启了注解扫描,那么相应的controller对象(HelloController)对象就会被创建。
  3. index.jsp发送请求,请求会先到达DispatcherServlet核心控制器,根据配置@RequestMapping注解找到执行的具体方法。浏览器发送请求,被DispatcherServlet捕获,该Servlet并不处理请求,而是把请求转发出去。转发的路径是根据请求URL,匹配@RequestMapping中的内容。
  4. 根据执行方法的返回值,再根据配置的视图解析器,去指定的目录下查找指定名称的JSP文件。即:根据方法的返回值,借助InternalResourceViewResolver找到对应的结果视图。
  5. Tomcat服务器渲染页面,做出响应。

执行过程

2.1.2 组件分析

  1. DispatcherServlet:前端控制器

    用户请求到达前端控制器,它就相当于MVC模式中的cdispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。

    dispatcherServlet

  2. HandlerMapping:处理器映射器

    HandlderMapping负责根据用户请求找到Handler,即处理器,SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

  3. Handler:处理器

    它就是开发中编写的具体业务控制器。由DispatcherServlet把用户请求转发到Handler。由Handler对具体的用户请求进行处理。

  4. Handler:处理器适配器

    通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

  5. View Resolver:视图解析器

    View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。

  6. View:视图

    将数据视图展示给客户端,即用户。

2.1.3 <mvc:annotation-driver>说明

SpringMVC的各个组件中,处理器映射器、处理器适配器、视图解析器成为SpringMVC的三大组件。使用<mvc:annotation-driver>自动加载RequestMappingHandlerMapping(处理器映射器)和RequestMappingHandlerAdapter(处理器适配器)。

2.2 常用注解说明

2.2.1 RequestMapping

作用:用于建立请求URL和处理请求方法之间的对应关系。

2.2.2 RequestParam

作用:把请求中指定名称的参数给控制器中的形参赋值。(适用于请求名称与属性名不相同的情况)

属性:

  • value:请求参数的名称。
  • required:请求参数中是否必须提供此参数。默认值:true。表示必须提供,如果不提供将报错。
1
2
3
4
5
@RequestMapping("/useRequestParam")
public String useRequestParam(@RequestParam("name")String username,@RequestParam(value="age",required=false)Integer age){
System.out.println(username+","+age);
return "success";
}

2.2.3 RequestBody

作用:用于获取请求体内容。直接使用得到是key=value&key=value...结构的数据。get请求方式不适用。

属性:

  • required:是否必须有请求体。默认值是:true。当取值是true时,get请求方式会报错。如果取值为falseget请求得到是null
1
2
3
4
5
@RequestMapping("/useRequestBody")
public String useRequestBody(@RequestBody(required=false) String body){
System.out.println(body);
return "success";
}

2.2.4 PathVariable

作用:用于绑定url中的占位符。例如:请求url/delete/{id},这个{id}就是url占位符。url支持占位符是spring3.0之后加入的。是springmvc支持rest风格URL的一个重要标志。

属性:

  • value:用于指定url中占位符名称。
  • required:是否必须提供占位符。

重点restful风格。

1
2
3
4
5
@RequestMapping("/usePathVariable/{id}")
public String usePathVariable(@PathVariable("id") Integer id){
System.out.println(id);
return "success";
}

2.2.5 RequestHeader

作用:用于获取请求消息头

属性:

  • value:提供消息头名称,用于指定获取消息头中的哪一部分。
  • required:是否必须有此消息头。
1
2
3
4
5
6
@RequestMapping("/useRequestHeader")
public String useRequestHeader(@RequestHeader(value="Accept-Language",
required=false)String requestHeader){
System.out.println(requestHeader);
return "success";
}

将请求头内容的信息封装到requestHeader参数中。

2.2.6 CookieValue

作用:用于把指定cookie名称的值传入控制器方法参数。

属性:

  • value:指定cookie的名称。键值对的形式,通过键来获取到它的值。
  • required:是否必须有cookie
1
2
3
4
5
6
@RequestMapping("/useCookieValue")
public String useCookieValue(@CookieValue(value="JSESSIONID",required=false)
String cookieValue){
System.out.println(cookieValue);
return "success";
}

2.2.7 ModelAttribute

作用:该注解是SpringMVC4.3版本以后新加入的。它可以用于修饰方法和参数。

出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。

出现在参数上,获取指定的数据给参数赋值。

属性:

  • value:用于获取数据的keykey可以是POJO的属性名称,也可以是Map结构的key

应用场景:

当表单提交数据不是完整的实体类数据时,保证没有提交数据的字段使用数据库对象原来的数据。

例如:

当我们在编辑一个用户时,用户有一个创建信息字段,该字段的值是不允许被修改的。在提交表单数据时肯定没有此字段的内容,一旦更新会把该字段内容置为null,此时就可以使用此注解解决问题。

示例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
@ModelAttributepublic 
public void showModel(User user) {
System.out.println("执行了showModel方法"+user.getUsername());
}
/***
接收请求的方法
* @param user* @return
*/
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user) {
System.out.println("执行了控制器的方法"+user.getUsername());
return "success";
}

即先执行showModel方法,再执行控制器中的方法testModelAttribute

2.2.7.1 ModelAttribute 修饰方法带返回值

需求:修改用户信息,要求用户密码不能修改

前端代码:

1
2
3
4
5
<form action="springmvc/updateUser" method="post">
用户名称:<input type="text" name="username" ><br/>
用户年龄:<input type="text" name="age" ><br/>
<input type="submit" value="保存">
</form>
  1. 模拟查询数据库中用户信息
1
2
3
4
5
6
7
@ModelAttribute
public User showModel(String username) {
//模拟去数据库查询
User userByName = findUserByName(username);
System.out.println("执行了 showModel 方法"+userByName);
return userByName;
}
  1. 模拟修改用户方法
1
2
3
4
5
@RequestMapping("/updateUser")
public String testModelAttribute(User user) {
System.out.println("控制器中处理请求的方法:修改用户:"+user);
return "success";
}
  1. 模拟去数据库查询
1
2
3
4
5
6
7
8
private User findUserByName(String username) {
// 以下4句用来模拟数据库中的原有对象,即查询出来的对象
User user = new User();
user.setUsername(username);
user.setAge(19);
user.setPassword("123456");
return user;
}

首先通过前端请求,获取username,而在showModel方法中参数,就是由前端请求的参数username。然后showModel方法体中通过数据库查询对象,再将对象进行返回。返回的对象是数据中的相对应的原有对象。(类似过滤器,后续在控制器中将需要修改的值,进行修改,不变的值,就不需要再次改动)。

2.2.7.2 ModelAttribute 修饰方法不带返回值

需求:修改用户信息,要求用户的密码不能修改

  1. 前端代码
1
2
3
4
5
6
<!-- 修改用户信息 --> 
<form action="springmvc/updateUser" method="post">
用户名称:<input type="text" name="username" ><br/>
用户年龄:<input type="text" name="age" ><br/>
<input type="submit" value="保存">
</form>
  1. 查询数据库中用户信息– 模拟去数据库查询
1
2
3
4
5
6
7
@ModelAttribute
public void showModel(String username,Map<String,User> map) {
//模拟去数据库查询
User user = findUserByName(username);
System.out.println("执行了 showModel 方法"+user);
map.put("abc",user);
}
  1. 模拟修改用户方法
1
2
3
4
5
@RequestMapping("/updateUser")
public String testModelAttribute(@ModelAttribute("abc")User user) {
System.out.println("控制器中处理请求的方法:修改用户:"+user);
return "success";
}
  1. 模拟去数据库查询
1
2
3
4
5
6
7
private User findUserByName(String username) {
User user = new User();
user.setUsername(username);
user.setAge(19);
user.setPassword("123456");
return user;
}

第二种不带返回值的方式更容易理解,即将原有的对象存放在map集合中,再通过注解,获取指定(根据map中的键)的数据给参数赋值。最后,将前端请求的参数对应的封装到这个参数中,就能够保证这个参数对象的所有属性都能够有值。

2.2.8 SessionAttributes

作用:用于多次执行控制器方法间的参数共享。只能作用在类对象(Class)上。

属性:

  • value:用于指定存入的属性名称
  • type:用于指定存入的数据类型
  1. 前端代码
1
2
3
4
<!-- SessionAttribute 注解的使用 --> 
<a href="springmvc/testPut">存入 SessionAttribute</a> <hr/>
<a href="springmvc/testGet">取出 SessionAttribute</a> <hr/>
<a href="springmvc/testClean">清除 SessionAttribute</a>
  1. 控制器中的代码
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
@Controller("sessionAttributeController")
@RequestMapping("/springmvc")
@SessionAttributes(value ={"username","password"},types={Integer.class})
public class SessionAttributeController {
/**
* 把数据存入 SessionAttribute
* @param model
* @return
* Model 是 spring 提供的一个接口,该接口有一个实现类 ExtendedModelMap
* 该类继承了 ModelMap,而 ModelMap 就是 LinkedHashMap 子类
*/
@RequestMapping("/testPut")
public String testPut(Model model){
model.addAttribute("username", "泰斯特");
model.addAttribute("password","123456");
model.addAttribute("age", 31);
//跳转之前将数据保存到 username、password 和 age 中,因为注解@SessionAttribute 中有这几个参数
return "success";
}

@RequestMapping("/testGet")
public String testGet(ModelMap model){
System.out.println(model.get("username")+";"+model.get("password")+";"+model.get("a
ge"));
return "success";
}

@RequestMapping("/testClean")
public String complete(SessionStatus sessionStatus){
sessionStatus.setComplete();
return "success";
}
}

通过model对象进行值的存储操作,底层会将值存储在request域中。而又使用@SessionAttributes注解,具体有@SessionAttributes(value ={"username","password"},types={Integer.class})。此时通过values数组的指定 – usernamepassword,则表明model在存储操作时,不仅会将这两个的值存储在request域对象中,同时也会存储在session域对象中。

取值时,使用的model实现类 – ModelMap

删除时,使用SessionStatus

2.3 请求参数的绑定

前端向后端进行请求时,表单中请求参数都是基于key=value的。SpringMVC绑定请求参数的过程是通过把表单请求参数,作为控制器中方法参数进行绑定的。

2.3.1 支持的数据类型

  1. 基本数据类型

    • 基本数据类型
    • String类型
  2. POJO类型参数(实现序列化接口)

    • 实体类
    • 关联的实体类
  3. 数组和集合类型参数

    包括List结构和Map结构的集合(包括数组)

SpringMVC绑定请求参数是自动实现的,但是想要使用,必须遵循使用要求。

2.3.2 使用要求

  1. 基本数据类型或者是String类型:

要求我们的参数名称必须和控制器中方法的形参名称保持一致。(严格区分大小写)

  1. POJO类型或者及其关联对象

如果表单中参数名称和POJO类的属性名称保持一致。并且控制器方法的参数类型是POJO类型。

  1. 如果是集合类型

    • 第一种

      要求集合类型的请求参数必须在POJO中。在表单中请求参数名称要和POJO中集合属性名称相同。

      List集合中的元素赋值,使用下标。

      Map集合中的元素赋值,使用键值对。

    • 第二种

      接收的请求参数是json格式数据。需要借助一个注解实现。

  2. SpringMVC可以实现一些数据类型自动转换。其内置转换器全部都在:

    org.springframework.core.convert.support包下。

2.3.3 使用实例

2.3.3.1 POJO类型作为参数
  1. 实体类代码
1
2
3
4
5
6
7
8
9
10
11
public class Account implements Serializable {

private Integer accountId;
private String accountName;
private Float money;
private Address address;

// getters and setters 方法

// toString方法
}

以上是Account类。其中关联Address类:

1
2
3
4
5
6
7
8
9
10
11
package cn.lizhi.domain;

public class Address {

private String provinceName;
private String cityName;

// getters and setters 方法

// toString方法
}
  1. 前端代码
1
2
3
4
5
6
7
<form action="account/saveAccount" method="post">
账户名称:<input type="text" name="name" ><br/>
账户金额:<input type="text" name="money" ><br/>
账户省份:<input type="text" name="address.provinceName" ><br/>
账户城市:<input type="text" name="address.cityName" ><br/>
<input type="submit" value="保存">
</form>

通过对象.属性的方式进行赋值。

  1. 控制器代码
1
2
3
4
5
@RequestMapping("/saveAccount")
public String saveAccount(Account account) {
System.out.println("保存了账户。。。。"+account);
return "success";
}
2.3.3.2 POJO类中包含集合类型参数
  1. 实体类 – User
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
package cn.lizhi.domain;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class User {

private String username;
private String password;
private Integer age;
private Date date;
private List<Account> accounts;
private Map<String,Account> accountMap;

// getter and setter

@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
", date=" + date +
", accounts=" + accounts +
", accountMap=" + accountMap +
'}';
}
}
  1. 前端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- POJO 类包含集合类型演示 -->
<form action="account/updateAccount" method="post">
用户名称:<input type="text" name="username" ><br/>
用户密码:<input type="password" name="password" ><br/>
用户年龄:<input type="text" name="age" ><br/>
账户 1 名称:<input type="text" name="accounts[0].name" ><br/>
账户 1 金额:<input type="text" name="accounts[0].money" ><br/>
账户 2 名称:<input type="text" name="accounts[1].name" ><br/>
账户 2 金额:<input type="text" name="accounts[1].money" ><br/>
账户 3 名称:<input type="text" name="accountMap['one'].name" ><br/>
账户 3 金额:<input type="text" name="accountMap['one'].money" ><br/>
账户 4 名称:<input type="text" name="accountMap['two'].name" ><br/>
账户 4 金额:<input type="text" name="accountMap['two'].money" ><br/>
<input type="submit" value="保存">
</form>

集合、列表赋值的方式。三要素:属性下标参数(属性)

  1. 控制器代码
1
2
3
4
5
@RequestMapping("/updateAccount")
public String updateAccount(User user) {
System.out.println("更新了账户。。。。"+user);
return "success";
}

2.4 请求参数乱码问题

tomcat8以后get请求方式,中文正常显示;post请求方式,中文会出现乱码问题。

在之前的serlvet学习中,对乱码解决的方式,是通过Filter过滤器。而现在SpringMVC提供好现有的类供我们使用。

首先post请求方式,在web.xml中配置一个过滤器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 配置解决中文乱码的过滤器 -->
<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>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>

如果想不过滤静态资源,在 Springmvc 的配置文件中可以配置,静态资源不过滤:

1
2
3
4
<!-- location 表示路径,mapping 表示文件,**表示该目录下的文件以及子目录的文件 -->
<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/images/" mapping="/images/**"/>
<mvc:resources location="/scripts/" mapping="/javascript/**"/>

get请求方式:

tomcatGETPOST请求处理方式是不同的,GET请求的编码问题,要改tomcatserver.xml配置文件,如下:

1
2
3
4
5
6
7
8
<Connector connectionTimeout="20000" port="8080"
protocol="HTTP/1.1" redirectPort="8443"/>
改为:
<Connector connectionTimeout="20000" port="8080"
protocol="HTTP/1.1" redirectPort="8443"
useBodyEncodingForURI="true"/>
<!-- 如果遇到 ajax 请求仍然乱码,请把:-->
useBodyEncodingForURI="true"改为 URIEncoding="UTF-8"

2.5 自定义类型转换器

例如,在前端输入日期格式时:

1
2
<!-- 特殊情况之:类型转换问题 --> 
<a href="account/deleteAccount?date=2018-01-01">根据日期删除账户</a>

可以看出,请求参数中,date=2018-01-01

当我们在后端接收到请求时:

1
2
3
4
5
@RequestMapping("/deleteAccount")
public String deleteAccount(String date) {
System.out.println("删除了账户:" + date);
return "success";
}

输出:删除了账户:2018-01-01。如果当我们将接收参数的类型改为Date类型时:

1
2
3
4
5
@RequestMapping("/deleteAccount")
public String deleteAccount(Date date) {
System.out.println("删除了账户:" + date);
return "success";
}

此时,前端再次请求时,会报400

下面便是我们自定义类型转化的方式:

首先,定义一个类,实现Convertet接口,该接口有两个泛型。

1
2
3
4
5
6
7
public interface Converter<S, T> {//S:表示接受的类型,T:表示目标类型
/**
* 实现类型转换的方法
*/
@Nullable
T convert(S source);
}

其中,S表示接受的类型,T表示目标类型。

其实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class StringToDate implements Converter<String, Date> {
@Override
public Date convert(String source) {

if (source == null || source == "") {
throw new RuntimeException("请输入日期");
}

SimpleDateFormat format = new SimpleDateFormat("yyyy-mm-dd");
try {
Date data = format.parse(source);
return data;
} catch (ParseException e) {
throw new RuntimeException("请输入正确的日期格式");
}

}
}

其次,在Spring配置文件中配置类型转换器。

Spring配置类型转换器的机制是,将自定义的转换器注册到类型转换服务中去。

1
2
3
4
5
6
7
8
9
10
11
12
<bean id="converterService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<!-- 给工厂注入一个新的类型转换器 -->
<property name="converters">
<array>
<!-- 配置自定义类型转换器 -->
<bean class="cn.lizhi.utils.StringToDate"/>
</array>
</property>
</bean>

<!-- 配置spring开启注解mvc的支持 -->
<mvc:annotation-driven conversion-service="converterService"></mvc:annotation-driven>

通过查看源码:

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
public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {
@Nullable
private Set<?> converters;
@Nullable
private GenericConversionService conversionService;

public ConversionServiceFactoryBean() {
}

public void setConverters(Set<?> converters) {
this.converters = converters;
}

public void afterPropertiesSet() {
this.conversionService = this.createConversionService();
ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
}

protected GenericConversionService createConversionService() {
return new DefaultConversionService();
}

@Nullable
public ConversionService getObject() {
return this.conversionService;
}

public Class<? extends ConversionService> getObjectType() {
return GenericConversionService.class;
}

public boolean isSingleton() {
return true;
}
}

其中converters是集合类型,并且有setter方法,通过上方的spring配置文件,给工厂注入一个新的类型转换器(不会覆盖原有的转换器)。

这样,在我们后面的使用中,便能够识别并对应转换成我们相应数据格式。

总之,实现转换器的方法以及对spring的配置,就是在原有的转换器集合中,再加入一种我们自己编写的转换器,以供我们使用。

2.6 响应数据

2.6.1 方法返回值的分类

2.6.1.1 String 类型

Controller方法返回字符串可以指定逻辑视图的名称,根据视图解析器为物理视图的地址。

即为前面所写的常规的返回值为String的方法,返回值为所需要跳转的物理视图的名称(即为跳转页面名称 –>地址)。

1
2
3
4
5
6
@RequestMapping(value="/hello") 
public String sayHello() {
System.out.println("Hello SpringMVC!!");
// 跳转到XX页面 -- 跳转到success页面
return "success";
}

模拟应用 – 模拟对数据库中的数据进行查询

  1. 前端代码

index.jsp页面

1
2
3
<!-- 模拟用户的修改 -->
<h3>修改用户</h3>
<a href="/user/initUpdate">模拟用户修改</a>

模拟update.jsp页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>首页</title>
</head>
<body>
<!-- 模拟用户的修改 -->
<h3>修改用户</h3>
<form action="/user/initUpdate" method="post">
姓名:<input type="text" name="username" value="${user.username}"><br>
密码:<input type="text" name="password" value="${user.password}"><br>
金额:<input type="text" name="money" value="${user.money}"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
  1. 后端控制器代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller("userController")
@RequestMapping("/user")
public class UserController {
@RequestMapping("/initUpdate")
public String initUpdate(Model model) {
// 模拟从数据库中查询数据
User user = new User();
user.setUsername("张三");
user.setPassword("123456");
user.setMoney(123d);
model.addAttribute("user", user);
return "update";
}
}

返回值是update,即最后跳转到update页面。

2.6.1.2 void类型

如果控制器的方法返回值是void,在执行程序报404的异常,默认查找jsp页面没有找到。

1
2
3
Type Status Report
消息 /WEB-INF/pages/user/initUpdate.jsp
描述 源服务器未能找到目标资源的表示或者是不愿公开一个已经存在的资源表示。

从上面的错误信息可以看出,它会默认跳转到控制器中@RequestMapping("/example")initUpdate的页面。

SpringMVCServlet原始API可以作为控制器中方法的参数,即在controller方法形参上可以定义requestresponse,使用requestresponse指定相应结果。

  1. 使用request(即为存储转发)转向页面

    1
    2
    request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request, 
    response);

    在存储转发中不需要写虚拟目录,但这里的资源路径需要写webApp中的全路径,没有像前面只写一个success的原因是,前一种是交给了视图解析器进行管理;而这里没有用到视图解析器,是单纯由request进行实现,故需要写出资源的全路径。

  2. 使用response进行页面的重定向

    1
    response.sendRedirect(request.getContextPath() + "/page.jsp");

    需要加上虚拟目录。

  3. 通过response指定响应结果,例如响应json数据:

    1
    2
    3
    response.setCharacterEncoding("utf-8");
    response.setContentType("application/json;charset=utf-8");
    response.getWriter().write("json串");
2.6.1.3 关键字的转发或重定向

使用关键字进行转发或重定向时,Spring不会再通过视图解析器帮我们解析路径,需要我们自己手动配置。即就像上一小结中,资源路径需要写全。

  1. Forward转发

controller方法在提供了String类型的返回值之后,默认就是请求转发。当然,我们可以手动进行设置:

1
2
3
4
5
@RequestMapping("/testForward")
public String testForward() {
System.out.println("test1方法执行了...");
return "forward:/WEB-INF/pages/success.jsp";
}

如果用了forward:,则路径必须写成实际视图url,不能写逻辑视图。

它相当于它相当于request.getRequestDispatcher("**url**").forward(request,response)。使用请求转发,既可以转发到jsp(到视图解析器中),也可以转发到其他的控制器方法。

  1. Redirect重定向

controller方法提供了一个String类型返回值之后,它需要在返回值里使用:redirect:

1
2
3
4
5
6
    @RequestMapping("/testRedirect")
public String testRedirect() {
System.out.println("testRedirect方法执行了...");
// return "redirect:testForward"; // 重定向至另一个servlet地址
return "redirect:/index.jsp"; // 重定向至jsp页面
}

它相当于response.sendRedirect(url)。需要注意的是,如果是重定向到jsp页面,则jsp页面不能写在WEB-INF目录中,否则无法找到。

原因:
重定向是客户端的,而转发是服务端内部的。
重定向是让客户端去访问重定向的地址,而WEB-INF下的文件是不能通过外部访问的!

2.6.1.4 ResponseBody响应json数据

作用:该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据。如:jsonxml等,通过Response响应给客户端。

需求:使用@ResponseBody注解实现将controller方法返回对象转换为json响应给客户端。

前置:SpringMVC默认用MappingJacksonHttpMessageConverterjson数据进行转换,需要加入jackson的包。我们的项目中通过导入依赖的方法进行包的管理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 对json处理的包,即能够使用@ResponseBody -->
<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>

需要配置前端控制器不拦截静态资源。

DispatcherServlet会拦截到所有的资源,导致一个问题就是静态资源(img、css、js)也会被拦截到,从而
不能被使用。解决问题就是需要配置静态资源不进行拦截,在springmvc.xml配置文件添加如下配置:

1
2
3
4
5
6
<!-- 设置静态资源不过滤 -->
<mvc:resources location="/css/" mapping="/css/**"/>
<!-- 样式 -->
<mvc:resources location="/images/" mapping="/images/**"/>
<!-- 图片 -->
<mvc:resources location="/js/" mapping="/js/**"/> <!-- javascript -->

其中,mvc:resources标签配置不过滤谁。

  • location元素表示webapp目录下的包下的所有文件.
  • mapping元素表示以/static开头的所有请求路径,如/static/a 或者/static/a/b.

客户端发送ajax的请求,传的是json字符串,后端把json字符串封装到user对象中。

响应:将对象转换成json字符串。

  1. 前端部分代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<input type="button" value="异步请求测试" id="testJson">
<script>
$(function () {
$("#testJson").click(function () {
$.ajax({
type: "post",
url: "${pageContext.request.contextPath}/user/testResponseBody",
contentType: "application/json;charset=utf-8", // 发送信息至服务器时内容编码类型
data: '{"username":"张三","password":"123","money":123}', // 发送的json数据
dataType: "json", // 服务器返回的数据类型 -- json
success: function (data) {
alert(data)
}
});

});
})
</script>
  1. 控制器中的代码
1
2
3
4
5
@RequestMapping("/testResponseBody")
public @ResponseBody User testResponseBody(@RequestBody User user) {
System.out.println("异步请求:" + user);
return user;
}

在控制器中的代码,其中@RequestBody将前端发送的ajax请求的json格式数据封装成JavaBean@ResponseBody将后端返回值JavaBean对象,格式化成json数据类型返回给客户端,直接响应。

总结:首先通过@RequestBody获取请求体数据,将前端请求的json字符穿转换成JavaBean对象,最后使用@ResponseBody注解把JavaBean对面转换成Json字符串,直接响应。

2.6.1.5 ModelAndView对象

ModelAndView对象是Spring提供的一个对象,可以用来调整具体的JSP视图,即可以用作控制方法的返回值。该对象中有两个方法:

方法1:addObject

1
2
3
4
public ModelAndView addObject(String attributeName, Object attributeValue) {
this.getModelMap().addAttribute(attributeName, attributeValue);
return this;
}

添加模型到该对象中,通过上面的代码可以看出,和前面所讲的请求参数封装中用到的对象是同一个。即jsp页面中同样可以使用EL表达式从request域中获取值。

方法2:setViewName

1
2
3
public void setViewName(@Nullable String viewName) {
this.view = viewName;
}

用于设置逻辑视图名称,视图解析器会根据名称前往指定的视图。这也ModelAndViewModel最大的不同,它可以设置跳转的逻辑视图名称。

使用示例:

1
2
3
4
5
6
7
8
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
System.out.println("testModelAndView...");
ModelAndView mv = new ModelAndView();
mv.addObject("username", "张三");
mv.setViewName("success");
return mv;
}
  1. 可以将对象存储到request域中。mv.addObject("key",value)
  2. 可以存储跳转到哪个页面中。mv.setViewName("success") – 选择视图解析器,进行解析。

前端代码:可以直接通过EL表达式进行值的获取,${username} --> 张三

ModelAndViewModel两者的不同:

  1. Model是每次请求中都存在的默认参数,利用其addAttribute()方法即可将服务器的值传递到jsp页面中;
  2. ModelAndView包含modelview两部分,使用时需要自己实例化,利用ModelMap用来传值,也可以设置view的名称。
  3. 总而言之,就是ModelAndView除了同Model一样包含存储的键值外,它还存储着跳转页面的名称地址,直接将ModelAndView对象整体作为返回值,其对象中存储的地址自动由SpringMVC进行视图解析。

2.7 SpringMVC实现文件上传

文件上传的前提:

  • form表单的enctype取值必须是:multipart/form-data。默认值是:application/x-www-form-urlencodedenctype:是表单请求正文的类型。
  • method属性取值必须是POST
  • 提供一个文件选择域<input type="file"/>

文件上传的原理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
当form 表单的enctype取值不是默认值后,request.getParameter()将失效。 
enctype=”application/x-www-form-urlencoded”时,form 表单的正文内容是:
key=value&key=value&key=value
当form 表单的enctype取值为Mutilpart/form-data时,请求正文内容就变成:
每一部分都是 MIME 类型描述的正文
-----------------------------7de1a433602ac 分界符
Content-Disposition: form-data; name="userName" 协议头
aaa 协议的正文
-----------------------------7de1a433602ac
Content-Disposition: form-data; name="file";
filename="C:\Users\zhy\Desktop\fileupload_demofile\b.txt"
Content-Type: text/plain 协议的类型(MIME 类型)
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-----------------------------7de1a433602ac--

使用Commons-fileupload组件实现文件上传,需要导入该组件相应的支撑jar包:

  • Commons-fileupload
  • Commons-io

Commons-io不属于文件上传组件的开发jar文件,但Commons-fileupload组件从1.1版本开始,它工作时需要commons-io包的支持。

导入依赖:

1
2
3
4
5
6
7
8
9
10
11
<!-- 文件上传依赖 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>

前端页面:

1
2
3
4
<form action="/user/testUpload" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"/><br>
<input type="button" value="文件上传"/>
</form>

后端页面:

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
@RequestMapping("/testUpload")
public String testUpload(HttpServletRequest request) throws Exception {
// 先获取到要上传的文件目录 - 即需要上传的目录设置成uploads
String path = request.getSession().getServletContext().getRealPath("/uploads");
// 创建File对象,用于向该路径下上传文件
File file = new File(path);
// 判断路径是否存在,若不存在则创建该路径
if (!file.exists()) {
file.mkdirs();
}
// 创建磁盘文件项工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(factory);
// 解析request对象
List<FileItem> list = fileUpload.parseRequest(request);
// 遍历
for (FileItem fileItem : list) {
// 判断文件项是普通字段,还是上传的文件
if (fileItem.isFormField()) {
continue;
} else {
// 上传文件
// 获取到上传文件的名称
String name = fileItem.getName();
String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
// 将文件名唯一化
name = uuid + "_" + name;
// 上传文件
fileItem.write(new File(file, name));
// 删除临时文件
fileItem.delete();

}
}
return "success";
}

备注:request.getSession().getServletContext()获取servlet容器对象(最大的域对象),也可以理解为项目真正部署到tomcat时获得的tomcat(服务器)对象。因为session是服务器端对象。而request.getSession().getServletContext().getRealPath()用于获取指定目录在服务器端所真正部署的路径。

2.7.1 SpringMVC传统方式的文件上传

SpringMVC传统方式的文件上传,指的是我们上传的文件和访问的应用存在于同一台服务器上,并且上传完成以后,浏览器可能跳转。

SpringMVC框架提供了MultipartFile对象,该对象表示上传的文件,要求变量名称必须和表单file标签的name属性名称相同。

  1. 配置文件解析器
1
2
3
4
5
<!-- 配置文件解析器对象,要求id名称必须是multipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 配置文件最大上传的byte -->
<property name="maxUploadSize" value="10485760"/>
</bean>
  1. 控制器代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RequestMapping("/testSpringMVCUpload")
public String testSpringMVCUpload(HttpServletRequest request, MultipartFile upload) throws IOException { // 参数upload要和前端中的name属性相同
System.out.println("SpringMVC方式的文件上传..");
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().replaceAll("-", "").toUpperCase();
filename = uuid + "_" + filename;
// 上传文件
upload.transferTo(new File(path,filename));
return "success";
}

SpringMVC文件上传的主要步骤图:

03-原理

首先前端请求,经过前端控制器将请求交给配置文件解析器,由相应的处理器进行处理。

2.7.2 SpringMVC跨服务器方式的文件上传

在实际开发中,我们可能会处理很多不同功能的服务器。例如:

  • 应用服务器:负责部署我们的应用
  • 数据库服务器:运行我们的数据库
  • 缓存和消息服务器:负责处理大并发访问的缓存和消息
  • 文件服务器:负责存储用户上传文件的服务器

即每个服务器负责每一个功能模块,而不是由一个服务器承担全部。分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率。请求都由应用服务器进行处理转发,而需要的功能都分发到每个单独的服务器进行处理。见下图:

04

现在模拟图片上传:

  1. 导入开发需要的Jar
1
2
3
4
5
6
7
8
9
10
11
<!-- 跨服务器上传需要的依赖 -->
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.18.1</version>
</dependency>
  1. 需要在图片服务器上的web.xml添加如下配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

即:接收文件的目标服务器可以支持写入操作。

  1. 编写应用服务器端的控制器代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RequestMapping("/testSpringMVCUpload2")
public String testSpringMVCUpload2(MultipartFile upload) throws IOException {
System.out.println("SpringMVC跨服务器上传...");
// 定义图片服务器的请求路径
String path = "http://localhost:8088/picture/uploads/";
// 获取到上传文件的名称
String filename = upload.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
filename = uuid + "_" + filename;
// 向图片服务器上传文件
// 创建客户端对象
Client client = Client.create();
// 连接图片服务器地址
WebResource webResource = client.resource(path + filename);
// 上传文件
webResource.put(String.class,upload.getBytes());
return "success";
}

2.8 SpringMVC的异常处理

系统中的异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。

系统的daoservicecontroller出现都通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理器进行异常处理。SpringMVC异常处理见下图:

05

控制器中捕获异常,从下向上抛出异常。

其目的是为了在出现异常的时候,跳到另外一个“友好”页面。模拟应用步骤:

  1. 自定义一个异常类。(继承Exception)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package cn.lizhi.other;

    public class SysException extends Exception {

    // 用于定义异常信息
    private String message;

    public SysException(String message) {
    this.message = message;
    }

    public SysException() {
    }

    @Override
    public String getMessage() {
    return message;
    }

    public void setMessage(String message) {
    this.message = message;
    }
    }
  2. 编写异常处理器类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class SysExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, Exception ex) {

    // 异常信息打印
    ex.printStackTrace();
    SysException sysException = null;
    if (ex instanceof SysException) { // 如果抛出的是自定义异常则直接转换
    sysException = (SysException) ex;
    } else {
    //如果抛出的不是系统自定义异常则重新构造一个系统错误异常。
    sysException = new SysException("系统错误,请于管理员联系");
    }

    ModelAndView mv = new ModelAndView();
    mv.addObject("message", sysException.getMessage());
    mv.setViewName("error");
    return mv;
    }
    }

    即异常处理器,实现接口 HandlerExceptionResolver,其中ex参数表示接收到的异常对象。其中创建的ModelAndView用于存入异常信息,键值对;再设置跳转路径。

    这里我们可以通过判断不同的异常类型跳转到不同的页面显示。

  3. bean配置文件中配置异常处理器类。

    1
    2
    <!-- 配置自定义异常处理器 -->
    <bean id="handlerExceptionResolver" class="cn.lizhi.other.SysExceptionResolver"/>
  4. 模拟出现异常

    1
    2
    3
    4
    5
    6
    @RequestMapping("testException")
    public String testException() {
    System.out.println("模拟异常处理器的处理方式");
    int i = 3 / 0;
    return "success";
    }

    页面出现结果 –> 系统错误,请于管理员联系

2.9 SpringMVC中的拦截器

Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。
用户可以自己定义一些拦截器来实现特定的功能。

谈到拦截器,还要向大家提一个词——拦截器链(Interceptor Chain)。拦截器链就是将拦截器按一定的顺
序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。

06

说到这里,可能大家脑海中有了一个疑问,这不是我们之前学的过滤器吗?是的它和过滤器是有几分相似,但
是也有区别,接下来我们就来说说他们的区别:

  • 过滤器是 servlet 规范中的一部分,任何 java web 工程都可以使用。

  • 拦截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能用。

  • 过滤器在 url-pattern 中配置了/*之后,可以对所有要访问的资源拦截。

  • 拦截器它是只会拦截访问的控制器方法,如果访问的是 jsphtml,css,image 或者 js 是不会进行拦
    截的。

  • 它也是 AOP 思想的具体应用。

  • 我们要想自定义拦截器, 要求必须实现:HandlerInterceptor 接口。

  • HandlerInterceptor拦截的是请求地址,所以针对请求地址做一些验证、预处理等操作比较合适。

2.9.1 自定义拦截器步骤

  1. 创建类,实现HandlerInterceptor接口,需要重写需要的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

    System.out.println("拦截器执行了...前");
    return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    System.out.println("拦截器执行了...后");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    System.out.println("页面跳转完毕后...");
    }
    }
    • preHandler

      方法是在controller方法执行前,进行拦截的方法;返回结果为true表示放行,返回结果为false表示拦截(配合条件判断使用),因为提供了requestresponse参数,所以可以使用转发或者重定向直接跳转到指定的页面。

      调用:按拦截器定义顺序调用

      作用:如果决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是需要业务处理器进行处理,则返回true;如果不需要再调用其他的组件去处理请求,则返回false

    • postHandler

      后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null

      按拦截器定义逆序调用;在拦截器链内所有拦截器返回成功后进行调用。

      作用:在业务处理器处理完请求后,但是DispatcherServlet向客户端返回响应前被调用,处理ModelAndView中的值。

    • afterCompletion

      调用顺序:按拦截器定义逆序调用

      调用时机:对拦截器链内所有拦截器内返回成功的拦截器才调用它的afterCompletion方法。

      作用:在业务处理器处理完请求后,但是DispathcerServlet向客户端返回响应前被调用。可以在该方法中进行一些资源清理的操作。

      view视图渲染完毕以后执行,不能再进行页面的跳转。

    一旦在preHandler方法中返回了false(等同于出现了异常),后续的拦截器和controller方法就都不会再执行,也不会再执行postHandler方法,只会倒叙执行afterCompletion方法。

拦截器只能拦截Controller中的方法。

  1. springmvc.xml配置文件中配置拦截器类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <!-- 配置拦截器 -->
    <mvc:interceptors>
    <mvc:interceptor>
    <!-- 哪些方法进行拦截 -->
    <mvc:mapping path="/user/*"/>
    <!-- 哪些方法不进行拦截
    <mvc:exclude-mapping path=""/>
    -->
    <!-- 注册拦截器对象 -->
    <bean id="myInterceptor" class="cn.lizhi.utils.MyInterceptor"/>
    </mvc:interceptor>
    </mvc:interceptors>

    可以配置多个拦截器,拦截器执行的顺序就是配置文件中注册的拦截器对象的顺序。

2.9.2 HandlerInterceptor接口中的方法总结

  1. preHandle方法是controller方法执行前拦截的方法

    1. 可以使用request或者response跳转到指定的页面

    2. return true放行,执行下一个拦截器,如果没有拦截器,执行controller中的方法。

    3. return false不放行,不会执行controller中的方法。

  2. postHandlecontroller方法执行后执行的方法,在JSP视图执行前。

    1. 可以使用request或者response跳转到指定的页面

    2. 如果指定了跳转的页面,那么controller方法跳转的页面将不会显示。

  3. postHandle方法是在JSP执行后执行

    1. request或者response不能再跳转页面了

三、SpringMVC扩展

3.0 启动搭建

SpringMVC是Spring的web模块;所有模块的运行都是依赖核心模块(IOC模块)

  • 核心容器模块
  • web模块

SpringMVC思想是由一个前端控制器能拦截所有请求,并智能派发;这个前端控制器是一个Servlet;应该在web.xml中配置servlet来拦截所有请求。

前端控制器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- 前端控制器 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置Servlet的初始化参数,读取springMVC的配置文件,创建spring容器,用于加载配置文件
MVC配置文件加载成功,那么其中的扫描就成功,继而到控制器中类加载成对象
-->
<init-param>
<!-- ContextConfigLocation:指定SpringMVC配置文件位置 -->
<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>
<!-- 配置资源路径/表明所有的资源皆可被访问到;/*和/都是拦截所有请求,/*的范围更大;还会拦截到*.jsp请求;一旦拦截,页面就无法显示了 -->
<url-pattern>/</url-pattern>
<!-- 表明只有hello的资源路径可以被访问到(拦截到)
<url-pattern>/hello</url-pattern>
-->
</servlet-mapping>

然后在springMVC.xml中配置扫描组件。

配置视图解析器,能帮我们拼接页面地址,即进行地址映射。方式,通过前缀和后缀的方式进行映射。

1
2
3
4
5
6
7
8
9
<!-- 配置spring创建容器时要扫描的包 -->
<context:component-scan base-package="cn.lizhi"></context:component-scan>
<!-- 配置视图解析器 Ioc容器对象,由Tomcat调用-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 文件具体所在的目录 -->
<property name="prefix" value="/WEB-INF/pages/"></property>
<!-- 文件解析的类型(后缀名) -->
<property name="suffix" value=".jsp"></property>
</bean>

SpringMVC中的HelloWorld的运行流程:

  1. 运行流程:

    1. 客户端点击链接发送请求
    2. 来到tomcat服务器
    3. SpringMVC的前端控制器收到所有请求
    4. 来看请求地址和@RequestMapping标配的哪个匹配,来找到到底使用哪个类的哪个方法
    5. 前端控制器找到了目标处理器类和目标方法,直接利用返回执行目标方法;
    6. 方法执行完成以后会有一个返回值;SpringMVC认为这个返回值就是要去的页面地址
    7. 拿到方法返回值以后;用视图解析器进行拼串得到完整的页面地址;
    8. 拿到页面地址,前端控制器帮我们转发到页面;
  2. @RequestMapping:就是告诉SpringMVC,这个方法用来处理什么请求。

  3. 如果不指定配置文件位置:

    /WEB-INF/springDispatcherServlet-serlvet.xml

    如果不指定也会默认去找一个文件;/WEB-INF/xxx-serlvet.xml,其中xxx是指定的前端控制器的名称,因此也可以创建这么一个xml文件作为SpringMVC的配置文件,直接放在web下。

3.1 前端控制器路径问题

SpringMVC中关于前端控制器路径配置的问题:

在整个Tomcat服务器中,服务器大web.xml中有一个DefaultServleturl-pattern=/,我们自定义配置中的前端控制器也设置的url-pattern=/,如果访问静态资源,就会来到DispatcherServlet(前端控制器)看那个方法的RequestMapping是这个index.html

在Tomcat中的DefaultServlet是Tomcat中处理静态资源的。除过Servlet和jsp都是静态资源;我们的前端控制设置了/禁用了tomcat服务器中的DefaultServlet。

为什么jsp又能访问;因为我们没有覆盖服务器中的JspServlet的配置。

关于上面这一块可以看下Tomcat总的web.xml文件,里面配置了jsp的servlet,静态资源的servlet,动态资源servlet,它们都设定了各种路径,来映射到这些对应个的servlet上进行处理。但是,一旦我们的DispatcherServlet进行了配置,会优先交给它进行拦截请求,到Controller中进行处理,其实本质上DispatcherServlet也是servlet,只是它将所有拦截到的请求都映射到给它自己来进行请求,交给Controller进行处理。

所以/*直接就是拦截所有请求;我们写/;也是为了迎合后面的Rest风格的URL地址。

3.2 字符编码

在web.xml中配置字符过滤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 配置解决中文乱码的过滤器 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--设置过滤器中的属性值,指定POST请求乱码-->
<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>

GET的请求编码,设置在服务器端,在server.xml的8080处添加URIEncoding=”UTF-8”

字符编码Filter一定要在其它Filter之前。

1
2
3
4
5
6
7
8
9
10
11
12
13
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String encoding = this.getEncoding();
if (encoding != null) {
if (this.isForceRequestEncoding() || request.getCharacterEncoding() == null) {
request.setCharacterEncoding(encoding); // 在这里设定字符编码,也就是在其它过滤器还没有获取到请求参数前,在这里给请求参数设定编码,否则其它filter都已经获取到参数进行使用了,再进行设置就没有意义了啊
}

if (this.isForceResponseEncoding()) {
response.setCharacterEncoding(encoding);
}
}
filterChain.doFilter(request, response);
}

3.3 SpringMVC数据传递

思考:SpringMVC除过在方法行传入原生的request和session外还能怎么样在数据带给页面?

可以在方法处传入Map、或者Model或者ModelMap。给这些参数里面保存的所有数据都会放在域中。可以在页面获取。

方式1:

1
2
3
4
5
@RequestMapping("/handle01")
public String handle01(Map<String, Object> map) {
map.put("msg", "你好");
return "success";
}

这样前端,可以通过在request域中获取到这个map中存储的值。

方式2/3:采用Model和ModelMap

1
2
3
4
5
6
7
8
9
10
11
@RequestMapping("/handle02")
public String handle02(Model model) {
model.addAttribute("msg", "你好model");
return "success";
}

@RequestMapping("/handle03")
public String handle02(ModelMap model) {
model.addAttribute("msg", "你好modelMap");
return "success";
}

同方式一一样,都可以通过在前端中的request域中获取到值。

通过三种的类型打印:

1
class org.springframework.validation.support.BindingAwareModelMap

都是同一种类型。

所以,Map、Model,ModelMap,最终都是BindingAwareModelMap工作。相当于给BindingAwareModel中保存的东西都会被放在请求域中。

通过源码分析,他们之间的关系:

image-20210618162524167

方法的返回值可以变为ModelAndView类型:

1
2
3
4
5
6
7
8
@RequestMapping("/handle04")
public ModelAndView handle04() {

// 之前的返回值我们就叫视图名;视图名,是通过视图解析器 最终拼串得到页面的真实地址;
ModelAndView mv = new ModelAndView("success");
mv.addObject("msg", "您好");
return mv;
}

这种方式,同时将view和model数据进行返回;而且数据是放在请求域中。可以为页面携带数据。

SpringMVC提供了一种可以临时给Session域中保存数据的方式;使用注解:@SessionAttributes。只能使用在类上。例如:@SessionAttributes(value = {"msg"},types={String.class});给BindingAwareModelMap中保存的数据,或者ModelAndView中的数据。同时给session中放一份。value指定保存数据时要给session中方的数据的key。分别以指定value和types两种方式进行保存session域。

3.4 ModelAttribute

1
2
3
4
@RequestMapping("/handle05")
public void handle05(Account account) {
System.out.println(account);
}

在全字段更新中,首先根据Id从数据库中查询出Account对象,再根据前端的传过来的字段值进行相应的覆盖,而不是创建一个新的对象,对其赋值,否则里面空值就会覆盖原来的数据库中的值。

就是通过ModelAttribute达到这个效果。

思想核心:

  1. SpringMVC要封装请求参数的Book对象不应该是自己new出来的。而应该是从数据库中拿到的准备好的对象。
  2. 再来使用这个对象封装请求参数。

@ModelAttribute,可以标记在方法和对象上。

标记在方法上:

  • 这个方法就会提前于目标方法先运行,即提前在数据中查询对应的对象信息。
  • 将这个对象信息保存起来(方便下一个方法还能使用),保存的方式,可以通过map进行保存(保存在request域中)
  • 可以告诉SpringMVC要封装的请求参数不应该是自己new出来的,而是从数据库中查询出来的。再使用这个对象封装参数
    • 在接收请求参数的对象地方再标记上@ModelAttribute,其中的value就是前面保存在map中指定的Key,目的是做映射,这样就能在请求参数封装的对象中,取出从数据库中查询到的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RequestMapping("/handle05")
public void handle05(@ModelAttribute("account") Account account) {
System.out.println(account);
}

@ModelAttribute
public void modelAttribute(Map<String, Object> map) {
System.out.println("ModelAttribute...测试");
// 下面模拟从数据库中查询到的数据
Account account = new Account();
account.setMoney(100f);
account.setAccountId(10);
account.setAccountName("123");
Address address = new Address();
address.setCityName("beijing");
address.setProvinceName("beijing");
account.setAddress(address);
map.put("account", account);
}

会在每个controller执行之前,先执行ModelAttribute目标方法,然后绑定在对应的参数上,这个参数并不是新new出来的,而是map中保存的。

3.4.1 原理

这里ModelAttribute中存放的map和Model,都是同一个对象。

image-20210618192910976

3.5 SpringMVC源码解析

3.5.1 前端控制器架构 —— DispatcherServlet

当请求到来的时候,其执行流程:

image-20210618195025752

再进一步细看doDispatch:

ha.handle执行目标方法,即处理(控制)器的方法被调用。

processDispatchResult

总流程:

  1. 所有请求过来DispatcherServlet收到请求;
  2. 调用doDispatch()方法进行处理
  3. 检查是否是文件上传请求
    • 如果是文件上传请求,就包装新的request
  4. 通过getHandler(processedResult)根据当前请求地址找到哪个类可以进行处理,即找到处理这个请求的目标处理器,就是找到对应的Controller
    • 根据当前请求在HandlerMapping中找到这个请求的映射信息,获取到目标处理器类
  5. 如果没有找到对应的处理器,就抛出异常。
  6. 通过getHandlerAdapter(),根据当前处理器你类获取到能执行这个处理器方法的适配器。就是确定当前类下哪个方法能够处理这个请求。即拿到能执行这个类的所有方法的适配器(反射工具)。(这里是注解方式的适配器)
    • 根据当前处理器类,找到当前类的HandlerAdapter(适配器),即具体的方法。
  7. 使用适配器执行目标方法,将目标方法执行完成后的返回值作为视图名,并设置到modelAndView中。目标方法无论怎么写,最终适配器执行完成以后都会执行后的信息封装成ModelAndView
  8. 如果没有视图名设置一个默认的视图名
  9. 根据ModelAndView的信息转发到具体的页面。processDispatchResult()方法;转发到目标页面,根据方法最终执行完成后封装到ModelAndView;转发到对应页面,而且ModelAndView中的数据可以从请求域中获取。

3.5.2 getHandler()细节

怎么根据当前请求就能找到哪个类来处理。进一步进行验证,进入该方法进行一探究竟。

该方法会返回目标处理器类的执行链;

HandlerMapping:处理器映射:他里面保存了每一个处理器能处理哪些请求的映射信息。

  • DefaultAnnotationHandlerMapping:下面的handlerMap保存了每个请求能够通过哪个controller进行处理。通过遍历,找到请求对应的处理器。(在IOC容器一启动,就创建controller对象,再进行扫描,每个处理器都能进行处理什么请求,并保存到handlerMap属性中;下一次请求过来,就来看哪个HandlerMapping中有这个请求)

3.5.3 getHandlerAdapter

根据前面得到的处理器,找到其对应请求的适配器。

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
```

存在三种方式的适配器,这里是采用注解版本的适配器。

寻求目标处理器类的适配器目的:要拿适配器才去执行目标方法。AnnotationMethodHandlerAdapter:能解析注解方法的适配器。处理器类中只要有标了注解的这些方法就可以使用。

#### 3.5.4 DispatcherServlet的属性

DispatcherServlet中的几个引用类型的属性,就是SpringMVC的九大组件。SpringMVC在工作的时候,就是这九大组件完成的

- 文件上传解析器
- 区域信息解析器 和国际化相关
- 主题解析器;强大的主题效果更换
- Handler映射信息
- Handler的适配器
- SpringMVC强大的异常解析功能;异常解析器
- viewNameTranslator
- FlashMap+Manager:SpringMVC中运行重定向携带数据的功能(重定向时,也能携带数据的作用)
- 视图解析器 -

共同点:都是接口,提供规范;提供了规范,进行扩展。

IOC容器启动,就会触发initStrategies方法。(onFresh()下的初始化方法)

以HandlerMapping的初始化为例:

```java

组件的初始化:去容器中找这个组件,如果没有找到就用默认的配置;(有些组件在容器是使用类型找的,有些组件是使用id找的)

3.5.5 ha.handle(…)深化

难点是方法执行。该方法是如何通过反射确定程序传入的参数,进行的方法执行?

以下列该方法为例:

1
2
3
4
5
6
7
8
9
10
11
@RequestMapping("/handle05")
@ResponseBody
public Account handle05(@ModelAttribute("account") Account account,
@RequestParam(value = "Tom") String name,
Map<String,Object> map,
HttpServletRequest request) {
System.out.println("方法执行....");
System.out.println(account);
System.out.println(name);
return account;
}

handler执行,是如何通过反射能够确定上面的参数,并执行该方法的?

该方法内部,最终是通过,invokHandlerMethod(request,response,handler)执行的目标方法。其大致流程:

  • 拿到方法的解析器

  • 方法解析器根据当前请求地址找到真正的目标方法

  • 创建一个方法执行器

  • 包装原生的request,response

  • 创建一个BindingAwareModelMap,创建一个隐含模型

  • 执行目标方法 —— Object result = methodInvoker.invokeHandlerMethod(...);真正执行目标方法;目标方法利用反射执行期间确定参数值,提前执行modelAttribute等所有的操作都是在方法中;

    • 找到所有@ModelAttribute注解标注的方法

    • args;确定modelAttribute方法执行时要使用的每一个参数的值,当前返回值的这个方法。

      • 创建了一个和参数个数一样长度数组,用于保存

      • 找到目标方法这个参数的所有注解,如果有注解就解析并保存注解的信息;

      • 如果没有找到注解

        • resolveCommonArgument,解析普通参数值
          • 继而进入,resolveStandarArgument(解析标准参数),即用来确定当前参数,是否是原生API。
        • 查看该参数是否是未解析参数
        • 查看该参数是否是默认值
        • 获取到参数类型,判断该参数类型是否Model旗下,或者参看是否是Map类型旗下。如果是的话,就将隐含模型将其赋值给args[i]参数。
    • 将目标方法参数通过暴力反射,做成可访问的。

    • attributeMethodToInvoke执行该目标方法。

    • 如果方法上标注的ModelAttribute注解如果有value值,就是attrName的值。如果没设置,其值就会变为返回值类型首字母小写,比如void,或者account等。@ModelAttribute标在方法上的另一个作用;可以把方法运行后的返回值按照方法上@ModelAttribute("value")指定key放到隐含模型中,如果没有设置,就用返回值首字母小写。

    • 把提前运行的ModelAttribute方法的返回值也放在隐含模型中。

  • @ModelAttribute方法执行完以后,再次解析目标方法参数是哪些值。

    • 如果参数标注了注解,保存是哪个注解的详细信息,

      • 如果参数有ModelAttribute注解,拿到ModelAttribute注解的值让attrName保存,其保存的是注解的value值,否则是返回值小写。
    • 如果没标注解

      • 先看是否普通参数(是否是原生api)
      • 如果不是,再看是否Model或者Map,如果是就传入隐含模型。如果操作map,使用的隐含模型在前面的@ModelAttribute
    • 如果自定参数没有注解

      • 先看是否是原生API
      • 不是,就再看是否model或者map
      • 再看是否是其他类型的,比如:SessioinStatus、Http
      • 如果不是,就判断是否是简单类型的属性,比如:Integer,String,基本类型等
        • 如果是,paramName=””
      • 否者,就给attrName=””

      如果是自定类型对象,最终产生两个效果;

      • 如果这个参数标注了ModelAttribute注解就给attrName赋值为这个注解的value值
      • 如果这个参数没有标注ModelAttribute注解就给attrName赋值””;
    • 确定自定义类型参数的值;还要将请求中的每一个参数赋值给这个对象。

      • WebDataBinder对象,即resolveModelAttribute(…)方法对数据进行绑定。

        • 如果attrName是空串;就将参数类型的首字母小写作为值

        • 确定对象目标值(SpringMVC确定POJO值的三步)

          • 如果隐含模型中有这个key(标了ModelAttribute注解就是注解指定的value,没标就是参数类型的首字母小写)指定的值;如果有讲这个值赋值给bindObject

          • 如果没有,再判断是否是SessionAttributes标注的属性,就从session中拿

          • 如果都不是,就利用反射创建对象。

  • 最后在return处,执行目标方法

总结:

  1. 运行流程简单版

  2. 确定方法每个参数的值

    1. 标了注解:保存注解的信息;最终得到这个注解应该对应解析的值;

    2. 没标注解

      1. 看是否原生API

      2. 看是否Model或者是Map,xxx

      3. 都不是,看是否是简单类型;paramName=””

      4. 给attrName赋值;attrName(参数标了@ModelAttribute("")就是指定的,没标就是””)

        确定自定义类型参数:

        1. attrName使用参数的类型首字母小写;或者使用之前@ModelAttribute("")的值
        2. 先看隐含模型中有这个attrName作为key对应的值;如果有就从隐含模型中获取并赋值
        3. 看是否@SessionAttributes(value="xxx");标注的属性,如果是从session中拿;
          • 如果存在该注解,但是拿不到,就会报出异常
        4. 否者,通过反射创建类型参数对应的对象
      5. 拿到之前创建好的对象,使用数据绑定器(WebDataBinder)将请求中的每个数据绑定到对象。

  3. @ModelAttribute标注的方法会提前运行并把方法的运行结果放在隐含模型中;

    • 放的时候会使用一个key;
      • 如果@ModelAttribute("value")指定了,就用指定的value;
      • 如果没有指定,就用返回值类型的首字母小写作为Key;
  4. @SessionAttributes(value="xxx")最好不要使用

    • 如果用的话,隐含模型中存在需要的值,这样就不会跳转到这一步
    • 如果隐含模型中不存在,那就需要session域中存在需要的值,否者会报错。

3.5.6 视图和视图解析器

上一小节中,能够让模型进行了数据处理,那该如何对视图进行渲染呢。

视图解析器中的拼串,是从当前web项目下的,/WEB-INF/..进行拼接,资源请求。

如果采用转发:

1
2
3
4
@RequestMapping("/handle06")
public String handle06() {
return "forward:/index.jsp";
}

采用转发,是从当前项目开始资源请求(\web项目的root目录下,不加就是相对路径),而不是/WEB-INF/..

采用这种方式,可以将请求转发给另外一个controller进行请求处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RequestMapping("/handle05")
@ResponseBody
public Account handle05(@ModelAttribute("account") Account account,
@RequestParam(name= "name",defaultValue = "tom") String name,
Map<String,Object> map,
HttpServletRequest request) {
System.out.println("方法执行....");
System.out.println(account);
System.out.println(name);
return account;
}

@RequestMapping("/handle06")
public String handle06() {
return "forward:/handle05"; // 派发给handle05进行请求处理
}

foward:前缀的转发,不会由我们配置的视图解析器进行解析。

如果采用重定向:

redirect:重定向的路径

原生的Servlet重定向需要加上项目名才能成功。

1
response.sendRedirect("demoName/hello.jsp")

而使用SpringMVC会为路径自动的拼接上项目名(写法同转发)。

总结:有前缀的转发和重定向操作,配置的视图解析器就不会进行拼串。

3.5.7 视图解析源码解析

  1. 方法执行后的返回值会作为页面地址参考,转发或重定向到页面
  2. 视图解析器会进行页面地址的拼串

任何方法的返回值,都会包装成ModelAndView

核心方法processDispatchResult(...)进行页面的渲染,就是将域中的互数据在页面进行展示。下面对该方法的分析:

调用render(…)进行渲染页面。

得到View与ViewResolver;

  • ViewResovler的作用是根据视图名(方法的返回值)得到View对象,具体体现在它的resolverViewName(...)方法上

再进行探究,如何通过这个方法的返回值(视图名)得到View对象:

  • 首先遍历所有的viewResolvers,调用resolverViewName(...)视图解析器根据方法的返回值,得到一个View对象。即:所有配置的视图解析器都来尝试根据视图名(返回值)得到View(视图)对象;如果能得到就返回,得不到就换下一个视图解析器。
  • resolverViewName(...)的具体实现createView(...)
    • 是否重定向
    • 如果是转发
    • 如果没有前缀,就使用父类默认创建一个View
    • 返回View对象
  • 调用View对象的render方法;
  • 最终方法落地在renderMergeOutputModel(...)方法。
    • 期间方法中exposeModelAsRequestAttributes(...)方法将隐藏模型中的数据设置到request中的请求域中。

结论:视图解析器只是为了得到视图对象;视图对象才能真正的转发(将模型数据全部放在请求域中)或者重定向到页面。视图对象才真正渲染视图。

1
<mvc:view-controller path="/handle" view-name="login"/>

配置在mvc下的该标签,进行view时,经过mvc处理(不重要)。但是,只有当前映射的好用,其它的不好使。所以看下面这个高级功能:

1
<mvc:annotation-driver/>

3.5.8 自定义视图和视图解析器

自定义视图解析器工作的整体流程:

  1. 让我们的视图解析器工作
  2. 得到我们的视图对象
  3. 我们的视图对象自定义渲染逻辑 - render负责渲染
1
response.getWriter().write("..") //
  1. 自定义视图和视图解析器的步骤

    • 编写自定义的视图及解析器

    • 视图解析器必须放在IOC容器中

    • 为了让自定义的视图解析器先执行,还需要实现Order接口(默认的视图解析器优先级最高)

实现代码:

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
public class MyViewResolver implements ViewResolver, Ordered {

private int order = 0;
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (viewName.startsWith("chemlez:")) {
return new MyView();
}
return null;
}

@Override
public int getOrder() {
return order;
}

public void setOrder(int order) {
this.order = order;
}
}

/**
* 自定义视图
*/
public class MyView implements View {
@Override
public String getContentType() {
return "text/html"; // 返回资源响应类型
}

@Override
public void render(Map<String, ?> map, HttpServletRequest request, HttpServletResponse response) throws Exception {
request.setCharacterEncoding("utf-8");
System.out.println(map);
response.setContentType("text/html");
response.getWriter().println("自定义视图解析生效了...");
}
}

视图解析器装配:

1
2
3
<bean id="myResolver" class="cn.lizhi.view.MyViewResolver">
<property name="order" value="0"/>
</bean>

3.5.9 数据转换

SpringMVC封装自定义类型对象的时候,是如何封装并绑定请求参数的?

JavaBean要和页面提交的数据进行一一绑定的过程。

牵扯到以下操作:

  1. 数据绑定期间的数据类型转换(例如:前端传递过来的Key=value都是字符串类型,就需要对其进行类型转换)
  2. 数据绑定期间的数据格式化问题(例如:日期格式转化)
  3. 数据校验,即我们提交的数据必须是合法的。

核心方法:

1
bindRequestParameters(...) // 请求参数解析与绑定

WebDataBinder:数据绑定器负责数据类型转化和数据校验

ConversionService组件:负责数据类型的转换以及格式化功能

Validators:负责数据校验工作

bindingResult:负责保存以及解析数据绑定期间数据校验产生的错误

自定义类型转换器:

  1. 不同类型的转换和格式化用它自己的converterConversionService存在多个converter

  2. ConversionService是一个接口,它里面有converter进行工作;

  3. 步骤:

    • 实现Converter接口,写一个自定义的类型转换器;

    • 两个泛型:

      • S:Source:原数据类型
      • T:Target:需要转换的数据类型
    • 在convert方法中写转换的逻辑

    • Converter是ConversionService中的组件

      • 将自己编写的converter放进ConversionService中
      • 将WebDataBinder中的ConversionService设置成我们这个加了自定义类型转换器的ConversionService
    • 配置出ConversionService,配置其对应的ConversionServiiceFactory的Bean,通过set注入配置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      <!-- 告诉SpringMVC别用默认的ConversionService,用我们自定义的ConversionService、这里包含我们自定义的Converter -->
      <!-- 配置spring开启注解mvc的支持 -->
      <mvc:annotation-driven conversion-service="converterService" /> // 1
      <bean id="converterService" class="org.springframework.context.support.ConversionServiceFactoryBean">
      <!-- 给工厂注入一个新的类型转换器,并使用我们自己配置的类型转换组件 -->
      <property name="converters">
      <array>
      <!-- 配置自定义类型转换器 -->
      <bean class="cn.lizhi.utils.StringToDate"/>
      </array>
      </property>
      </bean>

3.5.10 <mvc:annotation-driven/>标签

1
2
<!-- 静态资源能访问,动态映射的请求就不行 自己映射的请求就进行处理,不能处理的直接交给tomcat -->
<mvc:default-servlet-handler/> // 2

通过:

只有1时,DefaultAnnotationHandlerMapping中的handlerMap中保存了每一个资源的映射信息;

静态资源不能访问:就是handlerMap中没有保存静态资源映射的请求

  • handlerAdapter:方法执行的适配器;

只有2时,动态映射HandlerMapping对应的映射没有了DefaultAnnotationHandlerMapping没有了;使用SimpleUrlHandlerMapping替换了,他的作用就是将所有请求交给tomcat,而tomcat中,只配置了dispatcherServlet,没有其他映射的servlet,所以动态请求无法处理。

当1,2都添加时,会有RequestMappingHandlerMapping:动态资源可以访问;handlerMethods属性保存了每一个请求用哪个方法来处理。

  • HandlerAdapters:存在RequestMappingHandlerAdapter,原来的AnnotationMethodHandlerAdapter被换成RequestMappingHandlerAdapter。

四、SSM框架的整合

核心思想:通过Spring整合另外两个框架。

整合方式:配置文件加注解的方式。

整合的思路:

  • 搭建整合的环境
  • Spring的配置搭建完成
  • Spring整合SpringMVC框架
  • Spring整合Mybatis框架

4.1 环境搭建

  1. 数据库创建
1
2
3
4
5
6
7
create database ssm;
use ssm;
create table account(
id INTEGER PRIMARY key auto_increment,
name VARCHAR(32),
money DOUBLE(7,2)
);
  1. pom.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
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
<?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>cn.lizhi</groupId>
<artifactId>SpringMVC_03</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>

<name>SpringMVC_03 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.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.0.2.RELEASE</spring.version>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<mysql.version>5.1.6</mysql.version>
<mybatis.version>3.4.5</mybatis.version>
</properties>

<dependencies>
<!-- spring -->
<!-- 切入点表达式 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<!-- springAOP AOP核心功能,例如代理工厂等 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- springIOC -->
<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>
<!-- spring整合junit -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- 事务控制 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- SpringJDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- log start -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
</dependencies>

<build>
<finalName>SpringMVC_03</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>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
  1. 实体类编写
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
public class Account implements Serializable {

private Integer id;
private String name;
private Double money;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Double getMoney() {
return money;
}

public void setMoney(Double money) {
this.money = money;
}

@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
  1. dao接口编写

由于我们是使用Mybatis框架,所以不需要编写其实现类,只需要写接口即可。

1
2
3
4
5
6
public interface AccountDao {

public void saveAccount(Account account);

public List<Account> findAll();
}
  1. 编写Service接口和实现类
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
public interface AccountService {

public void saveAccount(Account account);

public List<Account> findAll();
}

public class AccountServiceImpl implements AccountService {

private AccountDao accountDao;

@Override
public void saveAccount(Account account) {
System.out.println("对用户进行保存...");
accountDao.saveAccount(account);

}

@Override
public List<Account> findAll() {
System.out.println("业务层:查询所有用户");
List<Account> accounts = accountDao.findAll();
return accounts;
}
}

4.2 Spring框架代码的编写

4.2.1 搭建和测试Spring的开发环境

  1. 在项目中创建applicationContext.xml的配置文件,编写具体的配置信息。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <?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:tx="http://www.springframework.org/schema/tx"
    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 http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 开启注解扫描,要扫描的是service层和dao层的注解,要忽略web层注解,因为web层(Controller层)让SpringMVC框架去管理 -->
    <context:component-scan base-package="cn.lizhi">
    <!-- 配置要忽略的注解 -->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    </beans>
  2. 编写测试方法,进行测试

    1
    2
    3
    4
    5
    6
    7
    @Test
    public void testSpring() {
    // 获取Spring容器
    ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    AccountService accountService = ac.getBean("accountService", AccountService.class);
    accountService.findAll();
    }

4.3 Spring整合SpringMVC框架

  1. web.xml中配置DispatcherServlet前端控制器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <!-- 配置前端控制器:服务器启动必须加载,需要加载springmvc.xml配置文件 -->
    <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <!-- 配置初始化参数,创建完DispatcherServlet对象,加载springmvc.xml配置文件 -->
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springmvc.xml</param-value>
    </init-param> <!-- 服务器启动的时候,让DispatcherServlet对象创建 -->
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>
  2. web.xml中配置DispatcherServlet过滤器解决中文乱码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!-- 配置解决中文乱码的过滤器 -->
    <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>
  3. 创建SpringMVC.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"?>
    <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">

    <!-- 扫描Controller(web层)的注解,别的不扫描 -->
    <context:component-scan base-package="cn.lizhi">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    <!-- 配置视图解析器 -->
    <bean id="viewResolver"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- JSP文件所在的目录 -->
    <property name="prefix" value="/WEB-INF/pages/"/> <!-- 文件的后缀名 -->
    <property name="suffix" value=".jsp"/>
    </bean>
    <!-- 设置静态资源不过滤 -->
    <mvc:resources location="/css/" mapping="/css/**"/>
    <mvc:resources location="/images/" mapping="/images/**"/>
    <mvc:resources location="/js/" mapping="/js/**"/> <!-- 开启对SpringMVC注解的支持 -->
    <mvc:annotation-driven/>
    </beans>
  4. 测试SpringMVC的框架搭建是否成功

    1. 编写index.jsplist.jsp前端页面

      1
      2
      3
      4
      <!-- index页面 -->
      <a href="/account/findAll" >查询所有</a>
      <!-- list页面 -->
      <h3>查询所有</h3>
    2. 编写控制器方法

      1
      2
      3
      4
      5
      6
      7
      8
      9
      @Controller("accountController")
      @RequestMapping("/account")
      public class AccountController {
      @RequestMapping("/findAll")
      public String findAll() {
      System.out.println("表面层:查询所有用户...");
      return "list";
      }
      }

      结果:查询所有

  5. Spring整合SpringMVC框架

    1. 目的:在Controller层中能成功调用service对象中的方法。

    2. 如果想在服务器启动的时候,获取到Spring的容器,那么就需要在项目启动的时候就去加载applicationContext.xml的配置文件。在web.xml中配置ContextLoaderListener监听器(该监听器只能加载WEB-INF目录下的applicationContext.xml的配置文件)。

      监听器的作用:监听器的作用是监听一些事件的发生从而进行一些操作,比如监听ServletContext,HttpSession的创建,销毁,从而执行一些初始化加载配置文件的操作,当Web容器启动后,Spring的监听器会启动监听,监听是否创建ServletContext的对象,如果发生了创建ServletContext对象这个事件(当web容器启动后一定会生成一个ServletContext对象,所以监听事件一定会发生),ContextLoaderListener类会实例化并且执行初始化方法,将Spring的配置文件中配置的bean注册到Spring容器中,监听的操作是读取WEB-INF/applicationContext.xml,但是我们可以在web.xml中配置多个需要读取的配置文件,如下方所示,读取完成后所有的配置文件中的bean都会注册到spring容器中。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
      /WEB-INF/config/application-context.xml
      /WEB-INF/config/cache-context.xml
      /WEB-INF/config/captcha-context.xml
      /WEB-INF/config/jeecms/jeecore-context.xml
      /WEB-INF/config/jeecms/jeecms-context.xml
      /WEB-INF/config/shiro-context.xml
      /WEB-INF/config/plug/**/*-context.xml
      /WEB-INF/config/quartz-task.xml
      /WEB-INF/config/zxw/zxw-context.xml
      </param-value>
      </context-param>

      web.xml中对监听器的配置:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      <!-- 配置Spring的监听器 -->
      <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      <!-- 配置加载类路径的配置文件 -->
      <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:applicationContext.xml</param-value>
      </context-param>
    3. Controller中注入service对象,调用service对象的方法进行测试

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      @Controller("accountController")
      @RequestMapping("/account")
      public class AccountController {

      @Autowired
      private AccountService accountService;

      @RequestMapping("/findAll")
      public String findAll() {
      List<Account> accounts = accountService.findAll();
      System.out.println("表现层:查询所有用户...");
      return "list";
      }
      }

      输出结果:

      业务层:查询所有用户
      表现层:查询所有用户…

4.4 Spring整合Mybatis框架

4.4.1 搭建和测试MyBatis的环境

  1. web项目中编写SqlMapConfig.xml的配置文件,编写核心配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <?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>
    <environments default="mysql">
    <environment id="mysql">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
    <property name="driver" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql:///ssm?useUnicode=true&amp;characterEncoding=utf8"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
    </dataSource>
    </environment>
    </environments>
    <!-- 使用的是注解 -->
    <mappers>
    <!-- <mapper class="cn.itcast.dao.AccountDao"/> -->
    <!-- 该包下所有的dao接口都可以使用 -->
    <package name="cn.itcast.dao"/>
    </mappers>
    </configuration>
  2. AccountDao接口的方法上添加注解,编写SQL语句

    1
    2
    3
    4
    5
    6
    7
    8
    public interface AccountDao {

    @Insert("insert into account (name,money) values(#{name},#{money})")
    public void saveAccount(Account account);

    @Select("select * from account")
    public List<Account> findAll();
    }
  3. 编写测试方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @Test
    public void saveAccount() throws IOException {
    Account account = new Account();
    account.setName("小黑");
    account.setMoney(234d);

    // 加载配置文件
    InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
    // 创建工厂
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(is);
    // 获取sqlSession对象
    SqlSession session = factory.openSession();
    // 常见代理对象
    AccountDao accountDao = session.getMapper(AccountDao.class);
    accountDao.saveAccount(account);
    // 提交事务
    session.commit();
    // 释放资源
    session.close();
    is.close();
    }

4.4.2 Spring整合MyBatis框架

目的:把SqlMapConfig.xml配置文件中的内容配置到applicationContext.xml配置文件中。由Spring为我们进行对象的管理。从上面的测试文件中,可以看出我们需要将工厂对象,session对象代理对象交由Spring容器进行管理。即:把Mybatis配置文件(SqlMapConfig.xml)中内容配置到spring配置文件中。

注意:

  • 当我们使用的是代理dao的模式,dao具体实现类由Mybatis使用代理方式创建,此时Mybatis配置文件不能删除。
  • 整合SpringMybatis时,Mybatis创建的Mapper.xml文件名必须和dao接口文件名一致。
  1. 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 配置C3P0的连接池对象 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///ssm?useUnicode=true&amp;characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 配置SqlSession的工厂 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置扫描dao的包 -->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.itcast.dao"/>
</bean>

可以删除SqlSessionMap配置文件。

  1. dao接口加上注解@Repository
  2. service中注入dao对象,进行测试。
  3. 配置Spring框架声明式事务管理
  • 配置事务管理器

    1
    2
    3
    4
    <!-- 配置Spring的声明式事务管理 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
    </bean>
  • 配置事务通知

    1
    2
    3
    4
    5
    6
    7
    <!-- 配置事务的通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
    <tx:method name="*" propagation="REQUIRED" read-only="false"/>
    <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
    </tx:attributes>
    </tx:advice>
  • 配置AOP增强

    1
    2
    3
    4
    5
    6
    7
    <!-- 配置 aop -->
    <aop:config>
    <!-- 配置切入点表达式 -->
    <aop:pointcut expression="execution(* cn.lizhi.service.impl.*.*(..))" id="pt1"/>
    <!-- 建立通知和切入点表达式的关系 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
    </aop:config>

五、附常用配置文件

5.1 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
58
59
60
61
62
63
64
65
66
67
<!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.xml配置文件 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置初始化参数,创建完DispatcherServlet对象,加载springmvc.xml配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 服务器启动的时候,让DispatcherServlet对象创建 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<!-- 配置解决中文乱码的过滤器 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- 配置Spring的监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置加载类路径的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>

<!-- 4、使用Rest风格的URI,将页面普通的post请求转为指定的delete或者put请求 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>HttpPutFormContentFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpPutFormContentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

</web-app>

5.2 pom.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
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
<?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>cn.lizhi</groupId>
<artifactId>SpringMVC_03</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>

<name>SpringMVC_03 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.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.0.2.RELEASE</spring.version>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<mysql.version>5.1.6</mysql.version>
<mybatis.version>3.4.5</mybatis.version>
</properties>

<dependencies>
<!-- spring -->
<!-- 切入点表达式 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<!-- springAOP AOP核心功能,例如代理工厂等 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- springIOC -->
<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>
<!-- spring整合junit -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- 事务控制 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- SpringJDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- log start -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>

<!--引入pageHelper分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.0.0</version>
</dependency>

<!-- MBG -->
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>

<!--JSR303数据校验支持;tomcat7及以上的服务器,
tomcat7以下的服务器:el表达式。额外给服务器的lib包中替换新的标准的el
-->
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>

<!-- 对json处理的包,即能够使用@ResponseBody -->
<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>


</dependencies>

<build>
<finalName>ssm_crud</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>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

5.3 SpringMVC配置文件

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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

<!--SpringMVC的配置文件,包含网站跳转逻辑的控制,配置 -->
<context:component-scan base-package="cn.lizhi" use-default-filters="false">
<!--只扫描控制器。 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

<!--配置视图解析器,方便页面返回 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>

<!--两个标准配置 -->
<!-- 将springmvc不能处理的请求交给tomcat -->
<mvc:default-servlet-handler/>
<!-- 能支持springmvc更高级的一些功能,JSR303校验,快捷的ajax...映射动态请求 -->
<mvc:annotation-driven/>

</beans>

5.4 Spring配置文件 – 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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">

<!-- 注解扫描,不扫描controller层,controller层交给SpringMVC进行管理 -->
<context:component-scan base-package="cn.lizhi">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

<!-- Spring的配置文件,这里主要配置和业务逻辑有关的 -->
<!--=================== 数据源,事务控制,xxx ================-->
<!-- 引入数据源配置文件 -->
<context:property-placeholder location="classpath:dbconfig.properties" />
<!-- 数据源配置 -->
<bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>

<!--================== 配置和MyBatis的整合=============== -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定mybatis全局配置文件的位置 -->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="dataSource" ref="pooledDataSource"></property>
<!-- 指定mybatis,mapper文件的位置 -->
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
</bean>

<!-- 配置扫描器,将mybatis接口的实现加入到ioc容器中 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--扫描所有dao接口的实现,加入到ioc容器中 -->
<property name="basePackage" value="cn.lizhi.dao"></property>
</bean>

<!-- 配置一个可以执行批量的sqlSession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
<constructor-arg name="executorType" value="BATCH"></constructor-arg>
</bean>
<!--============================================= -->

<!-- ===============事务控制的配置 ================-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--控制住数据源 -->
<property name="dataSource" ref="pooledDataSource"></property>
</bean>
<!--开启基于注解的事务,使用xml配置形式的事务(必要主要的都是使用配置式) -->
<aop:config>
<!-- 切入点表达式 -->
<aop:pointcut expression="execution(* cn.lizhi.service..*(..))" id="txPoint"/>
<!-- 配置事务增强 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
</aop:config>

<!--配置事务增强,事务如何切入 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 所有方法都是事务方法 -->
<tx:method name="*"/>
<!--以get开始的所有方法 -->
<tx:method name="get*" read-only="true"/>
</tx:attributes>
</tx:advice>

<!-- Spring配置文件的核心点(数据源、与mybatis的整合,事务控制) -->
</beans>

5.5 Mybatis全局配置文件 – mybatis-config.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"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

<typeAliases>
<package name="cn.lizhi.bean"/>
</typeAliases>

<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--分页参数合理化 -->
<property name="reasonable" value="true"/>
</plugin>
</plugins>
</configuration>

5.6 Mybatis逆向工程的配置文件 – mbg.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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<!-- 关闭注释 -->
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>

<!-- 配置数据库连接 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://url:3306/ssm?useUnicode=true&amp;characterEncoding=utf8"
userId="username"
password="password">
</jdbcConnection>

<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>

<!-- 指定javaBean生成的位置 targetPackage为包的路径,targetProject为项目路径,即两个能够连接在一起 -->
<javaModelGenerator targetPackage="cn.lizhi.bean" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>

<!-- 指定sql映射文件生成的位置 -->
<sqlMapGenerator targetPackage="mapper" targetProject="./src/main/resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>

<!-- 指定dao接口生成的位置,mapper接口 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="cn.lizhi.dao" targetProject="./src/main/java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>

<!-- table指定每个表的生成策略 -->
<table tableName="tbl_emp" domainObjectName="Employee"></table>
<table tableName="tbl_dept" domainObjectName="Department"></table>

</context>
</generatorConfiguration>

5.7 日志配置文件 – log4j.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=info, CONSOLE, LOGFILE

# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

5.8 数据库连接池 – dbconfig.properties

1
2
3
4
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://url:3306/ssm?useUnicode=true&characterEncoding=utf8
jdbc.user=username
jdbc.password=password

Comment