前言

进行Spring AOP编程,首先要声明一个切面,里面包含连接点和通知。然后把连接点作用到通知上面去,通知方法内部就是通知的逻辑。这个流程已经在博客【详解什么是Spring AOP】中给出过,而且在里面详细的解释了,什么是AOP。但是其中还是有很多可以深入了解的部分,比如我们这次题目中的@PointCut注解就是一个,所以我们直接走去官网看下@PointCut是怎么说的。更多Spring内容进入【Spring解读系列目录】。

切点声明的表达式

要了解切点都有哪些表达式,最好的地方就是官网【Supported Pointcut Designators】以下英文内容摘自官网。从官网上我们知道@PointCut 一共有execution、within、this、target、args、@target、@args、@within和@annotation这9个,我们重点关注execution、within、this、target、args前五个,因为后面的注解和前面的功能上是一样的。

Spring AOP supports the following AspectJ pointcut designators (PCD) for use in pointcut expressions:
• execution: For matching method execution join points. This is the primary pointcut designator to use when working with Spring AOP.
• within: Limits matching to join points within certain types (the execution of a method declared within a matching type when using Spring AOP).
• this: Limits matching to join points (the execution of methods when using Spring AOP) where the bean reference (Spring AOP proxy) is an instance of the given type.
• target: Limits matching to join points (the execution of methods when using Spring AOP) where the target object (application object being proxied) is an instance of the given type.
• args: Limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types.
• @target: Limits matching to join points (the execution of methods when using Spring AOP) where the class of the executing object has an annotation of the given type.
• @args: Limits matching to join points (the execution of methods when using Spring AOP) where the runtime type of the actual arguments passed have annotations of the given types.
• @within: Limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP).
• @annotation: Limits matching to join points where the subject of the join point (the method being executed in Spring AOP) has the given annotation.

在解释这些方法之前,我们先写一个小例子,要引入的maven依赖和配置类请参考【详解什么是Spring AOP】。首先要有一个切面,包含一个切点和一个通知。

@Component
@Aspect
public class DemoAspect {//这里的意思是使用execution匹配com.demo.dao包下的所有类的所有方法@Pointcut("execution(* com.demo.dao.*.*(..))") public void myPointcutExecution(){}@Before("myPointcutExecution()")public void beforeDao(){System.out.println("before print");}
}

然后我们有一个接口TargetDao和一个实现类DemoDao,我们的验证就基于这些打印。

public interface TargetDao{public void print();public void print(String a);public void print(Integer a);
}
@Repository("demodao")
public class DemoDao implements TargetDao{public void print(){ System.out.println("print null"); }public void print(String print){ System.out.println("print String"); }public void print(Integer print){  System.out.println("print Integer"); }
}

最后我们写一个测试入口DemoTest。

public class DemoTest {public static void main(String[] args) {AnnotationConfigApplicationContext anno=new AnnotationConfigApplicationContext(Appconfig.class);TargetDao dao = (TargetDao) anno.getBean("demodao");dao.print(1);dao.print();dao.print("1");}
}

我们直接运行,就会打印这六句话。。那么下面就先解释我们例子中使用的execution是干嘛的,以及怎么用的。

before print     -----> beforeDao()输出
print Integer       -----> print(Integer a)方法输出
before print        -----> beforeDao()输出
print null          -----> print()方法输出
before print        -----> beforeDao()输出
print String        -----> print(String a)方法输出

execution

execution:用于匹配方法执行 join points连接点。官网同时也说execution是Spring AOP主要的(primary)切点指定器。就是说大多数时候用这个就能搞定。"execution"非常的强大,它可以精确的控制到最小粒度。能够控制修饰符、返回类型、声明的类型、包名、类名、方法名字、参数、抛出的异常等等,想匹配什么就匹配什么。下面就是官方给的匹配模式,这里问号表示当前项可以有也可以没有,我们还是按照例子中的execution(* com.demo.dao.*.*(..))对每一个参数的语义进行解读。

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

[modifiers-pattern]:方法的可见性,如public,protected,private等等,省略不加这个的话,就代表所有的方法:
execution(private * com.demo.dao.*.*(..)):就是只匹配私有方法。
execution(* com.demo.dao.*.*(..)):把private删掉,就是匹配全部的方法。

