一、aop配置语法及依赖项
见上一节
二、以银行账户的事务操作为例
2.1 配置aop通知中的细节:
- aop:before 作用:
用于配置前置通知。指定增强的方法在切入点方法之前执行 属性: method:用于指定通知类中的增强方法名称 ponitcut-ref:用于指定切入点的表达式的引用 poinitcut:用于指定切入点表达式 执行时间点: 切入点方法执行之前执行
- aop:after-returning
作用: 用于配置后置通知 属性: method:指定通知中方法的名称。 pointct:定义切入点表达式 pointcut-ref:指定切入点表达式的引用 执行时间点: 切入点方法正常执行之后。它和异常通知只能有一个执行
aop:after-throwing作用: 用于配置异常通知 属性: method:指定通知中方法的名称。 pointct:定义切入点表达式 pointcut-ref:指定切入点表达式的引用 执行时间点: 切入点方法执行产生异常后执行。它和后置通知只能执行一个
aop:after 作用:用于配置最终通知 属性: method:指定通知中方法的名称。 pointct:定义切入点表达式 pointcut-ref:指定切入点表达式的引用 执行时间点: 无论切入点方法执行时是否有异常,它都会在其后面执行。
2.2 银行转账工程
需要增强的数据库事务操作方法及类代码如下:
package com.service.impl;
import com.dao.IAccountDao;
import com.domain.Account;
import com.service.IAccountService;
import java.util.List;
/**
* Created with IntelliJ IDEA.
*
* @author : gxm
* @date : 2020/5/5
* @time : 21:07
* @projectName : spring-account
* @description :
* To change this template use File | Settings | File Templates.
* Description:
**/
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao;
@Override
public void transfer(Account account1, Account account2,Float money) {
System.out.println("AccountServiceImpl");
// 1. 获取账户1的信息
List<Account> accounts1 = queryById(account1.getId());
// 2. 获取账户2的信息
List<Account> accounts2 = queryById(account2.getId());
// 3. 转出
for (Account account : accounts1) {
account.setBalance(account.getBalance()-money);
}
// 4. 转入
for (Account account : accounts2) {
account.setBalance(account.getBalance()+money);
}
// 5. 更新数据库
accounts1.forEach(this::updateAccount);
int a = 1/0;
accounts2.forEach(this::updateAccount);
}
@Override
public List<Account> queryAll() {
return accountDao.queryAll();
}
@Override
public List<Account> queryById(Integer id) {
return accountDao.queryById(id);
}
@Override
public List<Account> queryByName(String name) {
return accountDao.queryByName(name);
}
@Override
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
@Override
public void updateAccount(Account account) {
accountDao.updateAccount(account);
}
@Override
public void deleteAccountById(Integer id) {
accountDao.deleteAccountById(id);
}
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
}
2.3 配置四个通知实现动态代理支持事务操作
配置前置通知、后置通知、异常通知、最终通知
<!-- 创建aop方法增强 四个通知 -->
<aop:config>
<aop:pointcut id="accountServiceEnhance" expression="execution(* *.*.*.AccountServiceImpl.*(..))"/>
<aop:aspect id="transaction" ref="transactionManager">
<aop:before method="beginTransaction" pointcut-ref="accountServiceEnhance"/>
<aop:after-returning method="commit" pointcut-ref="accountServiceEnhance"/>
<aop:after-throwing method="rollBack" pointcut-ref="accountServiceEnhance"/>
<aop:after method="release" pointcut-ref="accountServiceEnhance"/>
</aop:aspect>
</aop:config>
2.3 配置环绕通知实现动态代理支持事务操作
spring的环绕通知,即类似于动态代理的实现过程,将具体的操作交由用户,前置、后置、异常、最终以及方法的调用均有用户自己操作,用户只需传入一个方法即可。
环绕通知需要实现ProceedJoinPoint
,该接口有一个方法proceed()
,相当于明确调用切入点方法,该接口可以作为环绕通知的方法参数,在程序执行时 spring会为我们提供改接口的实现类供我们使用。
在事务增强类中增加上述要求的方法,代码如下:
/**
* @author gxm
* @date 22:44
* @description 环绕通知,类似于动态代理,手动实现aop过程
* @param joinPoint 1
* @return java.lang.Object
* @since version-1.0
*/
public Object aopAround(ProceedingJoinPoint joinPoint) {
Object proceed = null;
try {
// 1. 前置通知
beginTransaction();
// 2. 环绕通知
proceed = joinPoint.proceed(joinPoint.getArgs());
// 3. 后置通知
commit();
} catch (Throwable throwable) {
// 4. 异常通知
rollBack();
throw new RuntimeException(throwable);
}finally {
// 5. 最终通知
release();
}
return proceed;
}
xml文件的配置,非常简单只需要配置aop以及around即可,具体配置如下:
<!-- 环绕通知 -->
<aop:config>
<aop:pointcut id="accountServiceEnhance" expression="execution(* *.*.*.AccountServiceImpl.*(..))"/>
<aop:aspect id="transaction" ref="transactionManager">
<aop:around method="aopAround" pointcut-ref="accountServiceEnhance"/>
</aop:aspect>
</aop:config>