第十六讲-切点匹配
第十六讲-切点匹配
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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .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语句:使用策略模式优化代码结构