[ret-type-pattern]:方法的返回值类型,如int,void等,也可以用*代替,就是所有的返回类型:
execution(String com.demo.dao.*.*(..)):匹配返回类型为String的方法。
execution(* com.demo.dao.*.*(..)):所有返回类型的方法都匹配,包括void。

[declaring-type-pattern]:方法所在类的全路径名,如com.demo.dao.SomeClass,如匹配包下所有的类就是com.demo.dao.*:
execution(String com.demo.dao.*.*(..)):匹配com.demo.dao包下的所有类的所有方法。
execution(String com.demo.dao.SomeClass.*(..)):匹配com.demo.dao包下的SomeClass类的所有方法。

[name-pattern]:方法名类型,如test(),或者用*表示代表所有方法:
execution(* com.demo.dao.SomeClass.test(..)):只匹配test方法。
execution(* com.demo.dao.SomeClass.*(..)):匹配SomeClass下面的所有方法。

[param-pattern]:方法的参数类型,如匹配java.lang.String:
execution(* com.demo.dao.SomeClass.test(String)):只匹配一个String参数
execution(* com.demo.dao.SomeClass.test(..)):匹配所有参数
execution(* com.demo.dao.SomeClass.test(String,..)):甚至只匹配第一个是String参数的方法。

[throws-pattern]:方法抛出的异常类型,如java.lang.Exception;
execution(private * com.demo.dao.*.*(..) throws Exception):匹配dao包下的所有抛出的Exception,也是可选的能够省略。

within

within:限定匹配一个确定的类型中的连接点,换成人话就是能匹配确定类中的方法。也就是within的最小粒度只能定义到一个类,或者一个包下的所有类的方法。Spring文档里所说的type就是指类,不要被它的术语搞糊涂了。所以我们可以理解within就是execution的一个辅助,用来简化我们写代码用的,但是有一点要注意,within不能够匹配接口,只能匹配目标类。比如我们在DemoAspect类中追加一个Pointcut的within方法,然后把通知指向这个方法,那么这个效果是一样的,同样会打印出六句话before print、print Integer、before print、print null、before print和print String。

@Pointcut("within(com.demo.dao.*)")
public void myPointcutWithin(){ }@Before("myPointcutWithin()")
public void beforeDao(){ System.out.println("before print"); }

args

args:限定匹配连接点,其中参数是执行类型的实例。基本上也是听不懂,那么我们看一个官网的例子是怎么定义的。

args(java.io.Serializable)

一样不好理解,我们换一个好理解的例子 @Pointcut("args(java.lang.Integer)")。其实args就是在匹配传参为Integer的所有方法。它匹配指定参数类型和指定参数数量的方法,与包名和类名无关。这个例子里只要满足传入的参数是Integer型,一旦调用起来,都会被匹配到。

@Pointcut("args(java.lang.Integer)")
public void myPointcutArgs(){ }@Before("myPointcutArgs()")
public void beforeDao(){ System.out.println("before print"); }

如果是使用args,那么打印出来的内容就只有4句话。因为我们限定了匹配Integer型的参数,所以只有print(Integer a)之前的beforeDao()方法输出了,其他都没有被匹配。

before print     -----> beforeDao()输出
print Integer       -----> print(Integer a)方法输出
print null          -----> print()方法输出
print String        -----> print(String a)方法输出

this

this:限定匹配连接点,其中bean的引用是一个指定类的实例,换句话说就是匹配代理对象。也就是说当代理对象这个类型的时候就去执行,这个this就是当前生成的代理对象。

target

target:限定匹配连接点,其中目标对象是一个指定类的实例。也就说目标对象等于这个接口的时候就去执行。目标对象就是原对象永远不会变。

这两个的概念涉及到了动态代理的底层原理了,所以我们要放在一起讲,同样也是添加新的Pointcut方法。至于代理对象和目标对象的详细解释,可以参考【详解什么是Spring AOP】。

@Pointcut("this(com.demo.dao.DemoDao)") //this表示的是代理对象
public void myPointcutThis(){ }@Pointcut("target(com.demo.dao.DemoDao)") //target表示的是目标对象
public void myPointcutTarget(){ }

