5.4.3. Declaring a Pointcut
PointCut designators (pcd) pointcut表达式函数
• 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 run in Spring AOP) has the given annotation.
类内部的方法调用不会触发AOP
spring aop框架是基于代理实现的。
你可以选择使用jdk动态代理,还是cglib动态代理。
若采用jdk动态代理,外部类只有在调用代理类所实现接口的public方法,才会被aop拦截。
若采用cglib动态代理,外部类在调用代理类的public | protected 级别方法时,就会触发aop拦截。一般还是建议都用public。
不懂部分1
Note that pointcut definitions are generally matched against any intercepted
method. If a pointcut is strictly meant to be public-only, even in a CGLIB proxy
scenario with potential non-public interactions through proxies, it needs to be
defined accordingly.
If your interception needs include method calls or even constructors within the
target class, consider the use of Spring-driven native AspectJ weaving instead of
Spring’s proxy-based AOP framework. This constitutes a different mode of AOP
usage with different characteristics, so be sure to make yourself familiar with
weaving before making a decision.
PointCut接口
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-api-pointcuts
用于告诉我们advices将被织入 哪些类 或 哪些方法 或 哪些类或哪些方法 。
- getClassFilter 告诉我们advices将被织入哪些类。
- getMethodMatcher 告诉我们advices将被织入哪些方法。
- 它们可以组合成A,B,A并B 三种情况
MethodMatcher中,若仅仅第一个matches返回true,而isRuntime返回false,那么AOP在构建代理类时,就能判断这个方法是否被切。
但若是isRuntime也返回true,第二个matches就会发挥作用,使得每次请求,都会使用它进行匹配,优点是切点可以关注方法的参数了,缺点是增加了开销。
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}
public interface ClassFilter {
// Should the pointcut apply to the given interface or target class
boolean matches(Class clazz);
}
public interface MethodMatcher {
boolean matches(Method m, Class<?> targetClass);
boolean isRuntime();
boolean matches(Method m, Class<?> targetClass, Object... args);
}
spring中的pointCut API
6.1.2. Operations on Pointcuts
spring支持切点的交集和并集。你可以使用
PointCuts类中的静态方法创建pointCut,
也可以使用ComposablePointCut,
当然更快捷的方法是使用AspectJ pointCut 表达式。
6.1.3. AspectJ Expression Pointcuts
至2.0起,spring最重要的pointCut类,是基于AspectJ提供类库的org.springframework.aop.aspectj.AspectJExpressionPointcut,因为它可以识别AspectJ pointCut表达式。
6.1.4. Convenience Pointcut Implementations
spring提供了多个方便的PointCut实现类,你可以直接使用他们。其他的实现类基本都是应用中特定PointCut的子类。
Static Pointcuts
Static Pointcuts可用于目标类的+目标方法。
但是没法获取方法的参数。
spring只会在被切方法的第一次调用时,判断是否命中该Static PointCut,后面每次调用都不会再判断了。
Regular Expression Pointcuts
指定静态切点的常规方法是使用表达式,除了spring外的多个aop框架都支持这种模式。
org.springframework.aop.support.JdkRegexpMethodPointcut 就是jdk提供的一种支持表达式创建切点的常规方法。
通过JdkRegexpMethodPointcut class, 你可以创建一个表达式列表,其中任意一个命中,都可以触发该切点(表达式并集)。
<bean id="settersAndAbsquatulatePointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>
然后还有一个更简单的类叫RegexpMethodPointcutAdvisor,把切点(pointCut)和建议(advice)的配置融为一体,可以与任何advice type合用(advice是一个interceptor拦截器,可以是beforeAdvice、throw advice或其他),用法如下:
<bean id="settersAndAbsquatulateAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="beanNameOfAopAllianceInterceptor"/>
</property>
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>
Dynamic pointcuts
动态pointCut更为消耗资源,它必须考虑方法参数以及静态方法,不同于静态pointCut仅在方法第一次调用时判断是否命中,动态pointCut在每次调用方法时都要判断(毕竟方法参数每次都在变)。
最重要的一个案例就是control flow pointcut。
Control Flow Pointcuts
Spring control flow pointcuts 概念上类似于 AspectJ cflow pointcuts, 但是可能威力更小(目前没法在pointCut的某个joinPoint里执行第二个pointCut的方法)。control flow pointcut 匹配的是整个调用栈(估计是整个调用栈的方法都会被切,待验证)。Control flow pointcuts 可以通过使用 org.springframework.aop.support.ControlFlowPointcut 指定。
control flow pointcut比其他动态pointCut消耗更发,在java1.4中,开销是其他动态pointcut的5倍。
6.1.5. Pointcut Superclasses
Spring提供了有用的切入点超类,以帮助您实现自己的切入点。
静态pointCut最常用,只需要重写一个方法(matches)即可。
class TestStaticPointcut extends StaticMethodMatcherPointcut {
public boolean matches(Method m, Class targetClass) {
// return true if custom criteria match
}
}
也有动态pointCut的超类。 您可以将自定义切入点与任何建议类型一起使用。
spring中的advice API
6.2.1. Advice Lifecycles
advice分为per-class advice,和per-instance advice。
per-class advice:最为常用,比如事务advice,它只是单纯切住类的方法及参数,与该类产生的对象的状态无关。
Per-instance advice:<font color="red">is appropriate for introductions, to support mixins.(不懂求助)</font>在这种情况下,advice会为代理对象添加状态。
在某些AOP代理中,支持既共享,又针对单个实例(per-instance)的advice.
You can use a mix of shared and per-instance advice in the same AOP proxy.
6.2.2. Advice Types in Spring
拦截环绕advice(Interception Around Advice)
spring中最最基础且常见的advice类型。
spring的拦截环绕advice参照了AOP的Alliance接口。
实现环绕advice的类,都实现了以下接口:
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
Introduction Advice
顾问介绍对象(接口),introductionInterceptor筛选顾问介绍来的对象。
接口可以被介绍
特殊的advice。使用其需要IntroductionAdvisor和IntroductionInterceptor。
```java
public interface IntroductionInterceptor extends MethodInterceptor {
boolean implementsInterface(Class intf);
}
```
The invoke() method inherited from the AOP Alliance MethodInterceptor interface must implement the introduction. That is, if the invoked method is on an introduced interface, the introduction interceptor is responsible for handling the method call — it cannot invoke proceed().
Introduction advice不能与任何切入点一起使用,因为它仅适用于类,而不适用于方法级别。 您只能通过IntroductionAdvisor使用introduction advice,它具有以下方法:
```java
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
ClassFilter getClassFilter();
void validateInterfaces() throws IllegalArgumentException;
}
public interface IntroductionInfo {
Class<?>[] getInterfaces();
}
```
没有MethodMatcher,因此没有与introduction advice相关的Pointcut。 它只能进行类过滤。
getInterfaces() 接口返回该advisor介绍的interface。
validateInterfaces() 用于区分介绍过来的interface是否可以被配置的IntroductionInterceptor所实现。
案例:我们想把如下接口介绍给一个或多个对象
```java
public interface Lockable {
void lock();
void unlock();
boolean locked();
}
```
我们给类做切面,所有set方法都会触发lock(),这样就可以在类本身没有察觉的情况下,使类变得不可修改。
首先,我们需要一个IntroductionInterceptor来完成繁重的工作。我们会实现org.springframework.aop.support.DelegatingIntroductionInterceptor,它很方便,尽管可以直接实现IntroductionInterceptor,但最好还是实现DelegatingIntroductionInterceptor。
DelegatingIntroductionInterceptor实例查找由它(delegate本身)实现的所有接口(IntroductionInterceptor除外),并支持针对其中任何一个的introduction。
The DelegatingIntroductionInterceptor is designed to delegate an introduction to an actual implementation of the introduced interfaces, concealing the use of interception to do so. You can set the delegate to any object using a constructor argument. The default delegate (when the no-argument constructor is used) is this. Thus, in the next example, the delegate is the LockMixin subclass of DelegatingIntroductionInterceptor. Given a delegate (by default, itself), a DelegatingIntroductionInterceptor instance looks for all interfaces implemented by the delegate (other than IntroductionInterceptor) and supports introductions against any of them. Subclasses such as LockMixin can call the suppressInterface(Class intf) method to suppress interfaces that should not be exposed. However, no matter how many interfaces an IntroductionInterceptor is prepared to support, the IntroductionAdvisor used controls which interfaces are actually exposed. An introduced interface conceals any implementation of the same interface by the target.
Thus, LockMixin extends DelegatingIntroductionInterceptor and implements Lockable itself. The superclass automatically picks up that Lockable can be supported for introduction, so we do not need to specify that. We could introduce any number of interfaces in this way.
Note the use of the locked instance variable. This effectively adds additional state to that held in the target object.
The following example shows the example LockMixin class:
```java
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {
private boolean locked;
public void lock() {
this.locked = true;
}
public void unlock() {
this.locked = false;
}
public boolean locked() {
return this.locked;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
if (locked() && invocation.getMethod().getName().indexOf("set") == 0) {
throw new LockedException();
}
return super.invoke(invocation);
}
}
```
通常,不用重写invoke()方法,因为如果方法被介绍给切面,在调用方法前会先执行委托方法,没有委托方法才会直接到joinpoint。DelegatingIntroductionInterceptor
Often, you need not override the invoke() method. The DelegatingIntroductionInterceptor implementation (which calls the delegate method if the method is introduced, otherwise proceeds towards the join point) usually suffices. In the present case, we need to add a check: no setter method can be invoked if in locked mode.
The required introduction only needs to hold a distinct LockMixin instance and specify the introduced interfaces (in this case, only Lockable). A more complex example might take a reference to the introduction interceptor (which would be defined as a prototype). In this case, there is no configuration relevant for a LockMixin, so we create it by using new. The following example shows our LockMixinAdvisor class:
JavaKotlin
public class LockMixinAdvisor extends DefaultIntroductionAdvisor {
public LockMixinAdvisor() {
super(new LockMixin(), Lockable.class);
}
}
We can apply this advisor very simply, because it requires no configuration. (However, it is impossible to use an IntroductionInterceptor without an IntroductionAdvisor.) As usual with introductions, the advisor must be per-instance, as it is stateful. We need a different instance of LockMixinAdvisor, and hence LockMixin, for each advised object. The advisor comprises part of the advised object’s state.
We can apply this advisor programmatically by using the Advised.addAdvisor() method or (the recommended way) in XML configuration, as any other advisor. All proxy creation choices discussed below, including “auto proxy creators,” correctly handle introductions and stateful mixins.