基于注解的 AOP 配置
以一个 Logger 工具类和 账户转账 作为示例
环境搭建
第一步:准备必要的代码
一个 AccountService 接口
/**
* 账户的业务层接口
*/
public interface AccountService {
/**
* 模拟保存账户
*/
void saveAccount();
/**
* 模拟更新账户
* @param id
*/
void updateAccountById(int id);
/**
* 模拟删除账户
* @return
*/
int deleteAccountById();
}
一个 AccountServiceImpl 实现了实现 AccountService 接口
/**
* 账户业务层实现类
*/
public class AccountServiceImpl implements AccountService {
public void saveAccount() {
System.out.println("保存账户");
}
public void updateAccountById(int id) {
System.out.println("更新账户");
}
public int deleteAccountById() {
System.out.println("删除账户");
return 0;
}
}
一个logger工具类
/**
* 用于记录日志的工具类,它里面提供了公共代码
*/
public class Logger {
/**
* 前置通知
*/
public void beforePrintLog(){
System.out.println("前置通知 logger类中的printLog方法开始记录日志");
}
/**
* 后置通知
*/
public void afterReturnPrintLog(){
System.out.println("后置通知 logger类中的printLog方法开始记录日志");
}
/**
* 异常通知
*/
public void afterThrowingPrintLog(){
System.out.println("异常通知 logger类中的printLog方法开始记录日志");
}
/**
* 最终通知
*/
public void afterPrintLog(){
System.out.println("最终通知 logger类中的printLog方法开始记录日志");
}
/**
* 环绕通知
*/
public void afterPrintLog(){
System.out.println("环绕通知 logger类中的printLog方法开始记录日志");
}
第二步:引入 jar包 创建 spring 的配置文件并导入约束
引入 jar 包
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
</dependencies>
创建 bean.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:aop="http://www.springframework.org/schema/aop"
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/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">
</beans>
第三步:把资源使用注解配置
AccountService
/**
* 账户业务层实现类
*/
@Service("accountService")
public class AccountServiceImpl implements AccountService {
...
省略中间重复内容
...
}
第四步:在配置文件中指定 spring 要扫描的包
<!-- 配置spring创建容器时要扫描的包 -->
<context:component-scan base-package="cn.itcast"></context:component-scan>
配置步骤
第一步:在 spring 配置文件中开启注解 AOP 的支持
<!-- 配置spring开启注解AOP的支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
第二步:把 Logger 类也使用注解配置
/**
* 用于记录日志的工具类,它里面提供了公共代码
*/
@Component("logger")
public class Logger {
...
省略中间重复内容
...
}
第三步:在通知类上使用@Aspect 注解声明为切面
作用: 把当前类声明为切面类
@Component("logger")
@Aspect
public class Logger {
...
省略中间重复内容
...
}
第四步:在增强的方法上使用注解配置通知
/**
* 用于记录日志的工具类,它里面提供了公共代码
*/
@Component("logger")
@Aspect
public class Logger {
@Pointcut("execution(* cn.itcast.service.impl.*.*(..))")
public void pt1(){};
/**
* 前置通知
*/
@Before("pt1()")
public void beforePrintLog(){
System.out.println("前置通知 logger类中的printLog方法开始记录日志");
}
/**
* 后置通知
*/
@AfterReturning("pt1()")
public void afterReturnPrintLog(){
System.out.println("后置通知 logger类中的printLog方法开始记录日志");
}
/**
* 异常通知
*/
@AfterThrowing("pt1()")
public void afterThrowingPrintLog(){
System.out.println("异常通知 logger类中的printLog方法开始记录日志");
}
/**
* 最终通知
*/
@After("pt1()")
public void afterPrintLog(){
System.out.println("最终通知 logger类中的printLog方法开始记录日志");
}
}
环绕通知注解配置
问题:
当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了
分析:
通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有
解决:
spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法
该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用
@Pointcut("execution(* cn.itcast.service.impl.*.*(..))")
public void pt1(){};
@Around("pt1()")
public Object aroundPrintLog(ProceedingJoinPoint pjp){
Object retrunValue= null;
try {
System.out.println("环绕通知 logger类中的printLog方法开始记录日志 前置通知");
Object[] args = pjp.getArgs();
retrunValue = pjp.proceed(); //明确调用业务层方法(切入点方法)
System.out.println("环绕通知 logger类中的printLog方法开始记录日志 后置通知");
} catch (Throwable throwable) {
System.out.println("环绕通知 logger类中的printLog方法开始记录日志 异常通知");
throwable.printStackTrace();
} finally {
System.out.println("环绕通知 logger类中的printLog方法开始记录日志 最终通知");
}
return retrunValue;
}