如果我们按照现在的例子,直接让通知使用this。

@Before("myPointcutThis()")
public void beforeDao(){ System.out.println("before print"); }

那么打印的结果就是只有三行,什么都没有匹配到。这是因为在JDK原生的动态代理中,代理对象和原对象不是一个对象。在我们的例子中DemoDao是TargetDao的实现,因此如果直接使用DemoDao就相当于直接使用了目标对象。而在JDK原生动态代理里面生成的DemoDao将会extend Proxy然后再implement TargetDao。所以由JDK生成的代理对象只能识别Proxy和TargetDao这两个类,因此我们想要命中DemoDao的时候就会出现一个也命中不了的局面。

print Integer        -----> print(Integer a)方法输出
print null          -----> print()方法输出
print String        -----> print(String a)方法输出

因此如果想要命中的话,有三种修改的方法,三种根据场景取其一即可:

第一:修改this为真正的动态代理对象类,也就是TargetDao。此时因为DemoDao实现了TargetDao这个代理,所以方法的执行也会交给DemoDao这个类中的方法去执行。

@Pointcut("this(com.demo.dao.TargetDao)") //this获取真正的代理对象
public void myPointcutThis(){ }

第二:修改通知方法的匹配为target语句执行,此时我们用的就是目标对象,也就是原对象,所以DemoDao自然可以被匹配到。

@Before("myPointcutTarget()")
public void beforeDao(){ System.out.println("before print"); }

第三:去Appconfig的配置类中加入@EnableAspectJAutoProxy(proxyTargetClass = true),把动态代理的执行交给CGLIB,因为CGLIB的动态代理是基于继承做的,所以这里TargetDao和DemoDao可以被看作一个对象,因此也能匹配到。

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan("com")
public class Appconfig {}

逻辑运算表达式的用法

除了这些以外,我们还可以使用逻辑符号对匹配使用逻辑运算,官网给出了可以用与&&, 或||和非!这三种表达式来完成我们更进一步的需求,比如我们想要匹配dao包下面的所有方法,但是传入Integer参数的除外,就可以在通知上这样写:

@Before("myPointcutExecution()&&!myPointcutArgs()")
public void beforeDao(){ System.out.println("before print"); }
运行结果:只有Integer前面的没有输出,因为被屏蔽了
print Integer       -----> print(Integer a)方法输出
before print        -----> beforeDao()输出
print null          -----> print()方法输出
before print        -----> beforeDao()输出
print String        -----> print(String a)方法输出

加注解的语义

我们已经详细的解释了execution、within、this、target、args这个五个常用的语义,其实最常用的就只有一个execution,而这些带有注解的@target、@args、@within和@annotation四个,其实是Spring给我们提供的自定义的部分。随便找@annotation举个例子。我们先创建一个注解类:

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno { }

然后给DemoDao中的print(Integer print)加上标注。就意味着我们讲只对这一个方法做匹配。

@Repository("demodao")
public class DemoDao implements TargetDao{public void print(){ System.out.println("print null"); }public void print(String print){ System.out.println("print String"); }@MyAnno   //只对这个方法做匹配public void print(Integer print){ System.out.println("print Integer"); }
}

最后返回AspectDemo类里面添加一个@annotation的切点方法,并且加上通知。

@Pointcut("@annotation(com.demo.anno.MyAnno)") //annotation表示的是目标对象
public void myPointcutAnno(){  }@Before("myPointcutAnno()")
public void beforeDao(){System.out.println("before print");
}运行结果和预期一样,只有print(Integer a)前面的before()方法输出了:
before print        -----> beforeDao()输出
print Integer       -----> print(Integer a)方法输出
print null          -----> print()方法输出
print String        -----> print(String a)方法输出

为什么我说自定义呢,因为这些有@注解的语义就是让用户自己定义内容的,如果要用@target,那就在DemoDao类上做注解。

@Repository("demodao")
@MyAnno   //只对这个类做匹配
public class DemoDao implements TargetDao{public void print(){ System.out.println("print null"); }public void print(String print){ System.out.println("print String"); }public void print(Integer print){ System.out.println("print Integer"); }
}

AspectDemo类里面添加一个@target的切点方法,并且加上通知。

