相关注解:
- 切入点(@Pointcut): 程序中有着多个连接点,切入点用来匹配相对应的带有通知的连接点,一个切入点可以匹配多个连接点。
- 切面(@Aspect): 通常是一个类,由切点和增强组成。
- 前置通知(@Before): 在目标方法运行之前运行
- 后置通知(@AfterReturning): 在我们的目标方法正常返回值后运行
- 异常通知(@AfterThrowing): 在我们的目标方法出现异常后运行
- 最终通知(@After): 在目标方法运行结束之后 ,不管有没有异常
- 环绕通知(@Around): 是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方法
项目结构:
创建maven工程,在pom.xml文件中添加相关依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
配置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">
<!--配置spring创建容器时要扫描的包-->
<context:component-scan base-package="com.ly"></context:component-scan>
<!--配置spring开启注解AOP的支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
配置表现层的接口以及实体类:
/**
* @Author: Ly
* @Date: 2020-08-02 22:14
*/
public interface IAccountService {
/**
* 模拟保存账户
*/
void saveAccount();
/**
* 模拟修改账户
* @param i
*/
void updateAccount(int i);
/**
* 模拟删除账户
* @return
*/
int deleteAccount();
}
/**
* @Author: Ly
* @Date: 2020-08-02 22:17
*/
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
public void saveAccount() {
System.out.println("执行了保存");
}
public void updateAccount(int i) {
System.out.println("执行力修改");
}
public int deleteAccount() {
System.out.println("执行了删除");
return 0;
}
}
配置通知类及相关方法:
/**
* @Author: Ly
* @Date: 2020-08-02 22:19
*/
@Component("logger")
@Aspect//表示当前类是一个切面
public class Logger {
@Pointcut("execution(public void com.ly.service.impl.*.*(..))")
private void pt1(){};
/**
* 前置通知
*/
@Before("pt1()")
public void beforePrintLog(){
System.out.println("前置Logger类中的printLog方法开始记录日志了");
}
/**
* 后置通知
*/
@AfterReturning("pt1()")
public void afterReturningPrintLog(){
System.out.println("后置Logger类中的printLog方法开始记录日志了");
}
/**
* 异常通知
*/
@AfterThrowing("pt1()")
public void afterThrowPrintLog(){
System.out.println("异常Logger类中的printLog方法开始记录日志了");
}
/**
* 最终通知
*/
@After("pt1()")
public void afterPrintLog(){
System.out.println("最终Logger类中的printLog方法开始记录日志了");
}
/**
* 环绕通知
* 问题:当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了
* 分析:通过对比动态代理中国的环绕通知代码,发现动态代理的环绕通知有明确的切入点调用,而我们的代码中没有
* 解决:Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法
* 该接口可以作为环绕通知的方法参数,在程序执行时,spring矿建会为我们提供该接口的实现类供我们使用
* spring中环绕通知:
* 它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方法
*/
//@Around("pt1()")
public Object aroundPrintLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try {
Object[] args=pjp.getArgs();//得到方法执行所需的参数
System.out.println("前Logger类中的aroundPrintLog方法开始记录日志了");
rtValue=pjp.proceed(args);//明确调用业务层方法(切入点方法)
System.out.println("后Logger类中的aroundPrintLog方法开始记录日志了");
return rtValue;
} catch (Throwable e) {
System.out.println("异常Logger类中的aroundPrintLog方法开始记录日志了");
throw new RuntimeException(e);
}finally {
System.out.println("最终Logger类中的aroundPrintLog方法开始记录日志了");
}
}
}
/**
* @Author: Ly
* @Date: 2020-08-03 16:57
* 测试AOP的配置
*/
public class AOPTest {
public static void main(String[] args) {
//1.获取容器
ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
//2.获取对象
IAccountService as=(IAccountService)ac.getBean("accountService");
//3.执行方法
as.saveAccount();
}
}
运行结果: