说明
平时学习Spring很少用到AOP这个功能,不过这个功能对Spring事务管理有一定的作用。发现真正在用的时候自己配环境的情况特别少,都是在别人配好环境的情况下就开始用了。所以更加要记录一下配置环境的过程啊。这里用一个转账实例来说明。
Spring事务管理
事务管理一般涉及到三个顶级接口,分别是PlatformTransactionManager(事务管理器),TransactionDefinition(事务详情),TransactionStatus(事务状态)。进行事务配置时,必须要配置事务管理器和事务详情。而事务状态是Spring底层用来进行相应操作的。
事务
开始之前说一下事务吧。这里有一点简单的介绍,可以看一下,这里要再说一下。事务状态和事务详情就不说了,可以查一下。这里主要说一下传播行为,比较常用的记录一下,不常用的不说了。
传播行为主要是指两个业务之间如何共享事务
例如转账的时候,先要从一个账户出账,然后另一个账户入账,这就可以看成是两个业务。传播行为常用的主要有下面几种:
required:必须
支持当前事务,A如果有事务,B将使用该事务,如果A没有事务,B将创建一个新的事务
supports:支持
支持当前事务,A如果有事务,B将使用该事务,如果A没有事务,B将以非事务执行
requires_new:必须新的
如果A有事务,将A的事务挂起,B创建一个新的事务,如果A没有事务,B创建一个新事务
事务管理器
我们比较常用的事务管理器一般有两种,分别是DataSourceTransactionManager和HibernateTransactionManager。
- DataSourceTransactionManager:jdbc开发时的事务管理器,采用jdbcTemplate
- HibernateTransactionManager:hibernate开发时用的事务管理器
这里用DataSourceTransactionManager来说明
准备工作
下面是一些准备工作,我们需要数据库中的数据,所以这里新建一个数据库
create database 数据库名;
use 数据库名;
create table account(
id int primary key auto_increment,
username varchar(50),
money int(20)
);
insert into account values(null,"jack",10000);
insert into account values(null,"rose",10000);
除了这个之外,我们还要新建一个项目,待会方便直接使用
jar包
目录
都是比较基础的代码,直接贴出来就好。
interface AccountDao
package com.shanmu.dao;
public interface AccountDao {
public void out(String outer,Integer money);
public void in(String inner,Integer money);
}
class AccountDaoImpl
package com.shanmu.daoimpl;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import com.shanmu.dao.AccountDao;
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{
@Override
public void out(String outer, Integer money) {
this.getJdbcTemplate().update("update account set money = money -? where username = ?",money,outer);
}
@Override
public void in(String inner, Integer money) {
this.getJdbcTemplate().update("update account set money = money +? where username = ?",money,inner);
}
}
interface AccountService
package com.shanmu.service;
public interface AccountService {
/**
* 转账
* @param outer
* @param inner
* @param money
*/
public void transfer(String outer,String inner,Integer money);
}
class AccountServiceImpl
package com.shanmu.serviceimpl;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.shanmu.dao.AccountDao;
import com.shanmu.service.AccountService;
public class AccountServiceImpl implements AccountService {
//由Spring注入
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
//这里是转账的方法,调用dao
public void transfer(String outer, String inner, Integer money) {
accountDao.out(outer, money);
/*
这里是测试事务的语句,当int i=1/0没有注释的时候会产生异常,如果没进行事务管理,
则只会出账,不会进账,总钱数少了1000,如果进行了事务管理,则会回滚,不会提交,
这样原本的账户不会发生变化。
*/
//int i=1/0;
accountDao.in(inner, money);
}
}
class TestApp
package com.shanmu;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.shanmu.service.AccountService;
public class TestApp {
@Test
public void demo01(){
String xmlPath="applicationContext.xml";
ApplicationContext applicationContext=new ClassPathXmlApplicationContext(xmlPath);
AccountService accountService=(AccountService)applicationContext.getBean("accountService");
accountService.transfer("jack", "rose", 1000);
}
}
这里的xml文件还没有给出,下面主要来看怎么加入事务了。
AOP配置事务——基于xml
指的是在Spring的xml文件中进行配置aop,自动生成代理,从而进行事务的管理,步骤如下:
- 配置事务管理器
- 配置事务详情
- 配置AOP
看一下配置文件(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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd"
是配置事务详情要用到的
-->
<!-- 1 dataSource -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/数据库名"></property>
<property name="user" value="root"></property>
<property name="password" value="密码"></property>
</bean>
<!-- 2 dao -->
<bean name="accountDao" class="com.shanmu.daoimpl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3 service -->
<bean name="accountService" class="com.shanmu.serviceimpl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 4事务管理 -->
<!-- 4.1 事务管理器 -->
<bean name="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 4.2 事务详情(事务通知),在aop筛选基础上,对ABC三个确定使用什么样的事务。
<tx:attributes>用于配置事务详情(事务属性)
<tx:method name=""/>详情具体配置
propagation 传播行为
isolation 隔离级别
-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- 这里传播行为默认为required -->
<tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<!-- 4.3 AOP编程,目标类有ABCD(4个连接点),切入点表达式 确定增强的连接器,从而获得切入点:ABC -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.shanmu.serviceimpl.*.*(..))"/>
</aop:config>
运行结果
看一下数据库
AOP配置事务——基于注解
步骤是:
- 配置事务管理器,并将事务管理器交于Spring
- 在目标类或目标方法中添加注释即可@Transactional
所以,首先在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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 1 dataSource -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/数据库名"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 2 dao -->
<bean name="accountDao" class="com.shanmu.daoimpl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3 service -->
<bean name="accountService" class="com.shanmu.serviceimpl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<!-- <property name="transactionTemplate" ref="transactionTemplate"></property> -->
</bean>
<!-- 4 事务管理 -->
<!-- 4.1 事务管理器 -->
<bean name="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 4.2 将管理器交于spring
* transaction-manager 配置事务管理器
* proxy-target-class
默认是false
若为true,底层强制使用cglib代理
-->
<tx:annotation-driven transaction-manager="txManager" />
然后在transfer方法上加上注解
package com.shanmu.serviceimpl;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.shanmu.dao.AccountDao;
import com.shanmu.service.AccountService;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
@Transactional
//@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT)
//给类加上该注解,则该类中所有方法执行事务,给方法上加注解,则该方法执行事务
public void transfer(String outer, String inner, Integer money) {
accountDao.out(outer, money);
/*
这里是测试事务的语句,当int i=1/0没有注释的时候会产生异常,如果没进行事务管理,
则只会出账,不会进账,总钱数少了1000,如果进行了事务管理,则会回滚,不会提交,
这样原本的账户不会发生变化。
*/
//int i=1/0;
accountDao.in(inner, money);
}
}
```java
这里会发现没有配事务详情,其实@Transactional注解可以写成下面的形式,这样就可以在括号中配置事务详情
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191116202146442.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hhbnNtdQ==,size_16,color_FFFFFF,t_70)
**结果**
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191116202435116.png)