SpringMVC笔记07 SSM整合
coconutnut

https://www.bilibili.com/video/BV1Sb411s7qa

P46~54

三层架构

先保证每个框架能单独运行

然后用Spring整合另外2个

准备数据库

account表,以前建过了,继续用

搭建环境

创建Maven工程,选webapp

解决创建项目过慢,加一对archetypeCatalog:internal

pom.xml

版本锁定

1
2
3
4
5
6
7
8
9
10
<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>

加依赖

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
<dependencies>
<!-- spring IOC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- spring AOP -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- JdbcTemplate -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- 事务 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- 测试 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</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>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>

<!-- spring MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- servlet -->
<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>

<!-- jsp标签 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

<!-- log -->
<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>

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

</dependencies>

创建目录

java和resources文件夹

用到的类

Account三个属性

1
2
3
private Integer id;
private String name;
private Double money;

做2个方法

1
2
public List<Account> findAll();
public void saveAccount(Account account);

Spring框架

配置 applicationContext.xml

resources下新建applicationContext.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"?>
<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,controller不需要Spring框架去处理 -->
<context:component-scan base-package="com.coconutnut" >
<!-- 配置哪些注解不扫描 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

</beans>

controller是表现层的,Spring框架不管

加注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service("accountService")
public class AccountServiceImpl implements IAccountService {

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

@Override
public void saveAccount(Account account) {
System.out.println("业务层:保存账户");
}
}

测试

用junit单元测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TestSpring {

@Test
public void test(){
// 加载配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

// 获取对象
IAccountService as = (IAccountService) ac.getBean("accountService");

// 调用方法
as.findAll();
}

}

WARNING说没有log4j的配置文件

复制一个log4j.properties到resources目录下即可

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=/Users/coconutnut/TREE/Midgard/Idea/SSM/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

要改一下路径

SpringMVC框架

配置 web.xml

WEB-INF文件夹下的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
<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>
<!-- 加载springmvc.xml配置文件 -->
<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>
</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>

</web-app>

配置 springmvc.xml

resources文件夹下新建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
25
26
27
28
29
30
31
32
<?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注解 -->
<context:component-scan base-package="com.coconutnut">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

<!--配置的视图解析器对象-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<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>

页面

index.jsp中加一个超链接

1
2
3
4
5
6
7
8
9
10
<html>
<head>
<title>Title</title>
</head>
<body>

<a href="account/findAll">测试</a>

</body>
</html>

WEB-INF中新建pages文件夹,新建list.jsp

1
2
3
4
5
6
7
8
9
10
<html>
<head>
<title>Title</title>
</head>
<body>

<h3>查询所有的账户信息</h3>

</body>
</html>

控制器

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

@RequestMapping("/findAll")
public String findAll(){
System.out.println("表现层:查询所有");
return "TODO";
}

}

部署

测试

Spring整合SpringMVC

目标:Controller中调用业务层方法

方法:要把Service注入到Controller中

问题:web.xml中配置了springmvc.xml,springmvc.xml中配置了对Controller的扫描,其它扫描的配置在applicationContext.xml中,而这个文件始终没有被加载过

解决:启动tomcat服务器时,加载Spring的配置文件applicationContext.xml

ServletContext对象生命周期和服务器相同,可以用监听器监听其创建和销毁

于是,可以在web.xml中用监听器加载Spring的配置文件

监听器是spring-web提供的

它默认只加载WEB-INF目录下的applicationContext.xml配置文件

可以复制一份丢到WEB-INF目录,或者手动设置路径

1
2
3
4
5
6
7
8
<!-- 配置Spring的监听器,默认只加载WEB-INF目录下的applicationContext.xml配置文件 -->
<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>

这样启动服务器时,Spring的配置文件也加载了

Service和Controller都放到容器中了

于是可以在AccountController中进行依赖注入

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

@Autowired
private IAccountService accountService;

@RequestMapping("/findAll")
public String findAll(){
System.out.println("表现层:查询所有");
accountService.findAll();
return "list";
}

}

测试

点击浏览器中测试按钮

成功

MyBatis框架

加注解

1
2
3
4
5
6
7
8
9
public interface IAccountDao {

@Select("select * from account")
public List<Account> findAll();

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

}