@Pointcut("@target(com.demo.anno.MyAnno)")
public void myPointcutAnno(){ }@Before("myPointcutAnno()")
public void beforeDao(){System.out.println("before print");
}运行结果,这里就是把定义的DemoDao里面的所有方法输出了:
before print        -----> beforeDao()输出
print Integer       -----> print(Integer a)方法输出
before print        -----> beforeDao()输出
print null          -----> print()方法输出
before print        -----> beforeDao()输出
print String        -----> print(String a)方法输出

@args无非就是自己定义的类型,@within也是自己定义的包中的类,一样的道理就不占篇幅了。

附 切点的表达式

关于切点的位置,官方文档给的非常之详细,这里为了方便大家搬运过来。以下全部可以放在@Pointcut(" ")里面。当然以下摘自官方文档。

The following examples show some common pointcut expressions:
• The execution of any public method:
执行所有public方法
execution(public * *(..))• The execution of any method with a name that begins with set:
执行所有的set命名开头的方法,这个不一定是set,也可以自己命名,比如我写的小例子中就可以这样@Pointcut("execution(* prin*(..))"),一样效果。
execution(* set*(..))• The execution of any method defined by the AccountService interface:
执行在AccountService 接口中定义的所有方法,也是支持自定义。当然这里要加上了包名才能识别位置。
execution(* com.xyz.service.AccountService.*(..))• The execution of any method defined in the service package:
执行在com.xyz.service包下的所有方法,就是我们例子里用的。
execution(* com.xyz.service.*.*(..))• The execution of any method defined in the service package or one of its sub-packages:
执行定义在service报下或者它的一个子包下的所有方法。
execution(* com.xyz.service..*.*(..))• Any join point (method execution only in Spring AOP) within the service package:
执行在service包下的所有连接点,方法的执行只能在SpringAOP框架内
within(com.xyz.service.*)• Any join point (method execution only in Spring AOP) within the service package or one of its sub-packages:
执行在service包或者其子包下的所有连接点,方法的执行只能在SpringAOP框架内
within(com.xyz.service..*)• Any join point (method execution only in Spring AOP) where the proxy implements the AccountService interface:
执行所有那些用代理实现的AccountService 接口的连接点。
this(com.xyz.service.AccountService)• Any join point (method execution only in Spring AOP) where the target object implements the AccountService interface:
执行所有那些用目标实现的AccountService 接口的连接点。
target(com.xyz.service.AccountService)• Any join point (method execution only in Spring AOP) that takes a single parameter and where the argument passed at runtime is Serializable:
对某一个类型的参数执行匹配。
args(java.io.Serializable)• Any join point (method execution only in Spring AOP) where the target object has a @Transactional annotation:
对于任何连接点的目标对象有一个Transactional(自定义)注解的。
@target(org.springframework.transaction.annotation.Transactional)• Any join point (method execution only in Spring AOP) where the declared type of the target object has an @Transactional annotation:
对于任何连接点的目标对象的声明类型具有Transactional(自定义)注解的。
@within(org.springframework.transaction.annotation.Transactional)• Any join point (method execution only in Spring AOP) where the executing method has an @Transactional annotation:
对于任何连接点的执行方法具有Transactional(自定义)注解的。
@annotation(org.springframework.transaction.annotation.Transactional)• Any join point (method execution only in Spring AOP) which takes a single parameter, and where the runtime type of the argument passed has the @Classified annotation:
对于任何具有单个参数的连接点,其运行时传递的参数类型有Classified(自定义)注解的。
@args(com.xyz.security.Classified)• Any join point (method execution only in Spring AOP) on a Spring bean named tradeService:
匹配任意在tradeService这个bean下的连接点。
bean(tradeService)• Any join point (method execution only in Spring AOP) on Spring beans having names that match the wildcard expression *Service:
广泛的匹配Spring bean有Service后缀的连接点。
bean(*Service)

