两个数据层的类 Dao1 和 Dao2,这里只是用最简单的插入语句来演示。
package com.youka.aop.dao;
import org.springframework.jdbc.core.JdbcTemplate;
public class Dao1 {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public int insert(String name, String sex) {
String sql = "insert into test1 (name,sex) value (?,?)";
return jdbcTemplate.update(sql,name,sex);
}
}
package com.youka.aop.dao;
import org.springframework.jdbc.core.JdbcTemplate;
public class Dao2 {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public int insert(int code) {
String sql = "insert into test2 (code) value (?)";
// 模拟异常
if (code == 3)
code = code / 0;
return jdbcTemplate.update(sql, code);
}
}
控制层 Service1 类
package com.youka.aop.service;
import com.youka.aop.dao.Dao1;
import com.youka.aop.dao.Dao2;
public class Service1 {
private Dao1 dao1;
private Dao2 dao2;
public void setDao1(Dao1 dao1) {
this.dao1 = dao1;
}
public void setDao2(Dao2 dao2) {
this.dao2 = dao2;
}
public void insert(String name, String sex, int code) {
dao1.insert(name, sex);
dao2.insert(code);
}
}
application.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"
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">
<!-- spring-jdbc -->
<context:property-placeholder location="classpath:jdbc.properties" />
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource1">
<property name="driverClassName" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${user}"></property>
<property name="password" value="${password}"></property>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate1">
<property name="dataSource" ref="dataSource1"></property>
</bean>
<bean class="com.youka.aop.dao.Dao1" id="dao1">
<property name="jdbcTemplate" ref="jdbcTemplate1"></property>
</bean>
<bean class="com.youka.aop.dao.Dao2" id="dao2">
<property name="jdbcTemplate" ref="jdbcTemplate1"></property>
</bean>
<bean class="com.youka.aop.service.Service1" id="service">
<property name="dao1" ref="dao1"></property>
<property name="dao2" ref="dao2"></property>
</bean>
</beans>
测试文件
package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.youka.aop.service.Service1;
public class test {
public static void main(String[] args) {
String path = "application.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(path);
Service1 service = (Service1) context.getBean("service");
service.insert("shiwu", "nan", 3);
}
}
如果不进行事务管理,执行完测试类 test 的 main 方法之后,会出现的现象是在 test1·表中添加了一条记录,但是 test2 表中因为发生了异常并没有添加记录。这在实际项目中是不允许出现的,例如,A 从银行 C 给 B 转账 100 元,银行从 A 账号减了 100 元,但是在执行给 B 账号加 100 元的时候出现异常了,最后并没有给 B 账户加上这 100 元,而且也没有返回 A 账号 100 元,这是不可接受的。
AOP 执行事务管理
pom.xml 引入依赖
<!-- AOP 需要的包>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
事务管理器
<!-- 事务管理器 -->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource1"></property>
</bean>
增强
需要用到 tx,需要在根标签处添加命名空间
// 添加 tx 命名空间
xmlns:tx="http://www.springframework.org/schema/tx"
// 在 xsi:schmaLocation 中添加 tx.xsd 的 Location 信息
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
<!-- 增强 -->
<tx:advice transaction-manager="transactionManager">
<!-- 需要切的连接点 -->
<tx:attributes>
<!-- 表示切到以insert开头的方法上,事务处理机制是一个事务 -->
<tx:method name="insert*" propagation="REQUIRED"></tx:method>
<tx:method name="update*" propagation="REQUIRED"></tx:method>
<tx:method name="delete*" propagation="REQUIRED"></tx:method>
</tx:attributes>
</tx:advice>
AOP
需要用到 aop,需要在根标签处添加命名空间
// 添加 tx 命名空间
xmlns:aop="http://www.springframework.org/schema/aop"
// 在 xsi:schmaLocation 中添加 tx.xsd 的 Location 信息
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/tx/spring-aop.xsd
连接点 —— Service1 类的 insert 方法
切点 —— Service1 包下的所有方法
切面 —— 切点 + 增强
<!-- 增强 -->
<tx:advice transaction-manager="transactionManager" id="advice1">
<!-- 需要切的连接点 -->
<tx:attributes>
<!-- 表示切到以insert开头的方法上,事务处理机制是一个事务 -->
<tx:method name="insert*" propagation="REQUIRED"></tx:method>
<tx:method name="update*" propagation="REQUIRED"></tx:method>
<tx:method name="delete*" propagation="REQUIRED"></tx:method>
</tx:attributes>
</tx:advice>
<!-- aop -->
<aop:config>
<!-- 切点 -->
<aop:pointcut expression="execution(* com.youka.aop.service.*.*(..))" id="pointCut1"/>
<!-- 切面 -->
<aop:advisor advice-ref="advice1" pointcut-ref="ponitCut1"/>
</aop:config>
知识点
<tx:method name="insert*" propagation="REQUIRED"></tx:method>
propagation 有七个属性值。
属性值 | 描述 |
---|---|
REQUIRED | 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 |
SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常。 |
REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
NESTED | 支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。 |
<aop:pointcut expression="* com.youka.aop.service.*.*(..)" id="pointCut1"/>
expression 语法:execution(修饰符 返回值 包名.类名/接口名.方法名(参数列表))
,* 表示任意值。