前言:AspectJ 不是 Spring框架的组成部分,是独立的AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用进行 AOP 操作
一.AOP相关术语
比如某个类中有如下4个方法:
Class A{
add();
delete();
update();
select();
}
1.连接点(JoinPoint)
类A中的4个方法都可能被增强,这4个方法成为连接点
2.切入点(PointCut)
如果我实际只增强 add()和update()方法,则add()和update()称为切入点;
即实际被真正增强方法
3.通知、增强(Advice)
比如我增强add()方法,在add()方法执行之前加上判断代码,这个判断代码就称为通知;
即实际增强的逻辑部分称为通知或增强
通知的五种类型:
前置通知 :在执行add()方法之前有一些逻辑部分后置通知(返回通知):在执行add()方法之后有一些逻辑部分环绕通知:在执行add()方法之前和之后都有一些逻辑部分异常通知:当执行add()方法出现异常时会执行的逻辑部分最终通知:与try-catch-finally中的finally类似,最终一定会执行的逻辑部分
4.切面(Aspect)
切面是一个动作,把我们的通知应用到切入点的过程叫做切面。
比如把判断代码加到add()方法之前的这个过程称为切面。
二.切入点表达式
切入点表达式的作用是知道对哪个类里面的哪个方法进行增强。
1.切入点表达式的格式
execution([权限修饰符][返回值类型][类全路径][方法名称]([参数列表])[异常])
其中返回值类型、方法名称和参数列表是必填的。
2.切入点表达式通配符:
*:匹配所有字符…:一般用于匹配多个包,多个参数+:表示类及其子类
举例 1:对 aop.annotation.Student 类里面的 eat方法进行增强 execution(public void aop.annotation.Student.eat(…))
举例 2:对 aop.annotation.Student 类里面的所有的方法进行增强 execution(* aop.annotation.Student.* (…))
举例 3:对 aop.annotation 包里面所有类,类里面所有方法进行增强 execution(* aop.annotation.. (…))
3.代码示例:
StudentProxy类:
@Component
@Aspect
public class StudentProxy {
//前置通知
@Before(value = "execution(public void aop.annotation.Student.eat(..))")
public void beforeDemo(){
System.out.println("饿了!");
}
//后置通知(返回通知)
@AfterReturning("execution(* aop.annotation.Student.*(..))")
public void afterReturningDemo(){
System.out.println("饭后甜点");
}
//最终通知
@After(value = "execution(void aop.annotation.Student.eat(..))")
public void afterDemo(){
System.out.println("饱了!");
}
//异常通知
@AfterThrowing("execution(void aop.annotation.Student.eat(..))")
public void afterThrowingDemo(){
System.out.println("噎住了!");
}
//环绕通知
@Around("execution(* aop.annotation.Student.*(..))")
public void aroundDemo(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("吃饭前...");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("吃饭后...");
}
}
Student类:
@Component
public class Student {
public void eat(){
System.out.println("eat something!");
}
}
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"
xmlns:aop="http://www.springframework.org/schema/aop"
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 https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启注解扫描-->
<context:component-scan base-package="aop.annotation"></context:component-scan>
<!--开启Aspect生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
测试类:
public class DemoTest {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("aop/annotation/bean.xml");
Student student = context.getBean("student", Student.class);
student.eat();
}
}
输出结果:
吃饭前... 饿了! eat something! 饭后甜点 饱了! 吃饭后... Process finished with exit code 0
更改Student中代码,让其报错,如下:
@Component public class Student {
public void eat(){
int i = 1/0; System.out.println("eat something!"); } }
输出结果:
4.相同切入点抽取
4.相同切入点抽取
pointDemo方法上使用@Pointcut注解,把上述代码中的前置通知的切入点表达式用pointDemo方法替换;
同理其他的切入点表达式都可以用其替换,方便开发。
//相同切入点抽取 @Pointcut(value = "execution(* aop.annotation.Student.eat(..))") public void pointDemo(){
} //前置通知 @Before(value = "pointDemo()") public void beforeDemo(){
System.out.println("饿了!"); }
5.@Order(数字类型值)注解
5.@Order(数字类型值)注解
当有多个增强类对同一个方法进行增强时,可以使用@Order(数字类型值)注解来进行优先级设置,数字类型值越小,优先级越高,越优先执行。
在创建一个StudentOneProxy类对Student类中对eat方法进行增强,设置@Order(2),StudentOneProxy如下:
@Component @Aspect @Order(2) public class StudentOneProxy {
@Before("execution(* aop.annotation.Student.eat(..))") public void BeforeDemo(){
System.out.println("StudentOneProxy.BeforeDemo..."); } @Around("execution(* aop.annotation.Student.eat(..))") public void aroundDemo(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("StudentOneProxy.Around..."); proceedingJoinPoint.proceed(); System.out.println("StudentOneProxy.Around..."); } }
在StudentProxy类上添加@Order(1)注解,截图如下:
输出结果:
吃饭前... 饿了! StudentOneProxy.Around... StudentOneProxy.BeforeDemo... eat something! StudentOneProxy.Around... 饭后甜点 饱了! 吃饭后... Process finished with exit code 0
可以看到设置为@Order(1)的Student类先执行。