SpringAOP @PointCut 切点解析相关推荐

  1. spring-aop常用切点表达式

    AOP是spring的最重要模块之一,关于AOP的原理,主要就是基于动态代理,可以查看官网Understanding AOP Proxies,本节内容不去深究AOP原理,仅仅列出在spring框架中编 ...

  2. Spring-AOP @AspectJ切点函数导读

    概述 annotation execution args和args within within和target target和this 概述 切点函数是AspectJ表达式语言的核心,也是使用@Aspe ...

  3. PointCut切点表达式

    一.切入点(切点)的作用 切点告知AOP当前切点在哪类的哪些方法上生效 PointCut表达式规定应用范围 二.PointCut切点表达式参数详解 public可以被省略,因为调用的目标方法默认都是p ...

  4. [Done]Spring @Pointcut 切点调用不到(SpringAOP嵌套方法不起作用) 注意事项

    今天在开发过程中,遇到一个问题卡了很久,测试代码如下: package spring.pointcut;import org.aspectj.lang.ProceedingJoinPoint; imp ...

  5. SpringAOP源码解析总结

    问题一.何时生成代理bean?? 在getBean()过程中,进行了bean的实例化.依赖注入.初始化后,通过bean后处理器进行生成代理类. 在getBean()过程中的AbstractAutowi ...

  6. 死磕Spring系列:SpringAOP源码解析

    1. 术语 在SpringAOP中,有很多专业术语,已经了解的小伙伴可以略过,不了解的朋友就需要注意看一下啦,不然会很懵的哟. Aspect(切面) 跨多个类的关注点的模块化,与垂直调用的业务树不同, ...

  7. Spring-AOP @AspectJ切点函数之target()和this()

    文章目录 概述 实例 target() this() 概述 target()切点函数通过判断目标类是否按类型匹配指定类来决定连接点是否匹配. 用于匹配当前目标对象类型的执行方法:注意是目标对象的类型匹 ...

  8. Spring-AOP @AspectJ切点函数之@within()和@target

    文章目录 概述 @target(M)的匹配规则 @within(M)的匹配规则 实例 @target @within 注意事项 概述 除了@annotation和@args外,还有另外两个用于注解的切 ...

  9. Spring-AOP @AspectJ切点函数之within()

    概述 语法 实例 withincomxgjNaiveWaiter withincomxgj withincomxgj withincomxgjMark 概述 通过类匹配模式串声明切点,within() ...

最新文章

  1. Snowball 关系提取,2篇知乎博客
  2. 网站如何获得优质链接
  3. 去分库分表的亿级数据NewSQL实践之旅
  4. 12v电量显示制作方法_如何制作老式12v充电器(四款12v充电器设计制作详解)
  5. 如何通便清肠快速见效_如何三个月合理瘦身减脂
  6. java ldap 分页_具有从属引用的 LDAP 分页查询未正确处理
  7. 滴水穿石-07Java开发中常见的错误
  8. DHCP服务器--红色箭头
  9. 区块链研习 | 区块链的能力很大又很小
  10. bzoj 1833: [ZJOI2010]count 数字计数(数字0-9的个数)
  11. 迅为IMX6ULL开发板Linux下电容触摸屏实验-实验程序编写
  12. smtp邮件服务器的作用,smtp服务器是什么意思(smtp服务器作用及使用指南)
  13. 小甲鱼 C语言 24课 指针和二维数组
  14. html代码中数学公式,html中使用mathjax数学公式
  15. fft算法的c语言实现dsp,基于DSP的FFT算法实现.doc
  16. const、*、的大乱斗
  17. QT项目实战之翻金币小游戏
  18. 笔记本性能参数有哪些
  19. 计算机考研340分什么概念,考研340分什么概念_中国考研网官网入口
  20. 数据结构-中序遍历二叉树(基于C++)

热门文章

  1. 2K分辨率有多高清 手机是否需要2K屏幕
  2. Spring IOC工作原理
  3. Install phpbb in godday.
  4. element 表头添加斜线
  5. Docker内运行ROS(melodic版本)以及使用Rviz
  6. Docker——更新至2021/3/2,腾讯云centos7-dockermysql远程连接
  7. apache通过.htaccess(rewrite)判断手机电脑跳转-手机用户重定向到手机版
  8. java反射getmethod_Java反射方法
  9. R语言使用原生包(基础导入包、graphics)中的plot函数可视化散点图(scatter plot)
  10. 设置子控件相对于父控件的相对位置