SpringAOP
内容纲要

SpringAOP

AOP (Aspect Orient Programming),直译过来就是 面向切面编程,AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。

AOP可以拦截指定的方法并且对方法增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离,比如Spring的事务,通过事务的注解配置,Spring会自动在业务方法中开启、提交业务,并且在业务处理失败时,执行相应的回滚策略。

AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。

主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。

简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。

核心概念

Spring AOP 的核心概念包括切面、连接点、通知、切点以及织入。下面我将对这些概念做一些简要的解释:

  1. 切面(Aspect):切面是一个模块化的横切关注点实现,它包括了连接点和通知。可以通过配置文件、注解等方式定义切面。
  2. 连接点(Joinpoint):程序中能够被切面插入的点,典型的连接点包括方法调用、方法执行过程中的某个时点等等。
  3. 通知(Advice):在连接点处执行的代码。通知分为各种类型,如前置通知、后置通知、环绕通知等。
  4. 切点(Pointcut):用于定义哪些连接点上应该应用通知。切点通过表达式进行定义,如匹配所有 public 方法或匹配某个包下的所有方法等。
  5. 织入(Weaving):指将切面应用到目标对象并创建新的代理对象的过程。织入可以在运行时完成,也可以在编译时完成。 Spring AOP 提供了两种织入方式:编译期织入和运行期织入。

除此之外,Spring AOP 还有其他常用的概念,如目标对象(Target)、代理对象(Proxy)等。目标对象是含有连接点的对象,而代理对象是 Spring AOP 创建的一个包含切面代码的对象。

如果AOP的环绕通知没定义,前置通知也不会执行。

而且环绕通知(@Around)的返回类型必须和原方法的返回类型相同。

ProceedingJoinPoint类

Proceedingjoinpoint 继承了JoinPoint,在JoinPoint的基础上暴露出 proceed(), 这个方法是AOP代理链执行的方法。

JoinPoint仅能获取相关参数,无法执行连接点。暴露出proceed()这个方法,就能支持 aop:around 这种切面(而其他的几种切面只需要用到JoinPoint,这跟切面类型有关),就能控制走代理链还是走自己拦截的其他逻辑。

:exclamation::注意ProcedingJoinPoint只能用在环绕通知里,其他通知只能用JoinPoint

可能拿不到注解的情况

补充:其实这根本不是拿不拿得到方法注解的问题,而是它拿的其实就是 signature.getClass()的注解,和我们想拿到的方法的注解根本不相关。

直接通过signture拿到的是代理的方法,而代理的方法是不会加上原方法的注解的。

只有拿到原始的方法才能拿到方法上注解的值。(可能取到类上的某些注解)

@Before("cutPoint()")
public void before(JoinPoint joinPoint) throws NoSuchMethodException {
    log.info("===== 前置通知开始");
    String methodName = joinPoint.getSignature().getName();
    log.info("所切方法名:[{}]",methodName);
    Signature signature = joinPoint.getSignature();
    Annotation[] annotations = signature.getClass().getAnnotations();
    boolean hasAn = false;
    for(Annotation a:annotations){
        if(a instanceof MyAnnotation){
            hasAn = true;
        }
        log.info("直接从Signature取注解:[{}]",a);
    }
    if(!hasAn){
        log.info("直接从Signature取注解,取不到自定义注解");
    }
    Class[] parameterTypes = ((MethodSignature) signature).getParameterTypes();
    Method readMethod = joinPoint.getTarget().getClass().getDeclaredMethod(signature.getName(), parameterTypes);
    Annotation[] annotations1 = readMethod.getAnnotations();
    for(Annotation a:annotations1){
        log.info("从原始方法取注解:[{}]",a);
    }
    log.info("===== 前置通知结束");
}

使用Demo

1.导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.Demo

@Aspect
@Slf4j
public class AnnotationAspectj {

    /*
     切入点签名

        1.匹配方法
        表示匹配所有方法
        1)execution(* *(..))
        //表示匹配com.fsx.run.UserService中所有的公有方法
        2)execution(public * com.fsx.run.UserService.*(..))
        //表示匹配com.fsx.run包及其子包下的所有方法
        3)execution(* com.fsx.run..*.*(..))

        2.拦截指定类型的方法
        // AService下面所有外部调用方法,都会拦截。备注:只能是AService的方法,子类不会拦截的
        @Pointcut("within(com.fsx.run.service.AService)")

        3.匹配方法标有指定注解
        // 可以匹配所有方法上标有此注解的方法
        @Pointcut("@annotation(com.fsx.run.anno.MyAnno)")

        4.切入点引用
        // 这个就是一个`reference pointcut`  甚至还可以这样 @Before("point1() && point2()")
        @Before("point()")
        public void before() {
            System.out.println("this is from HelloAspect#before...");
        }
     */
    @Pointcut("@annotation(com.demo.springboot_demo.Annotation.MyAnnotation)")
    void cutPoint(){}

    /**
     * 前置通知
     */
    @Before("cutPoint()")
    public void before(){
        log.info("===== 前置通知");
    }
    /**
     * 环绕通知
     */
    @Around("cutPoint()")
    public Object around(ProceedingJoinPoint joinPoint){
        log.info("===== 环绕通知开始");
        // 所切对象的类
        Class<?> aClass = joinPoint.getTarget().getClass();
        log.info("目标对象:[{}]--[{}]",aClass,aClass.getName());
        // 所切方法名
        String methodName = joinPoint.getSignature().getName();
        log.info("所切方法名:[{}]",methodName);
        StringBuilder sb = new StringBuilder();
        // 所切方法参数
        Object[] args = joinPoint.getArgs();
        for(Object arg:args){
            sb.append(arg);
        }
        log.info("所切方法的传入参数列表:[{}]",sb.toString());
        long start = System.currentTimeMillis();
        Object result = null;
        // 执行目标方法,并获取返回值
        try{
            result = joinPoint.proceed();
        }catch(Exception e){
            e.printStackTrace();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        long end = System.currentTimeMillis();
        log.info("===== 方法执行共用时:[{}]",end -start);

        log.info("===== 环绕通知结束");
        return result;
    }

    /**
     * 后置通知
     */
    @After("cutPoint()")
    public void after(){
        log.info("===== 后置通知开始");
        log.info("===== 后置通知结束");
    }

    /**
     * 返回后通知
     */
    @AfterReturning(value = "cutPoint()",returning = "r")
    public void afterReturin(Object r){
        log.info("===== 返回后通知开始");
        log.info("返回值:[{}]",r);
        log.info("===== 返回后通知结束");
    }

    /**
     * 抛异常后通知
     */
    @AfterThrowing(value = "cutPoint()",throwing = "t")
    public void afterThrow(Throwable t){
        log.info("===== 异常通知开始");
        log.error("异常通知:[{}]",t.getMessage());
        log.info("===== 异常通知结束");
        throw new RuntimeException(t);
    }

}

测试

@Component
public class TestAnnotation {
    TestAnnotation(){}
    @MyAnnotation
    public int test(String str,int start,int end){
        System.out.println("入参"+str+" - "+String.valueOf(start)+ " - "+String.valueOf(end));
        System.out.println("start + end = " + (start - end));
        return start-end;
    }
}

@Test
void test1() {
    String str = "test";
    int start = 10;
    int end = 5;
    testAnnotation.test(str,start,end);
}

测试结果

正常结束

image-20240702191936484

环绕通知@Around -> 前置通知@Before -> 原方法执行 -> 方法执行结束 -> @AfterReturning -> @After

异常结束

image-20240702192233948

@Around -> @Before -> 原方法执行 -> @AfterThrow -> @After

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