基于AspectJ实现AOP操作

前言: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. *:匹配所有字符
  2. …:一般用于匹配多个包,多个参数
  3. +:表示类及其子类

举例 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.相同切入点抽取

pointDemo方法上使用@Pointcut注解,把上述代码中的前置通知的切入点表达式用pointDemo方法替换;
同理其他的切入点表达式都可以用其替换,方便开发。


    //相同切入点抽取
    @Pointcut(value = "execution(* aop.annotation.Student.eat(..))")
    public void pointDemo(){
    
    

    }
    
    //前置通知
    @Before(value = "pointDemo()")
    public void beforeDemo(){
    
    
        System.out.println("饿了!");
    }

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类先执行。

猜你喜欢

转载自blog.csdn.net/MrYushiwen/article/details/111736821