作者简介:☕️大家好,我是Aomsir,一个爱折腾的开发者!
个人主页:Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客
当前专栏:Spring5应用专栏_Aomsir的博客-CSDN博客
文章目录
- 前言
- 切入点详解
- 切入点表达式
- 方法切入点表达式
- 注意点
- 类切入点表达式
- 包切入点表达式
- 切入点函数
- execution函数
- args函数
- within函数
- @annotation函数
- 切入点函数逻辑运算
- and
- or
- 总结
- 参考文献
前言
在前面的几篇文章里,我们已经探讨了AOP的四步编程以及Spring所提供的额外方法接口。今天,我们要探讨的是另一个核心概念—— 切入点。切入点决定了在何处插入这些额外的功能。但如之前文章所述,我们的切入点表达式相当粗糙,将额外的功能直接加入了所有类和方法中。这种做法明显不够精细,因为并不是每个原始方法都需要加入如日志、性能监测之类的额外功能
切入点详解
切入点就是这样的execution(* * (..))
,这个切入点表达式就是将额外功能加在所有的原始方法上。这个切入点里面它里面有切入点函数和切入点表达式
切入点表达式
方法切入点表达式
切入点表达式是AOP中一个关键的概念,用于定义哪些方法需要加入额外的功能。理解其语法和含义对于编写精确的切入点至关重要,如下图结合描述
- 第一个* : 它代表
返回值类型(包括修饰符)
。使用*意味着匹配所有的返回值类型。如果你只想为返回int或void的方法添加额外的功能,你可以相应地替换它。 - 第二个* : 这代表
方法名(包括包、类)
。使用*会匹配类中的所有方法。如果你想特定某个方法(如login)加入额外的功能,那么你可以替换它为login。 - 括号中的 …: 这部分代表
参数列表
。…表示匹配任意数量、任意类型的参数。如果你想特定某种参数类型和数量的方法加入额外功能,如login(String, String)
,那么可以明确地指出这两个参数的类型。
然后我为public boolean login(String name, String password)
方法加入额外功能,切入点表达式就是boolean login(String, String)
。测试结果也验证了这一点:只有login方法被增强,而register方法并没有被影响。这也说明了切入点表达式的强大和灵活性,使我们能够精确地控制哪些方法需要被增强,而哪些不需要
<aop:config>
<!--定义切入点,将额外方法加在所有类的方法上-->
<aop:pointcut id="pc" expression="execution(boolean login (String, String))"/>
<!--组装,将额外功能与切入点进行组装整合-->
<aop:advisor advice-ref="arround" pointcut-ref="pc" />
</aop:config>
public class TestProxy {
@Test
public void test2() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.login("Aomsir", "123456");
userService.register(new User());
}
}
注意点
- 方法名写成
find*
代表加在所有方法名以find开头的方法上 - 返回值以及方法参数类型如果不在
java.lang
下则需要写全类名 - 参数类型写成
(String, ..)
代表第一个参数必须为String,后面的参数不作要求 public * *(..)
代表加在所有类的public修饰的方法上
类切入点表达式
级别的切入点可以看作是针对方法的切入点的一种扩展。它的表达方式非常直观,例如,* com.aomsir.service.UserService.*(..)
。这个表达式指向com.aomsir.service.UserService这个类中的所有方法,不论它们的返回类型和参数是什么。换句话说,这个类中的每一个方法都将加入额外功能。
如果我们稍作变动,·* *.UserService.*(..)
,则目标变为了项目中任意一级包下名为UserService的类的所有方法。而表达式* *..UserService.*(..)
则更为通用,它针对项目中的任意包(及其子包)下名为UserService的类的所有方法。
这些表达式的灵活性为我们提供了在不同层级和范围内插入额外功能的能力,使得我们可以更精确地控制何处加入这些功能
包切入点表达式
包切入点,正如其名,是专门用来定位特定包中的类和方法。实际上,我们在之前的类切入点描述中已经接触过它。通过包切入点,我们可以更精确地决定在哪些包中的类或方法上添加额外的功能。
例如,使用* com.aomsir.service.*.*(..)
这个表达式,我们可以将额外的功能加入到com.aomsir.service包下的所有类和方法中。而如果我们希望仅针对某个子包如com.aomsir.service.UserService,那么可以使用* com.aomsir.service.UserService.*.*(..)
。
这种方式尤其适合于大型项目,其中可能存在多个模块或功能区块,每个区块都有其自己的包。通过包切入点,我们可以为特定模块的所有类和方法统一地添加相同的额外功能,如日志记录或安全检查,而不必为每个类或方法单独定义切入点。
这种方法提供了一个高效的方式来组织和管理代码的横切关注点,确保项目结构的清晰和维护的便捷性
切入点函数
切入点函数在AOP中起着至关重要的作用,用于解析和执行切入点表达式,从而决定哪些方法或类应加入额外的功能。以execution(* * (..))
为例,其中的execution()正是一个切入点函数,专门用于匹配方法的执行。
除了execution()外,AOP还提供了一系列其他的切入点函数,以满足不同的匹配需求:
- args(): 根据方法参数类型进行匹配。
- within(): 针对特定的类型或包进行匹配。
- @annotation(): 根据方法上的注解来确定匹配。
每个切入点函数都有其独特的应用场景和匹配规则。了解并正确使用这些函数可以使我们在定义AOP增强时更加精确和高效
execution函数
execution()
是AOP中最常用且功能最为强大的切入点函数。它的灵活性使其可以应用于匹配方法、类甚至是包级别的切入点表达式。例如,使用execution(* com.aomsir.service..*(..))
,我们可以匹配com.aomsir.service包及其子包下的所有方法。
然而,这种强大的功能性也带来了一些复杂性。execution()的表达式结构可能相对繁琐,尤其是当我们需要定义更加细致的匹配规则时。尽管如此,它的强大和灵活性使得我们可以精确地控制增强的应用位置,为AOP提供了坚实的基础
args函数
args()
是AOP中的另一个重要切入点函数,它专门用于根据方法的参数类型进行匹配。例如,通过使用args(String, String)
,我们可以轻松地为所有具有两个String类型参数的方法添加额外功能。
相比之下,要达到同样的效果,execution()函数的表达式会稍显复杂,形如execution(* *(String, String))
。虽然功能上等同,但args()提供了一个更为简洁直观的写法。
这种简化在定义切入点时降低了复杂度,使得我们可以更轻松地捕捉目标方法,并为之添加相应的增强功能。
within函数
within()是AOP中的一个核心切入点函数,主要用于针对类和包的匹配。使用within(),我们可以轻松地为特定类或特定包下的所有类定位切入点。
以within(* *..UserService)
为例,这个表达式会匹配项目中任意包(及其子包)下名为UserService的类中的所有方法。
这种方式让我们能够方便地为特定的类或整个包下的类一次性定义相同的切入点,实现代码的集中管理和优化
@annotation函数
@annotation()是AOP中的一个独特切入点函数,专门用于匹配带有指定注解的方法。通过这个函数,我们可以为那些带有特定注解的方法集中地添加额外功能。
例如,表达式@annotation(com.aomsir.annotation.Log)
旨在匹配所有带有com.aomsir.annotation.Log
注解的方法。这意味着,只要方法上标注了Log注解,它就会被自动加入额外的功能。
这种方法为我们提供了一个简洁且高度灵活的方式,使得我们可以基于注解来组织和管理切入点,进而实现代码的模块化和易于维护。
切入点函数逻辑运算
切入点函数的逻辑运算使我们能够组合多个切入点函数,满足更为复杂和特定的匹配需求。这种整合方式允许我们在定义切入点时使用逻辑组合,如“与”、“或”和“非”,确保只有满足多重条件的方法或类被选中,从而实现更精细的控制
and
and操作是逻辑中的“与”运算,在切入点表达式中,它用于组合多个条件,确保所有指定的条件都被满足。例如,当我们想匹配一个带有两个String类型参数的login方法时,可以直接使用execution(* login(String, String))。但我们也可以利用and运算将两个表达式结合起来,如execution(* login(…)) and args(String, String)。
值得注意的是,and操作不能用于连接两个同类型的切入点函数,确保组合的表达式既有意义又避免冲突
or
or操作是逻辑中的“或”运算,在切入点表达式中,它允许我们匹配满足任一条件的方法。例如,若我们想同时匹配login和register方法,我们需要使用or操作。表达式为:execution(* login(..)) or execution(* register(..))
。
与and操作不同,or操作允许连接两个同类型的切入点函数,使得我们可以灵活地组合多个条件,满足广泛的匹配需求
总结
本篇文章深入探讨了AOP中切入点的各个方面。在切入点表达式部分,我们详述了方法、类以及包的匹配方式。这些表达式极为实用,只需根据业务需求进行灵活组合,即可轻松定义所需的切入点。而在切入点函数部分,我们讨论了几种主要的切入点函数及其逻辑组合。当这些函数与切入点表达式共同作用时,我们可以实现更为精确和细致的匹配,确保AOP的增强功能能够被恰当地应用于项目中
参考文献
- 孙哥孙帅suns说Spring5~学不会Spring? 因为你没找对人
- Spring官方文档