SSM框架整合流程与原理解读(附源码链接)

news2024/9/25 23:13:46

本文参考黑马教程,对 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 的开发环境

  1. 在 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>
    
  2. 在 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等)
    参考: / 和 /* 的区别和用法

  3. 创建 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 的开发环境

  1. 编写 index.jsp 的超链接

    <a href="account/findAll">测试查询所有</a>
    
  2. 创建 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

  1. 在 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 文件下。因此可以设置文件的路径,用以指示加载类路径下的配置文件。

  2. 在 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 的开发环境

  1. 在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的开发环境

  1. 编写账户持久层接口映射文件 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>
    
  2. 映射文件 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

  1. 在 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>
    
  2. 更新账户业务层接口,给 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);
        }
    }
    
  3. 添加 index.jsp 超链接

    <a href="account/update">测试更新账户</a>
    
  4. 编写表现层测试方法

    @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";
        }
    }
    
  5. 编写成功界面,查看结果。
    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>
    
  6. 测试结果:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    测试结果表明: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 的容器。

  1. 由于 SpringMVC 在扫描时扩大了扫描范围,从而导致 Controller 层在注入 Service 时,实际注入的是子容器中的 Service 实例
  2. 事务被配置在父容器中,Spring 父容器在装载 Service 时会应用事务配置,而 SpringMVC 只是单纯加载 Service 的实例,没有事务的配置
  3. 所以启动服务器,采用就近原则,也就是用了 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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/430425.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通过KNN分类模型预测股票涨跌,然后与基准收益画图对比

目录 1 获取数据 2 特征工程&#xff1a;定义一个用于分类的函数 3 特征工程&#xff1a;生成训练数据 4 根据训练数据对分类模型进行拟合&#xff0c;并给出得分 5 使用训练完成的分类模型进行数据预测 6 定义几个有用的函数 7 生成基准收益和策略收益对比结果 记录一下…

排序算法——快速排序(C语言多种实现及其优化策略)

快速排序总述快速排序递归框架单趟快速排序**hoare法****挖坑法**前后指针法快排改进key的选取**随机选key****三数取中**小区间优化**面对多个重复数据时的乏力**总述 快速排序可以说是排序界的大哥的存在&#xff0c;在c库中的qsort和c库中的sort两个排序底层都是用快速排序…

常用运放电路总结记录

前言 上一篇文章我们复习了一下运放的基本知识&#xff0c;尽量的用简单的描述带大家去理解运算放大器&#xff1a; 带你理解运算放大器 对于运放的使用&#xff0c;存在着一些经典常用的应用电路&#xff0c;这个其实网络上已经有大量的文章做记录总结了&#xff0c;作为电…

【Elastic (ELK) Stack 实战教程】11、使用 ElastAlert 实现 ES 钉钉群日志告警

目录 一、ElastAlert 概述 二、安装 ElastAlert 2.1 安装依赖 2.2 安装 Python 环境 2.3 安装 ElastAlert 2.4 ElastAlert 配置文件 2.5 创建 ElastAlert 索引 2.6 测试告警配置是否正常 三、ElastAlert 集成钉钉 3.1 下载 ElastAlert 钉钉报警插件 3.2 创建钉钉机器…

【硬件外设使用】——can

【硬件外设使用】——can can基本概念can 通讯can使用方法pyb.can can可用的传感器 can基本概念 CAN是Controller Area Network的缩写&#xff0c;即控制器局域网。它是一种多主机串行通信协议&#xff0c;用于连接计算机、传感器、执行器和其他设备。 常用于汽车、工业自动化…

如何在不丢失数据的情况下重装Windows 10?

为什么需要重新安装Windows 10&#xff1f; 随着时间的推移&#xff0c;Windows可能会变慢。这可能是由多种原因引起的&#xff0c;例如您安装了许多额外的启动程序&#xff0c;这些程序会延长启动过程等。如果您的Windows系统速度变慢并且无论您卸载多少程序都没有加速&…

CodeGeeX论文发表:揭秘AI辅助编程工具背后的大模型

近日&#xff0c;CodeGeeX模型迭代v1.5版本上线&#xff0c;用户反馈模型效果和使用效率较之前有大幅提升。 恰逢CodeGeeX团队在arxiv上发布了论文&#xff0c;详细介绍了CodeGeeX AI编程辅助工具背后的代码生成大模型的架构、训练过程及推理加速等工作。 今天我们对这篇论文的…

【从零开始学Skynet】实战篇《球球大作战》(三):封装常用的API

为什么要封装&#xff1f;封装可以减少一些重复代码&#xff0c;提高我们的工作效率。 1、定义属性 新建文件lualib/service.lua&#xff0c;定义模块的属性&#xff0c; service模块是对Skynet服务的一种封装&#xff0c;代码如下所示&#xff1a; local skynet require &qu…

Linux 下编译 thrift

thrift编译需要依赖 openssl&#xff0c;首先按照文章《Openssl在Linux下编译/交叉编译》编译openssl。 网上有文章说thrift编译还需要依赖Boost&#xff0c;libevent&#xff0c;但是我发现不依赖这两个库也能把thrift编译出来。在 https://github.com/apache/thrift/releases…

R -- 二分类问题的分类+预测

brief 分类大致分为有监督分类和无监督分类&#xff0c;这里学习有监督分类。有监督分类一般包括逻辑回归、决策树、随机森林、支持向量机、神经网络等。 有监督学习基于一组包含预测变量值和输出变量值的样本单元。然后可以将全部数据分为一个训练数据集和一个验证数据集&…

【好刊推荐】知名出版社影响因子7+被踢出SCI,投稿前如何选期刊?

今年3月Hindawi旗下的19本期刊被SCIE剔除&#xff0c;其中有一本影响因子7&#xff0c;以下从期刊各个指标方面分析一下具体原因&#xff1a; 期刊剔除&#xff1a;影响因子7 期刊简介 期刊名称&#xff1a; OXIDATIVE MEDICINE AND CELLULAR LONGEVITY ISSN / eISSN&#…

Stacking算法预测银行客户流失率

Stacking算法预测银行客户流失率 描述 为了防止银行的客户流失&#xff0c;通过数据分析&#xff0c;识别并可视化哪些因素导致了客户流失&#xff0c;并通过建立一个预测模型&#xff0c;识别客户是否会流失&#xff0c;流失的概率有多大。以便银行的客户服务部门更加有针对…

Android桌面长按图标快捷方式——Shortcuts

简介 当我们在长按Android应用的桌面图标时&#xff0c;一般回弹出一个列表&#xff0c;上面一般有应用信息、卸载应用等功能&#xff0c;并且部分应用在这里还添加了自己的快捷方式&#xff0c;今天主要介绍如何添加自定义的快捷方式。 长按桌面显示的快捷方式在Android中叫…

中小企业面临怎样的数字化转型局面

当前&#xff0c;我国经济长期向好的基本面没有改变&#xff0c;但承受着“需求收缩、供给冲击、预期减弱”的三重压力&#xff0c;中小企业的数字化转型之路较之以往更加艰难、曲折。为帮助中小企业纾困解难、平稳渡过危机&#xff0c;需进一步优化政策“组合拳”&#xff0c;…

单片机中常用的轻量级校验算法

UART有一个奇偶校验&#xff0c;CAN通信有CRC校验。Modbus、MAVlink、USB等通信协议也有校验信息。 在自定义数据存储时&#xff0c;有经验的工程师都会添加一定校验信息。 你平时通信&#xff0c;或者数据存储时&#xff0c;你有用到校验信息吗&#xff1f;下面就介绍几种常见…

Java面试题总结 | Java面试题总结3-JVM模块(持续更新)

JVM 文章目录JVMJVM的内存组成模型java的内存模型定义了什么java的内存分布情况程序计数器是什么&#xff1f;堆、栈、方法区都存放的是什么堆和栈的区别类加载JMM主内存和本地内存交互操作volatile如何保证可见性volatile如何保证有序性happen-before了解过吗&#xff1f;内存…

【JS】BOM 详解(工作必备)

文章目录BOM一、History &#xff08;浏览器记录&#xff09;1.1、history.go&#xff08;指定页&#xff09;1.2、history.back&#xff08;上一页&#xff09;1.3、history.forword&#xff08;下一页&#xff09;二、Location&#xff08;浏览器地址&#xff09;2.1、操作属…

基于OpenCV的图片和视频人脸识别

目录 &#x1f969;前言 &#x1f356;环境使用 &#x1f356;模块使用 &#x1f356;模块介绍 &#x1f356;模块安装问题: &#x1f969;人脸检测 &#x1f356;Haar 级联的概念 &#x1f356;获取 Haar 级联数据 &#x1f357; 1.下载所需版本 &#x1f357; 2.安…

前后端不分离项目如何使用elementUI

首先&#xff0c;去官网下载element 的js和css和字体等文件 其次&#xff0c;分别将js和css 引入到项目 然后就可以使用了&#xff0c;使用方法和vue中使用element方法一致、

5款最新最实用的小软件,让你的工作和生活更轻松

我喜欢发现和分享一些好用的软件&#xff0c;我觉得它们可以让我们的工作和生活更加轻松和快乐。今天给大家介绍五款我最近发现的软件&#xff0c; GIF录制工具——Screen To Gif Screen To Gif是一款完全免费的GIF录制神器&#xff0c;可以让你轻松地录制屏幕、摄像头或画板…