切点函数是AspectJ表达式语言的核心, 也是使用@AspectJ进行切面定义的难点.本小节我们通过具体的实例对切点函数进行深入学习.
1.@annotation()
@annotation()表示标注了某个注解的所有方法,这个比较简单.
2.execution()
execution()是最常使用的切点函数,其语法如下:
execution(<修饰符模式>? <返回类型模式> <方法名模式> (<参数模式>) <异常模式>?)
除了返回类型模式, 方法名模式, 参数模式是必须的外,其他两个都不是必须的
(1) 通过方法签名定义切点
- execution(public * *(..))
匹配所有目标类的public 方法, 第一个*号代表返回类型, 第二个*号代表方法名.而..代表任意的入参
- execution(* *To(..))
匹配目标类所有以To结尾的方法, 第一个*代表返回类型, *To代表所有以To结尾的方法
(2) 通过类定义切点
- execution(* com.bao.bao.Waiter.*(..))
匹配Waiter接口的所有方法, 第一个*号代表返回任意类型, com.bao.bao.Waiter.*代表Waiter接口中的任意方法
- execution(* com.bao.bao.Waiter+.*(..))
匹配Waiter接口及其所有实现类的方法, 这个和上面的区别在于,这个不但匹配Waiter接口中定义的方法, 还匹配Waiter实现类中定义的方法(但不在Waiter接口中定义),而上面的那个只匹配Waiter接口中定义的方法
(3) 通过类包定义切点
在类名模式串中, ".*"表示包下的所有类, 而"..*"表示包, 子孙包下的所有类
- execution(* com.bao.bao.*(..))
匹配com. bao.bao包下所有类的所有方法
- execution(* com.bao.bao..*(..))
匹配com.bao.bao包,子孙包下所有类的所有方法. ".."出现在类名中时, 后面必须跟"*"表示包,子孙包下的所有类
- execution(* com..*.*Dao.find*(..))
匹配包名前缀为com的任何包下类名后缀为Dao的方法, 方法名必须以find为前缀.
(4) 通过方法入参定义切点
切点表达式中, 方法入参部分比较复杂,可以使用"*"和".."通配符.其中"*"表示任意类型的参数;而".."表示任意类型参数且参数个数不限.
- execution(* joke(String, *))
匹配目标类中的joke()方法, 该方法第一个入参为String, 第二个入参可以是任意类型
- execution(* joke(String, ..))
匹配目标类中的joke()方法, 该方法第一个入参为String,后面可以有任意多个入参,且类型不限
3. args()和@args()
args()函数的入参是类名, @args()函数的入参必须是注解类的类名.虽然args()允许在类名后面使用+通配符, 但该通配符在这里没有意义, 添加和不添加都是一样的.
(1) args()
该函数接收一个类名, 表示目标类的方法的入参是指定类(包括子类)时, 那么该方法匹配该切点 .如
args(com.bao.bao.Waiter)
表示运行时入参类型是Waiter的方法, 这和execution(* *(com.bao.bao.Waiter))的区别在于后者是针对方法的签名而言, 而前者是针对方法运行时入参而言的
(2) @args()
该函数接受一个注解类的类名, 当方法运行时入参对象所对应的类标注了指定注解时, 该方法就匹配切点.这个切点函数的匹配规则不太容易理解...
4.within()
within()函数定义的连接点是针对目标类而言的,而非针对运行期对象的类型而言, 这和execution()是相同的.但和execution()不同的是,execution()所指定的连接点可以大到包, 小到方法入参,而within()所指定的连接点最小范围只能是类
(1) within(com.bao.bao.NaiveWaiter)
匹配目标类NaiveWaiter中的所有方法, 如果切点调整为within(com.bao.bao.Waiter), 则NaiveWaiter和NaughtyWaiter中的所有方法都不匹配.
(2) within(com.bao.bao.*)
匹配com.bao.bao中的所有类, 但不包括子孙包
(3) within(com.bao.bao..*)
匹配com.bao.bao包及子孙包中的类
5.@within()和@target()
和@annotation()及@args()函数一样, 他们也只接受注解类名作为入参,其中@target(M)匹配任意标注了@M的目标类, 而@within(M)匹配标注了@M的类及子孙类.
@target(M)切点的匹配规则如下:
假设NaiveWaiter标注了@Monitorable, 而其子孙类CuteNaiveWaiter没有标注@Monitorable, 则@target(com.bao.bao.Monitorable)匹配NaiveWaiter类的所有方法, 但不匹配CuteNaiveWaiter类的方法.
@within(M)切点的匹配规则如下:
假设NaiveWaiter标注了@Monitorable, 而其子孙类CuteNaiveWaiter没有标注@Monitorable, 则@within(com.bao.bao.Monitorable)匹配NaiveWaiter类的所有方法, 同时还匹配CuteNaiveWaiter类的方法.
有一个值得注意的地方, 假如某个接口标注了@M, 则所有实现该接口的类并不匹配@within(M)
6.target()和this()
target()切点函数通过判断目标类是否按类型匹配指定类决定连接点是否匹配,而this()则通过判断代理类是否按类型匹配指定类来决定是否和切点匹配.
target():
(1) target(com.bao.bao.Waiter)
NaiveWaiter, 以及CuteNaiveWaiter的所有方法都匹配切点,包括那些未在Waiter接口中定义的方法