第十六讲-切点匹配

第十六讲-切点匹配

1. 基于方法的名字进行切点匹配

前面我们已经了解到,Spring使用切点老判断某个目标方法是否进行功能增强,我们前面也使用了AspectJ表达式来匹配某个方法需要进行功能增强。

那么切点到底是怎么匹配并判断我们设置的方法的呢?其实这个判断来自于AspectJExpressionPoint中的一个方法:matches方法,如下面所示:

package com.cherry.chapter1.a16;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;

public class A16Test {
    private static final Logger log = LoggerFactory.getLogger(A16Test.class);
    public static void main(String[] args) throws NoSuchMethodException {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* bar())");
        System.out.println((pointcut.matches(T1.class.getDeclaredMethod("foo"), T1.class)));
        System.out.println(pointcut.matches(T1.class.getDeclaredMethod("bar"), T2.class));
    }
}

class T1{
    private static final Logger log = LoggerFactory.getLogger(T1.class);
    public void foo() {
        log.debug("T1 foo()...");
    }

    public void bar() {
        log.debug("T1 bar()...");
    }
}

class T2  {
    private static final Logger log = LoggerFactory.getLogger(T2.class);

    public void foo() {
        log.debug("T2 foo()...");
    }

    public void bar() {
        log.debug("T2 bar()...");
    }
}

matches方法有两个参数,第一个参数为目标方法,第二个方法是目标方法所在的类,返回值为布尔类型,如果匹配成功,返回true,反之亦然。

运行如下:

false
true

我们发现,bar()方法匹配到了,而foo()方法并没有匹配的到。

2. 基于方法上的注解进行切点匹配

除了基于方法名字进行切点匹配外,还可以基于方法上的注解进行切点匹配,如下面的例子:

AspectJExpressionPointcut pointcutByAnnotation = new AspectJExpressionPointcut();
// 如果方法上如果有@Transactional注解就会匹配通过
pointcutByAnnotation.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
System.out.println((pointcutByAnnotation.matches(T1.class.getDeclaredMethod("foo"), T1.class)));
System.out.println(pointcutByAnnotation.matches(T1.class.getDeclaredMethod("bar"), T2.class));

运行如下:

true
false

对于标注了@Transactional注解的方法它的底层是不是使用了AspectJExpression配合了@Annotation切点表达式实现的匹配呢?答案不是的。因为在Spring中@Transactional的注解用法有很多种,

  • 除了加在方法上表示被标注的方法需要事务增强;
  • 还可以加在类上,加在类上表明标注了@Transactional的类中的全部方法都需要事务增强。
  • 除了上述两者,如果@Transactional注解标注在接口上,而某一个类实现了该接口,那么该类上的方法也需要事务增强

3. 自定义在方法和类上进行切点匹配

前面我们举的例子都是只能匹配到方法上,不能匹配到类上的注解信息,因此我们就不能用AspectJExpressionPoint这个实现了,我们得换一个接口MethodMatcher接口,但是这个实现类在Spring中没有对外提供访问,因此呢,我们手动模拟实现一下:

package com.cherry.chapter1.a16;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;

public class A16Test {
    private static final Logger log = LoggerFactory.getLogger(A16Test.class);
    public static void main(String[] args) throws NoSuchMethodException {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* bar())");
        System.out.println((pointcut.matches(T1.class.getDeclaredMethod("foo"), T1.class)));
        System.out.println(pointcut.matches(T1.class.getDeclaredMethod("bar"), T2.class));

        System.out.println("======================");

        AspectJExpressionPointcut pointcutByAnnotation = new AspectJExpressionPointcut();
        // 如果方法上如果有@Transactional注解就会匹配通过
        pointcutByAnnotation.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
        System.out.println((pointcutByAnnotation.matches(T1.class.getDeclaredMethod("foo"), T1.class)));
        System.out.println(pointcutByAnnotation.matches(T1.class.getDeclaredMethod("bar"), T2.class));

        System.out.println("<======================>");
        StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {
            @Override   // 进行匹配
            public boolean matches(Method method, Class<?> targetClass) {
                // 首先找到方法上有没有加注解(Spring提供的工具类)
                MergedAnnotations annotations = MergedAnnotations.from(method);
                // 判断注解上有没有@Transactional注解
                if (annotations.isPresent(Transactional.class)) {
                    return true;    //方法上有@Transactional注解返回true
                }
                // 继续在方法所在的类上判断有没有加@Transactional注解
                annotations = MergedAnnotations.from(targetClass);
                if (annotations.isPresent(Transactional.class)) {
                    return true;    //类上有@Transactional注解返回true
                }
                // 类上和方法上都没有@Transactional注解则返回false
                return false;
            }
        };

