# 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 - ![](../res/ssm-newProject.png) #### 添加 Maven 支持 1. 右键工程名,点击 Add Framwork Support.. - ![](../res/ssm-addFS.png) 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
username:
password:
``` 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; } } ``` ### 配置文件编写 配置文件编写前我们需要简单了解一下整个请求流程: - ![](../res/springmvc-flow.png) #### `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 运行时逻辑模型: - ![](../res/ssm-tomcatRuntimeModel.png) 1. Tomcat 容器中一个 Context 为一个工程,一个 wrapper 封装一个 Servlet。注意这里的 Context 跟 Spring 中的 ApplicationContext 没有关系,它代表的是一个容器,实现类是 `org.apache.catalina.core.StandardContext` 1. Tomcat 容器中的 Context 与 Spring 中的 ApplicationContext 的关系如下图(不是类图): - ![](../res/ssm-contextInTomcatAndSpring.png) ### 运行测试 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)