# Maven + Spring + SpringMVC + Mybatis 整合
通过这篇文章你可以获取的内容:
1. 整个配置流程。
1. 对于每一个配置项,Spring 到底帮我们做了什么?
本文假设读者:
1. 未使用 IDEA 进行过 Web 项目开发
1. 未使用过 Maven 进行项目构建
1. 已学习过 Spring、SpringMVC、Mybatis
环境搭建:
- IDE: `IntelliJ IDEA 2017.1.1(Ultimate)`
- Web Server: `Tomcat 8.0.43`
- Spring: `4.3.7.RELEASE`
- Mybatis: `3.4.4`
如果需要示例代码,可以参考 [SSM-Demo](https://github.com/c-rainstorm/ssm-Demo).
---
- [Maven + Spring + SpringMVC + Mybatis 整合](#maven--spring--springmvc--mybatis-整合)
- [Spring && SpringMVC](#spring--springmvc)
- [工程搭建](#工程搭建)
- [新建 Web Application](#新建-web-application)
- [添加 Maven 支持](#添加-maven-支持)
- [工程目录简介](#工程目录简介)
- [代码实现](#代码实现)
- [View 层](#view-层)
- [Controller](#controller)
- [Service](#service)
- [配置文件编写](#配置文件编写)
- [`Spring.xml`](#springxml)
- [`Spring-mvc.xml`](#spring-mvcxml)
- [`web.xml`](#webxml)
- [运行测试](#运行测试)
- [Spring && SpringMVC && Mybatis](#spring--springmvc--mybatis)
- [代码实现](#代码实现-1)
- [Dao](#dao)
- [service](#service)
- [配置文件编写](#配置文件编写-1)
- [`UserDao.xml`](#userdaoxml)
- [`Spring.xml`](#springxml-1)
- [pom.xml](#pomxml)
- [运行测试](#运行测试-1)
- [参考](#参考)
---
## Spring && SpringMVC
### 工程搭建
#### 新建 Web Application
1. File -> new -> project..
1. Java -> Web Application
- 
#### 添加 Maven 支持
1. 右键工程名,点击 Add Framwork Support..
- 
1. 配置编译器源版本及目标字节码版本
- 在 `pom.xml` 配置文件中添加以下配置。
- **Tips:不添加的话目标字节码版本默认使用 `JDK1.5`**
```xml
1.8
1.8
```
#### 工程目录简介
```
.
|-- pom.xml // Maven 配置文件
|-- src
| |-- main
| | |-- java // Java 代码存放位置
| | `-- resources // 配置文件存放位置
| `-- test
| `-- java // 测试类存放位置
`-- web // 存放 View 层用到的文件,会直接部署到服务器
|-- index.jsp
`-- WEB-INF
`-- web.xml // Web 应用程序的 Web 组件的配置和部署信息
```
### 代码实现
我们以一个最简单的用户登陆操作来说明配置的过程。
#### View 层
1. form 表单
```html
```
1. 使用 JS 在传输之前进行加密
```js
$(document).ready(function () {
// 以为网络传输时使用明文,所以直接将密码提交到后台的方式很不安全,所以在传输之前就进行加密。
// 在这里加密方法用的是 MD5 。你也可以选择其他的加密方式,加密用的 JS 库应该很容易着,
// 这里我用的是 [JavaScript-MD5](https://github.com/blueimp/JavaScript-MD5)。
$("form").on("submit", function () {
var pass = $(".password");
pass.val(md5(pass.val()));
})
})
```
#### Controller
1. 因为 Controller 需要用到 Spring 的注解,所以需要先添加 Maven 依赖,即将下面的配置写入 pom.xml 中,并 Import Change。
```xml
4.3.7.RELEASE
org.springframework
spring-core
${org.springframework.version}
org.springframework
spring-beans
${org.springframework.version}
org.springframework
spring-orm
${org.springframework.version}
org.springframework
spring-context
${org.springframework.version}
org.springframework
spring-web
${org.springframework.version}
org.springframework
spring-webmvc
${org.springframework.version}
org.springframework
spring-aop
${org.springframework.version}
org.apache.logging.log4j
log4j-api
2.8.2
org.apache.logging.log4j
log4j-core
2.8.2
```
1. Controller
```java
package com.github.crainstorm.oss.user.action;
@Controller
@RequestMapping("/user")
public class UserAction {
private static Logger logger = LogManager.getLogger(UserAction.class);
@Autowired
private UserService service;
@RequestMapping("/checkLogin")
public String checkLogin(User user) {
logger.trace("enter checkLogin...");
if (logger.isDebugEnabled()) {
logger.debug(user);
}
if (service.loginSuccess(user)) {
logger.info("login success...");
return "redirect:/index.jsp";
} else {
logger.info("login failed...");
return "login/user";
}
}
}
```
1. 用到了 `log4j2`, 简单配置一下
```xml
```
#### Service
```java
package com.github.crainstorm.oss.user.service;
public interface UserService {
boolean loginSuccess(User user);
}
```
```java
package com.github.crainstorm.oss.user.service;
@Service
public class UserServiceImpl implements UserService {
private static final Logger LOGGER = LogManager.getLogger(UserServiceImpl.class);
// 因为先配置 Spring + SpringMVC, 所以 dao 层先不管
public boolean loginSuccess(User user) {
return true;
}
}
```
### 配置文件编写
配置文件编写前我们需要简单了解一下整个请求流程:
- 
#### `Spring.xml`
```xml
```
#### `Spring-mvc.xml`
```xml
```
#### `web.xml`
```xml
org.springframework.web.context.ContextLoaderListener
contextConfigLocation
classpath:spring.xml
CharacterEncodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
CharacterEncodingFilter
/
DispatcherServlet
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-mvc.xml
1
DispatcherServlet
/
```
- Tomcat 运行时逻辑模型:
- 
1. Tomcat 容器中一个 Context 为一个工程,一个 wrapper 封装一个 Servlet。注意这里的 Context 跟 Spring 中的 ApplicationContext 没有关系,它代表的是一个容器,实现类是 `org.apache.catalina.core.StandardContext`
1. Tomcat 容器中的 Context 与 Spring 中的 ApplicationContext 的关系如下图(不是类图):
- 
### 运行测试
1. 因为 IDEA 不会自动帮我们部署 Maven 库,所以我们需要将这些库添加到 /WEB-INF/lib/ 下:
1. 右键工程,点击 Open Module Settings,点击左侧的 Artifacts
1. 我们可以看到的是 Output Layout,左侧是 WEB-INF,右侧是添加的 Maven 依赖,全选右侧 Maven,右键并点击 put into /WEB-INF/lib
1. 这样在启动服务器时依赖库就可以自动部署到服务器了,需要注意的是**每增加一些 Maven 依赖,在启动服务器前都需要将这些依赖添加到 /WEB-INF/lib**
1. 配置 Tomcat 服务器
1. File -> settings -> Build,Execution,Deployment -> Application Servers ,添加一个 Tomcat 服务器
1. Run -> Edit Configurations 添加一个 Tomcat Server(Local)
1. Application server 选择刚配置的 Tomcat 服务器
1. Deployment 添加 Artifacts,右侧的 Application context 指的是如何访问该工程,比如 如果填了 `/oss`, 那么可以通过 `http://localhost:8080/oss` 来访问首页,如果这个地方做了修改 Server 下的默认连接也需要作相应修改,否则是无法访问的。
1. 也可以修改一下配置的命名,默认是 Unnamed。
1. 使用 log4j2 来输出 Spring 的日志,这样万一出了什么问题的话就能很快的定位问题位置
1. 配置 Logger
```xml
```
1. 导入适配器依赖
```xml
org.apache.logging.log4j
log4j-jcl
2.1
```
1. 测试结果
```log
2017-04-23 10:23:09,422 [http-nio-8080-exec-11] TRACE com.github.crainstorm.oss.user.action.UserAction - enter checkLogin...
2017-04-23 10:23:09,422 [http-nio-8080-exec-11] DEBUG com.github.crainstorm.oss.user.action.UserAction - User{username='17839918876', password='f217305153bc4761daaf98b3e5bb41c7', nickname='17839918876', phone='null', birthday=null}
2017-04-23 10:23:09,422 [http-nio-8080-exec-11] INFO com.github.crainstorm.oss.user.action.UserAction - login success...
```
## Spring && SpringMVC && Mybatis
### 代码实现
#### Dao
```sql
create table user
(
user_id int not null AUTO_INCREMENT,
username varchar(25) not null unique,
password varchar(120) not null,
nickname varchar(20),
phone char(11) unique not null,
avatar varchar(50) default '/images/avatars/default.jpg',
sex varchar(8) default '保密',
birthday date,
is_valid bool not null default true,
primary key (user_id),
index (username),
check(sex in('男','女','保密'))
);
```
```java
package com.github.crainstorm.oss.user.dao;
/**
* Created by chen on 4/17/17.
*/
@Repository
public interface UserDao {
int checkLogin(User user);
}
```
#### service
```java
@Transactional // 事务
@EnableTransactionManagement // 重要!重要!重要! 如果不加的话,部署到服务器上以后事务无法生效,即 JDBC 无法被 Spring 的事务管理器管理,执行过程中出现问题无法回滚
@Service
public class UserServiceImpl implements UserService {
private static final Logger LOGGER = LogManager.getLogger(UserServiceImpl.class);
@Autowired
private UserDao dao;
@Transactional(readOnly = true)
public boolean loginSuccess(User user) {
int num = dao.checkLogin(user);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("selected " + num + " records....");
}
return num > 0;
}
}
```
### 配置文件编写
#### `UserDao.xml`
- 与 UserDao 放在同一目录下
```xml
```
#### `Spring.xml`
```xml
```
#### pom.xml
```xml
com.alibaba
druid
1.0.29
org.mybatis
mybatis
3.4.4
org.mybatis
mybatis-spring
1.3.0
mysql
mysql-connector-java
6.0.3
```
### 运行测试
1. 配置 log4j2 输出 Mybatis 和 spring-mybatis 的日志
```xml
```
1. 部署 Dao 层 Mapper。IDEA 会将 `src/main/java` 下的类编译好以后部署到 `/WEB-INF/class`, 但是 `src/main/java` 里的其他文件默认是不部署的,所以我们需要配置 `pom.xml` 将 Mapper 也部署到服务器
```xml
src/main/java
**/dao/*.xml
src/main/resources
```
1. 测试结果
```
2017-04-23 12:42:07,682 [http-nio-8080-exec-2] TRACE com.github.crainstorm.oss.user.action.UserAction - enter checkLogin...
2017-04-23 12:42:07,682 [http-nio-8080-exec-2] DEBUG com.github.crainstorm.oss.user.action.UserAction - User{username='rainstorm', password='f217305153bc4761daaf98b3e5bb41c7', nickname='rainstorm', phone='null', birthday=null}
2017-04-23 12:42:07,682 [http-nio-8080-exec-2] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Creating new transaction with name [com.github.crainstorm.oss.user.service.UserServiceImpl.loginSuccess]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; ''
2017-04-23 12:42:07,687 [http-nio-8080-exec-2] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@20c3cf2a] for JDBC transaction
2017-04-23 12:42:07,687 [http-nio-8080-exec-2] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Setting JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@20c3cf2a] read-only
2017-04-23 12:42:07,688 [http-nio-8080-exec-2] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@20c3cf2a] to manual commit
2017-04-23 12:42:07,688 [http-nio-8080-exec-2] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
2017-04-23 12:42:07,688 [http-nio-8080-exec-2] DEBUG org.mybatis.spring.SqlSessionUtils - Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@34a6aa63]
2017-04-23 12:42:07,688 [http-nio-8080-exec-2] DEBUG org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@20c3cf2a] will be managed by Spring
2017-04-23 12:42:07,688 [http-nio-8080-exec-2] DEBUG com.github.crainstorm.oss.user.dao.UserDao.checkLogin - ==> Preparing: SELECT COUNT(*) FROM user WHERE username = ? AND password = ?;
2017-04-23 12:42:07,689 [http-nio-8080-exec-2] DEBUG com.github.crainstorm.oss.user.dao.UserDao.checkLogin - ==> Parameters: rainstorm(String), f217305153bc4761daaf98b3e5bb41c7(String)
2017-04-23 12:42:07,705 [http-nio-8080-exec-2] TRACE com.github.crainstorm.oss.user.dao.UserDao.checkLogin - <== Columns: COUNT(*)
2017-04-23 12:42:07,705 [http-nio-8080-exec-2] TRACE com.github.crainstorm.oss.user.dao.UserDao.checkLogin - <== Row: 1
2017-04-23 12:42:07,705 [http-nio-8080-exec-2] DEBUG com.github.crainstorm.oss.user.dao.UserDao.checkLogin - <== Total: 1
2017-04-23 12:42:07,705 [http-nio-8080-exec-2] DEBUG org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@34a6aa63]
2017-04-23 12:42:07,705 [http-nio-8080-exec-2] DEBUG com.github.crainstorm.oss.user.service.UserServiceImpl - selected 1 records....
2017-04-23 12:42:07,705 [http-nio-8080-exec-2] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@34a6aa63]
2017-04-23 12:42:07,705 [http-nio-8080-exec-2] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@34a6aa63]
2017-04-23 12:42:07,706 [http-nio-8080-exec-2] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@34a6aa63]
2017-04-23 12:42:07,706 [http-nio-8080-exec-2] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Initiating transaction commit
2017-04-23 12:42:07,706 [http-nio-8080-exec-2] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@20c3cf2a]
2017-04-23 12:42:07,708 [http-nio-8080-exec-2] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Resetting read-only flag of JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@20c3cf2a]
2017-04-23 12:42:07,709 [http-nio-8080-exec-2] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@20c3cf2a] after transaction
2017-04-23 12:42:07,709 [http-nio-8080-exec-2] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
2017-04-23 12:42:07,709 [http-nio-8080-exec-2] INFO com.github.crainstorm.oss.user.action.UserAction - login success...
2017-
```
## 参考
1. [Annotation-based container configuration](https://docs.spring.io/spring/docs/4.3.7.RELEASE/spring-framework-reference/html/beans.html#beans-annotation-config)
1. [Classpath scanning and managed components](https://docs.spring.io/spring/docs/4.3.7.RELEASE/spring-framework-reference/html/beans.html#beans-classpath-scanning)
1. [Configuring Spring MVC](https://docs.spring.io/spring/docs/4.3.7.RELEASE/spring-framework-reference/html/mvc.html#mvc-config)
1. [ @RequestBody, @ResponseBody 注解详解 ](http://blog.csdn.net/walkerjong/article/details/7520896)
1. [Spring mvc HandlerMapping VS HandlerAdapter](http://stackoverflow.com/questions/23325111/spring-mvc-handlermapping-vs-handleradapter)
1. [org.springframework.web.context.ContextLoaderListener](https://docs.spring.io/spring/docs/4.3.7.RELEASE/javadoc-api/org/springframework/web/context/ContextLoaderListener.html)
1. [org.springframework.web.context.ContextLoader](https://docs.spring.io/spring/docs/4.3.7.RELEASE/javadoc-api/org/springframework/web/context/ContextLoader.html)
1. [22.2 The DispatcherServlet](https://docs.spring.io/spring/docs/4.3.7.RELEASE/spring-framework-reference/html/mvc.html#mvc-servlet)
1. [Purpose of ContextLoaderListener – Spring MVC](https://schoudari.wordpress.com/2012/07/23/purpose-of-contextloaderlistener-spring-mvc/)
1. [Servlet 工作原理解析](https://www.ibm.com/developerworks/cn/java/j-lo-servlet/)
1. [mybatis](http://www.mybatis.org/mybatis-3/zh/)
1. [mybatis-spring](http://www.mybatis.org/spring/zh/index.html)