本文参考黑马教程,对 MyBatis、Spring、SpringMVC 三个框架进行逐步整合,并对整合后事务失效原因进行总结。
源码链接:https://download.csdn.net/download/weixin_43819566/87690821
文章目录
- 一、搭建整合环境
- 1.1 整合项目说明
- 1.2 整合的思路 ★
- 1.3 创建数据库和表结构
- 1.4 创建maven的工程
- 1.5 编写实体类
- 1.6 编写 dao 接口
- 1.7 编写 service 接口和实现类
- 1.9 项目最终目录展示
- 二、Spring 框架代码的编写
- 2.1 搭建 Spring 的开发环境
- 2.2 测试 Spring 的开发环境
- 三、SpringMVC 框架代码的编写
- 3.1 搭建 SpringMVC 的开发环境
- 3.2 测试 SpringMVC 的开发环境
- 四、Spring 整合 SpringMVC 的框架
- 4.1 Spring 整合 SpringMVC 原理解析 ★
- 4.2 Spring 整合 SpringMVC
- 五、MyBatis 框架代码的编写
- 5.1 搭建 MyBatis 的开发环境
- 5.2 测试 MyBatis的开发环境
- 六、Spring 整合 MyBatis 框架
- 6.1 Spring 整合 MyBatis 原理解析 ★
- 6.2 Spring 整合 MyBatis
- 6.3 mapperLocations 配置的问题
- 七、Spring 框架配置 AOP 与事务控制
- 八、Spring 事务控制失效问题的原因
- 8.1 事务不起作用的原因
- 8.2 解决方案
一、搭建整合环境
1.1 整合项目说明
SSM整合会使用多种方式,本文讲述 XML+注解方式
1.2 整合的思路 ★
① 先搭建整合的环境
② 先把 Spring 的配置搭建完成
③ 再使用 Spring 整合 SpringMVC 框架
④ 最后使用 Spring 整合 MyBatis 框架
1.3 创建数据库和表结构
1.4 创建maven的工程
pom.xml 文件引入坐标,并将项目部署到 tomcat 服务器中。pom文件代码如下:
<properties>
<spring.version>5.2.0.RELEASE</spring.version>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.16</log4j.version>
<mysql.version>8.0.11</mysql.version>
<mybatis.version>3.5.6</mybatis.version>
</properties>
<dependencies>
<!-- 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>
<!--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>
<!--springmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring测试-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring事务控制-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!--jdbc模板技术-->
<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>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.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>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--日志-->
<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>
<!--spring 和 mybatis 整合包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!--druid数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
1.5 编写实体类
public class Account implements Serializable {
private Integer id;
private String name;
private Float money;
...
}
set、get 方法省略
1.6 编写 dao 接口
public interface AccountDao {
/**
* 查询所有账户
*/
public List<Account> findAll();
/**
* 根据名称查询账户
*/
Account findAccountByName(String name);
/**
* 更新账户
*/
void updateAccount(Account account);
}
1.7 编写 service 接口和实现类
/**
* 账户业务层接口
*/
public interface AccountService {
/**
* 根据id查询账户信息
*
* @return
*/
List<Account> findAll();
/**
* 转账
* @param sourceName 转成账户名称
* @param targetName 转入账户名称
* @param money 转账金额
*/
void transfer(String sourceName,String targetName,Float money);
}
/**
* 账户业务层实现类
*/
@Service
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public List<Account> findAll() {
System.out.println("业务层查询所有账户");
return null;
}
public void transfer(String sourceName, String targetName, Float money) {
System.out.println("转账方法执行");
}
}
1.9 项目最终目录展示
二、Spring 框架代码的编写
2.1 搭建 Spring 的开发环境
在项目的 resources 目录下创建 applicationContext.xml 的配置文件,编写具体的配置信息。
<?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">
<!--开启注解的扫描-->
<context:component-scan base-package="com.axy">
<!--配置哪些注解不扫描-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
注: Spring 与 SpringMVC 的注解应分开扫描,其容器的 Bean 不会冲突,具体缘由后文会解释。
2.2 测试 Spring 的开发环境
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestSpring {
@Autowired
private AccountService as;
@Test
public void testFindAll(){
as.findAll();
}
}
测试结果:
结果显示:spring 环境初步搭建完成。
三、SpringMVC 框架代码的编写
3.1 搭建 SpringMVC 的开发环境
-
在 web.xml 中配置 DispatcherServlet 前端控制器
<!--配置前端控制器--> <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> <!--启动服务器创建该 servlet --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <!--发任何请求都经过dispatcherServlet--> <url-pattern>/</url-pattern> </servlet-mapping>
-
在 web.xml 中配置过滤器解决中文乱码
<!-- 解决中文乱码的过滤器 --> <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>
注:/ 和 /* 的区别和用法
<url-pattern>/</url-pattern> 会匹配到/login这样的路径型url,不会匹配到模式为*.jsp这样的后缀型url
<url-pattern>/*</url-pattern> 会匹配所有url:路径型的和后缀型的url(包括/login,.jsp,.js和*.html等)
参考: / 和 /* 的区别和用法 -
创建 springmvc.xml 的配置文件,编写配置文件
<?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.axy.controller" /> <!--配置的视图解析器对象--> <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 mapping="/static/" location="/static/**" /> <!--开启 springmvc 注解的支持--> <mvc:annotation-driven /> </beans>
3.2 测试 SpringMVC 的开发环境
-
编写 index.jsp 的超链接
<a href="account/findAll">测试查询所有</a>
-
创建 AccountController 类,编写方法进行测试
@Controller @RequestMapping("account") public class AccountController { @RequestMapping("findAll") public String findAllAccount(){ System.out.println("表现层查询所有"); return "list"; } }
测试结果:
测试结果表明:springmvc 搭建完成。
四、Spring 整合 SpringMVC 的框架
目的:在controller中能成功的调用service对象中的方法。
4.1 Spring 整合 SpringMVC 原理解析 ★
javaWeb 中最大的域对象是 ServletContext,随着服务器的启动而创建,随着服务器关闭而销毁。
ContextLoaderListener 监听器,用来监听 ServletContext 域对象的创建和销毁。
当监听到 ServletContext 创建时,去加载 Spring 的配置文件,会创建 WEB 版本的工厂,存储到 ServletContext 对象中。
4.2 Spring 整合 SpringMVC
-
在 web.xml 中配置 ContextLoaderListener 监听器
<!--配置监听器,用于监听ServletContext创建--> <!--默认只加载 WEB-INF 下面的配置文件--> <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>
注:
① ContextLoaderListener 监听器实现了 ServletContextListener 接口,用于监听 ServletContext 对象的创建。
② 监听器默认只加载 WEB-INF 下面的配置文件,为了方便管理,最好将 Spring 配置文件放入 resources 文件下。因此可以设置文件的路径,用以指示加载类路径下的配置文件。 -
在 controller 中注入 service 对象,调用 service 对象的方法进行测试。
@Controller @RequestMapping("account") public class AccountController { @Autowired private AccountService accountService; @RequestMapping("findAll") public String findAllAccount(){ System.out.println("表现层查询所有"); accountService.findAll(); return "list"; } } @Service public class AccountServiceImpl implements AccountService { public List<Account> findAll() { System.out.println("业务层查询所有"); return null; } ... }
测试结果:
测试结果表明:spring 的配置文件成功被加载。
五、MyBatis 框架代码的编写
5.1 搭建 MyBatis 的开发环境
-
在web项目中创建注配置文件 mybatis.xml,并编写核心配置。
<?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> <!-- 配置引入外部 properties 文件--> <properties resource="druid.properties" /> <!--使用 typeAliases 配置别名--> <typeAliases> <package name="com.axy.domain" /> </typeAliases> <!--配置环境--> <environments default="mysql"> <!-- 配置 mysql 的环境--> <environment id="mysql"> <!-- 配置事务 --> <transactionManager type="JDBC" /> <!--配置连接池--> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> </environments> <!--配置映射标签--> <mappers> <package name="com.axy.dao"/> </mappers> </configuration>
druid.properties:
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssmdb?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=CST jdbc.username=root jdbc.password=123456 initialSize=5 maxActive=10 maxWait=3000
5.2 测试 MyBatis的开发环境
-
编写账户持久层接口映射文件 AccountDao.xml,映射文件如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.axy.dao.AccountDao"> <select id="findAll" resultType="Account"> select * from account </select> <select id="findAccountByName" resultType="Account"> select * from account where name = #{name} </select> <update id="updateAccount"> update account set money = #{money} where id = #{id} </update> </mapper>
-
映射文件 AccountDao.xml:
public class TestMyBatis { private InputStream in; private SqlSession sqlSession; private AccountDao accountDao; @Before public void init() throws Exception{ //1.读取配置文件 in = Resources.getResourceAsStream("mybatis.xml"); //2.获取SqlSessionFactory SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); //3.获取SqlSession对象 sqlSession = factory.openSession(); //4.获取dao的代理对象 accountDao = sqlSession.getMapper(AccountDao.class); } @After public void destroy() throws Exception { //提交事务 sqlSession.commit(); //释放资源 sqlSession.close(); in.close(); } @Test public void testFindAll() throws IOException { List<Account> list = accountDao.findAll(); for (Account account : list) { System.out.println(account); } } }
测试结果:
结果表明:MyBatis 框架初步搭建完成。
六、Spring 整合 MyBatis 框架
目的:把 mybatis.xml 配置文件中的内容配置到 applicationContext.xml 配置文件中。
6.1 Spring 整合 MyBatis 原理解析 ★
参考:Mybatis从入门到精通系列 02 —— 执行查询的底层原理分析
整体的执行流程,首先需要创建 sqlSessionFactory 工厂,通过工厂创建 sqlSession 对象,在通过 sqlSession 创建代理对象,最后执行查询所有的方法。因此,将 sqlSessionFactory 工厂的创建交给 spring 管理,以后的 sqlSession 对象的全由 IOC 容器中的工厂来创建。有了 sqlSession 可以创建代理对象,最后将代理对象放入 IOC 容器。
6.2 Spring 整合 MyBatis
-
在 spring 的配置文件 applicationContext.xml 中,分别配置连接池、sqlSessionFactory 工厂和 AccountDao 接口所在的包。
<!--spring整合mybatis--> <!--把 sqlSession 的创建交给 IOC 容器--> <!--有工厂可以创建 sqlSession => 有 sqlSession 可以创建代理对象 => 最后将代理对象放入 IOC 容器 --> <!--导入 jdbc 配置文件--> <context:property-placeholder location="classpath:druid.properties" system-properties-mode="FALLBACK" /> <!--配置连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <!--配置sqlSessionFactory工厂--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 数据源 --> <property name="dataSource" ref="dataSource" /> <!-- 配置别名 --> <property name="typeAliasesPackage" value="com.axy.domain" /> <!-- 配置 xml 映射配置文件--> <!-- 如果 Mapper.xml 与 Mapper.class 在同一个包下且同名,spring 扫描 Mapper.class 的同时会自动扫描同名的 Mapper.xml 并装配到 Mapper.class。 如果 Mapper.xml 与 Mapper.class 不在同一个包下或者不同名,就必须使用配置 mapperLocations 指定 mapper.xml 的位置。--> <!--<property name="mapperLocations" value="classpath:mapper/*.xml" />--> </bean> <!--配置AccountDao接口所在的包--> <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.axy.dao" /> </bean>
-
更新账户业务层接口,给 AccountDao 添加依赖注入。
@Service public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; public List<Account> findAll() { System.out.println("业务层查询所有账户"); return accountDao.findAll(); } public void transfer(String sourceName, String targetName, Float money) { System.out.println("测试转账"); //1. 根据名称查询转出账户 Account source = accountDao.findAccountByName(sourceName); //2. 根据名称查询转入账户 Account target = accountDao.findAccountByName(targetName); //3. 转出账户减钱 source.setMoney(source.getMoney()-money); //4. 转入账户加钱 target.setMoney(target.getMoney()+money); //5. 更新转出账户 accountDao.updateAccount(source); //6. 更新转入账户 accountDao.updateAccount(target); } }
-
添加 index.jsp 超链接
<a href="account/update">测试更新账户</a>
-
编写表现层测试方法
@Controller @RequestMapping("account") public class AccountController { @Autowired private AccountService accountService; @RequestMapping("findAll") public String findAllAccount(Model model){ System.out.println("表现层查询所有"); List<Account> list = accountService.findAll(); model.addAttribute("list", list); return "list"; } @RequestMapping("update") public String update(){ System.out.println("表现层转账!!!"); accountService.transfer("aaa", "bbb", 100f); return "success"; } }
-
编写成功界面,查看结果。
list.jsp:<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html> <head> <title>Title</title> </head> <body> <h3>账户信息如下</h3> ${requestScope.list}<br> ${requestScope.list[0]}<br> ${requestScope.list[1]}<br> </body> </html>
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html> <head> <title>Title</title> </head> <body> <h3>更新成功</h3><br> </body> </html>
-
测试结果:
测试结果表明:Spring 整合 MyBatis 成功。
另外,mybatis.xml 在整合后除测试外,就不用了。
6.3 mapperLocations 配置的问题
参考:mybatis 整合 spring 之 mapperLocations 配置的问题
Mapper.xml 与 Mapper.class 在同一个包下且同名,spring 扫描 Mapper.class 的同时会自动扫描同名的 Mapper.xml 并装配到 Mapper.class。
如果 Mapper.xml 与 Mapper.class 不在同一个包下或者不同名,就必须使用配置 mapperLocations 指定 mapper.xml 的位置。
例1:如项目中,AccountDao 接口与接口的配置文件有相同的包结构,服务器启动后,class 文件与 xml 文件,会在同一个包下且同名,那么不需要配置<property name=“mapperLocations” value=“classpath:com/axy/dao/*.xml” />
。
例2 :如项目中,AccountDao 接口与接口的配置文件不具有相同的包结构,服务器启动后,class 文件与 xml 文件,会在不同包下,那么需要配置<property name=“mapperLocations” value=“classpath:com/mapper/*.xml” />
。
七、Spring 框架配置 AOP 与事务控制
在 spring 配置文件 applicationContext.xml 中配置事务管理器、事务通知与 AOP
<!--配置 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="*" propagation="REQUIRED" read-only="false" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<!--配置AOP增强-->
<aop:config>
<!--切入点表达式-->
<aop:pointcut id="pt1" expression="execution(* com.axy.service.impl.*.*(..))" />
<!--建立事务通知 与 切入点表达式的对应关系-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1" />
</aop:config>
参考:Spring 从入门到精通系列 12—— Spring 中的事务控制
测试事务控制:
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
...
public void transfer(String sourceName, String targetName, Float money) {
System.out.println("业务层转账");
//1 根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
//2 根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
//3 转出账户减钱
source.setMoney(source.getMoney()-money);
//4 转入账户加钱
target.setMoney(target.getMoney()+money);
//5 更新转出账户
accountDao.updateAccount(source);
// 模拟异常
int i = 10/0;
//6 更新转入账户
accountDao.updateAccount(target);
}
}
测试结果(设置账户初试金额为1000):
结果表明,事务已经被控制住了。
自此 SSM 框架完成整合。
八、Spring 事务控制失效问题的原因
参考:ssm整合后配置的事务不起作用的原因和解决方法、SSM框架整合,事务不起作用的原因
假设 springmvc.xml 的配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
...>
<!--开启注解扫描-->
<context:component-scan base-package="com.axy" />
...
</beans>
8.1 事务不起作用的原因
web.xml 文件的加载顺序 context-param -> Listener -> filter -> Servlet(同类级别按照顺序执行)
,即先加载 spring 的配置文件再加载 springmvc 的配置文件,从而先初始化 spring 容器再初始化 springmvc 的容器。
- 由于 SpringMVC 在扫描时扩大了扫描范围,从而导致 Controller 层在注入 Service 时,实际注入的是子容器中的 Service 实例。
- 事务被配置在父容器中,Spring 父容器在装载 Service 时会应用事务配置,而 SpringMVC 只是单纯加载 Service 的实例,没有事务的配置。
- 所以启动服务器,采用就近原则,也就是用了 mvc 容器中的 ServiceImpl 实例,是没有经过事务配置的类。
8.2 解决方案
spring 和 springMVC 的注解扫描一定分开。
springmvc.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
...>
<!--开启注解扫描-->
<context:component-scan base-package="com.axy.controller" />
<!--方式二:开启注解扫描,只扫描 Controller
<context:component-scan base-package="com.axy.controller">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>-->
...
</beans>
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
...>
<!--开启注解的扫描-->
<context:component-scan base-package="com.axy">
<!--配置哪些注解不扫描-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
避坑点:如果设置的包的范围比较大,如:com.axy
事务是不会控制住的。可理解@Controller 注解与 @Service @Component @Repository 拥有一样的效果,@Service 被 springmvc 扫描后,加入 mvc 容器的是没有事务的配置。
<!--开启注解扫描,只扫描 Controller-->
<context:component-scan base-package="com.axy">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>