一、pom.xml
<packaging>jar</packaging>
<dependencies>
<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>
</dependencies>
二、bean.xml
这个为使用注解实现aop的xml配置
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<?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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.itheima"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
三、使用注解编写Logger类
1、使用分开的四个通知类型分别实现前、后、最终、异常通知
@Aspect为表示这个类为一个切面
这里使用两种方式写表达式
一种是像@After后面一样直接填写表达式
另一种就是先写一个pt1()方法,在上面注上@Pointcut,里面填写表达式
在引用Pointcut注解的时候,比如@Before,要注意是pt1(括号),括号不能省略,否则会报切入点表达式错误
@Component("logger")
@Aspect
public class Logger {
@Pointcut("execution(* com.itheima.service.impl.*.*(..))")
public void pt1(){}
@Before("pt1()")
public void beginLog(){
System.out.println("开始执行方法");
}
@After("execution(* com.itheima.service.impl.*.*(..))")
public void endLog(){
System.out.println("方法执行结束(最终)");
}
@AfterReturning("pt1()")
public void successLog(){
System.out.println("方法执行成功(执行完方法后就调用)");
}
@AfterThrowing("pt1()")
public void exceptionLog(){
System.out.println("方法执行异常");
}
}
四、Client类
只调用saveAccount方法来测试
public class Client {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService as = ac.getBean("accountService",IAccountService.class);
as.saveAccount();
}
}
运行结果:
开始执行方法
新建账户
方法执行结束(最终)
方法执行成功(执行完方法后就调用)
notice:
这里有一个发现,就是我们使用spring的几个通知使用注解分开编写(即不使用环绕通知)的时候,先执行最终通知,再执行执行成功,(相当于先执行了finally,再执行方法执行完的操作,我们平常是先执行方法执行完的操作,最后再执行finally)是存在一个顺序上的问题的,比如我们在最终方法需要关闭一个流,而执行成功的方法又需要这个流来操作,按照这个顺序,那么就会出现问题,因为正常执行完所有操作后,再执行最终操作的。
而使用环绕通知则没有这个问题:
使用环绕通知注解:
@Around("execution(* com.itheima.service.impl.*.*(..))")
public Object arroundLogger(ProceedingJoinPoint pdj){
Object rtValue = null;
try {
Object args[] = pdj.getArgs();
System.out.println("前置方法执行");
rtValue=pdj.proceed(args);
System.out.println("方法执行成功(执行完方法就调用)");
return rtValue;
} catch (Throwable throwable) {
System.out.println("异常方法执行");
throwable.printStackTrace();
}finally {
System.out.println("最终方法执行");
}
return rtValue;
}
执行顺序没有问题
前置方法执行
新建账户
方法执行成功(执行完方法就调用)
最终方法执行
因此更推荐使用环绕通知