        // 测试
        System.out.println(pt3.matches(T1.class.getDeclaredMethod("foo"), T1.class));
        System.out.println(pt3.matches(T3.class.getDeclaredMethod("bar"), T3.class));
        System.out.println(pt3.matches(T4.class.getDeclaredMethod("foo"), T4.class));
    }
}




class T1{
    private static final Logger log = LoggerFactory.getLogger(T1.class);
    @Transactional
    public void foo() {
        log.debug("T1 foo()...");
    }

    public void bar() {
        log.debug("T1 bar()...");
    }
}

class T2  {
    private static final Logger log = LoggerFactory.getLogger(T2.class);

    public void foo() {
        log.debug("T2 foo()...");
    }

    public void bar() {
        log.debug("T2 bar()...");
    }
}

@Transactional
class T3 {
    private static final Logger log = LoggerFactory.getLogger(T3.class);

    public void foo() {
        log.debug("T3 foo()...");
    }

    public void bar() {
        log.debug("T3 bar()...");
    }
}

@Transactional
interface I{
    void foo();
}

class T4 implements I{

    @Override
    public void foo() {
        System.out.println("T4 implements I's foo()...");
    }
}

运行如下:

false
true
======================
true
false
<======================>
true
true
false

我们发现住了最后一个其它都成功了,这是为什么呢?其实问题出现在了MergedAnnotations.from(targetClass)方法上,该方法默认是按照传递过来的类对象上查找是否标注了哪些注解,而我们的T4类上确实没有标注@Transactional注解,因此返回false。那么我们有什么办法去查找这个类的父类或者接口有哪些注解呢?其实很简单,我们更改一下搜索注解的策略即可,如下面所示:

MergedAnnotations annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
true

完整代码如下:

package com.cherry.chapter1.a16;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;

public class A16Test {
    private static final Logger log = LoggerFactory.getLogger(A16Test.class);
    public static void main(String[] args) throws NoSuchMethodException {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* bar())");
        System.out.println((pointcut.matches(T1.class.getDeclaredMethod("foo"), T1.class)));
        System.out.println(pointcut.matches(T1.class.getDeclaredMethod("bar"), T2.class));

        System.out.println("======================");

        AspectJExpressionPointcut pointcutByAnnotation = new AspectJExpressionPointcut();
        // 如果方法上如果有@Transactional注解就会匹配通过
        pointcutByAnnotation.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
        System.out.println((pointcutByAnnotation.matches(T1.class.getDeclaredMethod("foo"), T1.class)));
        System.out.println(pointcutByAnnotation.matches(T1.class.getDeclaredMethod("bar"), T2.class));

        System.out.println("<======================>");
        StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {
            @Override   // 进行匹配
            public boolean matches(Method method, Class<?> targetClass) {
                // 首先找到方法上有没有加注解(Spring提供的工具类)
                 MergedAnnotations annotations = MergedAnnotations.from(method);
                // 判断注解上有没有@Transactional注解
                if (annotations.isPresent(Transactional.class)) {
                    return true;    //方法上有@Transactional注解返回true
                }
                // 继续在方法所在的类上判断有没有加@Transactional注解
                // annotations = MergedAnnotations.from(targetClass);
                annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
                if (annotations.isPresent(Transactional.class)) {
                    return true;    //类上有@Transactional注解返回true
                }
                // 类上和方法上都没有@Transactional注解则返回false
                return false;
            }
        };

        // 测试
        System.out.println(pt3.matches(T1.class.getDeclaredMethod("foo"), T1.class));
        System.out.println(pt3.matches(T3.class.getDeclaredMethod("bar"), T3.class));
        System.out.println(pt3.matches(T4.class.getDeclaredMethod("foo"), T4.class));
    }
}




class T1{
    private static final Logger log = LoggerFactory.getLogger(T1.class);
    @Transactional
    public void foo() {
        log.debug("T1 foo()...");
    }

    public void bar() {
        log.debug("T1 bar()...");
    }
}

class T2  {
    private static final Logger log = LoggerFactory.getLogger(T2.class);

    public void foo() {
        log.debug("T2 foo()...");
    }

    public void bar() {
        log.debug("T2 bar()...");
    }
}

@Transactional
class T3 {
    private static final Logger log = LoggerFactory.getLogger(T3.class);

    public void foo() {
        log.debug("T3 foo()...");
    }

    public void bar() {
        log.debug("T3 bar()...");
    }
}

@Transactional
interface I{
    void foo();
}

class T4 implements I{

    @Override
    public void foo() {
        System.out.println("T4 implements I's foo()...");
    }
}

完整的运行结果如下:

false
true
======================
true
false
<======================>
true
true
true
posted @   LilyFlower  阅读(4)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示