配置 sqlMapConfig.xml

在resources中新建sqlMapConfig.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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://localhost:3306/groot?characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="iamgroot"/>
</dataSource>
</environment>
</environments>

<!-- 引入映射配置文件 -->
<mappers>
<package name="com.coconutnut.dao"/>
</mappers>
</configuration>

测试

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
public class TestMyBatis {

@Test
public void test01() throws Exception {
// 加载配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
// 创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 创建SqlSession对象
SqlSession session = factory.openSession();
// 获取到代理对象
IAccountDao dao = session.getMapper(IAccountDao.class);
// 查询所有数据
List<Account> list = dao.findAll();
for(Account account : list){
System.out.println(account);
}
// 关闭资源
session.close();
in.close();
}

@Test
public void test02() throws Exception {
Account account = new Account();
account.setName("熊大");
account.setMoney(400d);

// 加载配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
// 创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 创建SqlSession对象
SqlSession session = factory.openSession();
// 获取到代理对象
IAccountDao dao = session.getMapper(IAccountDao.class);

// 保存
dao.saveAccount(account);

// 提交事务
session.commit();

// 关闭资源
session.close();
in.close();
}

}

测试查询

测试保存

Spring整合MyBatis

目标:Service能调用Dao

此时Service已经在容器中了,需要把生成的Dao的代理对象也存到容器中

配置

在Spring的配置文件applicationContext.xml中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- Spring整合MyBatis框架 -->
<!-- 配置连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/groot?characterEncoding=utf8"/>
<property name="user" value="root"/>
<property name="password" value="iamgroot"/>
</bean>

<!-- 配置SqlSessionFactory工厂 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>

<!-- 配置AccountDao接口所在包 -->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.coconutnut.dao"/>
</bean>

有了这段配置,sqlMapConfig.xml其实就不需要了,可以删除

加注解

在AccountDao上加@Repository注解

在AccountServiceImpl中注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Service("accountService")
public class AccountServiceImpl implements IAccountService {

@Autowired
private IAccountDao accountDao;

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

@Override
public void saveAccount(Account account) {
System.out.println("业务层:保存账户");
accountDao.saveAccount(account);
}
}

在AccountController中把查出的数据存入Model

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

@Autowired
private IAccountService accountService;

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

}

页面

在list.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" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>

<h3>查询所有的帐户</h3>

<c:forEach items="${list}" var="account">
${account.name}
</c:forEach>

</body>
</html>

测试查询

出了一个异常

1
2
3
4
5
2020-04-07 12:31:59,102 36291  [r$PoolThread-#0] WARN  resourcepool.BasicResourcePool  - com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask@64fe7a60 -- Acquisition Attempt Failed!!! Clearing pending acquires. While trying to acquire a needed new resource, we failed to succeed more than the maximum number of allowed acquisition attempts (30). Last acquisition attempt exception: 
java.sql.SQLException: Unknown system variable 'tx_isolation'
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1055)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
...

查询发现是mysql-connector-java版本太低导致

在pom.xml中将

1
<mysql.version>5.1.6</mysql.version>

改为

1
<mysql.version>5.1.48</mysql.version>

再试

成功

事务

保存方法还需管理事务

Spring中进行声明式事务管理,在applicationContext.xml中增加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--配置Spring框架声明式事务管理-->
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>

<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="find*" read-only="true"/>
<tx:method name="*" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>

<!--配置AOP增强-->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.coconutnut.service.impl.*ServiceImpl.*(..))"/>
</aop:config>

页面

在index.jsp中新增表单

1
2
3
4
5
6
7
8
9
10
11
12
13
<body>

<a href="account/findAll">测试查询</a>

<h3>测试保存</h3>

<form action="account/save" method="post">
姓名:<input type="text" name="name" /><br/>
金额:<input type="text" name="money" /><br/>
<input type="submit" value="保存"/><br/>
</form>

</body>

控制器

增加save方法

1
2
3
4
5
6
@RequestMapping("/save")
public void save(Account account, HttpServletRequest request, HttpServletResponse response) throws IOException {
accountService.saveAccount(account);
response.sendRedirect(request.getContextPath()+"/account/findAll");
return;
}

存完重定向到list页面

测试保存

点击保存

成功

项目结构