0. 介绍
上一篇文章中spring aop增强类 ,这里面存在一个问题: 增强被织入到了目标类的所有方法中 .假设我们希望有选择地植入目标类的某些特定方法中,怎么办?这就需要使用切点进行目标连接点的定位.描述连接点是进行aop编程最主要的工作,
需要说明的两个东西:
增强:提供了连接点方位信息,如织入到方法的前面,后面等
切点:进一步描述了植入哪些类的哪些方法上
1. 切点
spring通过 org.springframework.aop.Pointcut 来描述
- spring 的 pointcut 核心抽象
- 由 ClassFilter 和 MethodMatcher 组成
- 通过ComposablePointcut 可以将 ClassFilter 和 MethodMatcher 和 Pointcut 组合起来
package org.springframework.aop;
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
Pointcut TRUE = TruePointcut.INSTANCE;
}
Pointcut继承树:切点类型
- 静态方法切点:org.springframework.aop.support.StaticMethodMatcherPointcut
- 动态方法切点:org.springframework.aop.support.DynamicMethodMatcherPointcut
- 注解切点:org.springframework.aop.support.annotation.AnnotationMatchingPointcut
- 表达式切点:org.springframework.aop.support.ExpressionPointcut
- 流程切点:org.springframework.aop.support.ControlFlowPointcut
- 复合切点:org.springframework.aop.support.ComposablePointcut
2. 切面=切点+增强
org.springframework.aop.Advisor 继承树
org.springframework.aop.PointcutAdvisor 继承树
2.1:静态普通方法名匹配切面
public class Waiter {
public void greetingTo(String name) {
System.out.println("greeting to :"+name);
}
public void serveTo(String name) {
System.out.println("serving to :"+name);
}
}
public class Seller {
public void greetingTo(String name) {
System.out.println("greeting to :"+name);
}
}
定义切面
import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import java.lang.reflect.Method;
public class GreetingAdvisor extends StaticMethodMatcherPointcutAdvisor {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return method.getName().equals("greetingTo");
}
@Override
public ClassFilter getClassFilter() {
/*return new ClassFilter() {
@Override
public boolean matches(Class<?> clazz) {
return Waiter.class.isAssignableFrom(clazz);
}
};*/
//return clazz -> Waiter.class.isAssignableFrom(clazz);
return Waiter.class::isAssignableFrom;
}
}
定义前置增强
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class GreetingBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
String clientName = (String) args[0];
System.out.println("How are you! Mr."+clientName);
}
}
xml配置文件:
<!--
定义Waiter 的目标对象,或者说是真实对象
-->
<bean id="waiterTarget" class="com.ghq.cloud.advisor.Waiter"/>
<!--
定义Seller 的目标对象,或者说是真实对象
-->
<bean id="sellerTarget" class="com.ghq.cloud.advisor.Seller"/>
<!--
定义前置增强
-->
<bean id="greetingBeforeAdvice" class="com.ghq.cloud.advisor.GreetingBeforeAdvice"/>
<!--
定义切面,
p:advice-ref: 将greetingBeforeAdvice装配到切面greetingAdvisor上
-->
<bean id="greetingAdvisor" class="com.ghq.cloud.advisor.GreetingAdvisor"
p:advice-ref="greetingBeforeAdvice"/>
<!--
定义一个抽象的父bean
p:interceptorNames:设置切面
p:proxyTargetClass:代理
-->
<bean id="parent" abstract="true"
class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="greetingAdvisor"
p:proxyTargetClass="true" />
<!--
生成真正的代理对象
p:target-ref:代理对象所代理的真实对象
-->
<bean id="waiter" parent="parent" p:target-ref="waiterTarget"/>
<bean id="seller" parent="parent" p:target-ref="sellerTarget"/>
main方法:
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:beans2.xml");
Waiter waiter = (Waiter) context.getBean("waiter");
Seller seller = (Seller) context.getBean("seller");
waiter.greetingTo("snow");
waiter.serveTo("snow");
seller.greetingTo("snow");
}
输出结果:我们发现只有Waiter类的greetingTo方法被增强了。
How are you! Mr.snow
greeting to :snow
serving to :snow
greeting to :snow
缺点:不够灵活。
2.2:静态正则表达式方法名匹配切面
直接xml配置所有:
<!--
定义Waiter 的目标对象,或者说是真实对象
-->
<bean id="waiterTarget" class="com.ghq.cloud.advisor.Waiter"/>
<!--
定义Seller 的目标对象,或者说是真实对象
-->
<bean id="sellerTarget" class="com.ghq.cloud.advisor.Seller"/>
<!--
定义前置增强
-->
<bean id="greetingBeforeAdvice" class="com.ghq.cloud.advisor.GreetingBeforeAdvice"/>
<!--
定义切面,
p:advice-ref: 将greetingBeforeAdvice装配到切面greetingAdvisor上
-->
<bean id="regexpMethodPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
p:advice-ref="greetingBeforeAdvice">
<property name="patterns">
<list>
<value>.*greet.*</value>
</list>
</property>
</bean>
<!--
定义一个抽象的父bean
p:interceptorNames:设置切面
p:proxyTargetClass:代理
-->
<bean id="parent" abstract="true"
class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="regexpMethodPointcutAdvisor"
p:proxyTargetClass="true" />
<!--
生成真正的代理对象
p:target-ref:代理对象所代理的真实对象
-->
<bean id="waiter" parent="parent" p:target-ref="waiterTarget"/>
<bean id="seller" parent="parent" p:target-ref="sellerTarget"/>
2.3:流程切面
希望WaiterDelegate 里面的service方法所调用的方法都被增强
可以使用:DefaultPointcutAdvisor 结合 ControlFlowPointcut实现
public class WaiterDelegate {
private Waiter waiter;
public void setWaiter(Waiter waiter) {
this.waiter = waiter;
}
public void service(String clientName){
waiter.greetingTo(clientName);
waiter.serveTo(clientName);
}
}
xml配置:
<bean id="waiterTarget" class="com.ghq.cloud.advisor.Waiter"/>
<bean id="greetingBeforeAdvice" class="com.ghq.cloud.advisor.GreetingBeforeAdvice"/>
<!--
定义切面,
p:advice-ref: 将greetingBeforeAdvice装配到切面greetingAdvisor上
-->
<bean id="controlFlowPointcut" class="org.springframework.aop.support.ControlFlowPointcut">
<constructor-arg index="0" type="java.lang.Class" value="com.ghq.cloud.advisor.WaiterDelegate"/>
<constructor-arg index="1" type="java.lang.String" value="service"/>
</bean>
<bean id="defaultPointcutAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
p:pointcut-ref="controlFlowPointcut"
p:advice-ref="greetingBeforeAdvice"/>
<bean id="parent" abstract="true"
class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="defaultPointcutAdvisor"
p:proxyTargetClass="true" />
<bean id="waiter" parent="parent" p:target-ref="waiterTarget"/>
main方法:
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:beans4.xml");
Waiter waiter = (Waiter) context.getBean("waiter");
WaiterDelegate delegate = new WaiterDelegate();
delegate.setWaiter(waiter);
waiter.greetingTo("aaa");
waiter.serveTo("bbb");
System.out.println("----------------");
delegate.service("cccc");
}
输出结果:
greeting to :aaa
serving to :bbb
----------------
How are you! Mr.cccc
greeting to :cccc
How are you! Mr.cccc
serving to :cccc
2.4:复合切点切面
-
在前面的列子中定义的切面仅仅有一个切点,但是,有时候一个切点可能难以描述目标连接点的信息。比如在前面流程切面的例子中,假设我们希望由WaiterDelegate#service()发起调用且被调用方法是Waiter#greetingTo时才织入增强,这个切点就是复合切点。因为它是由两个单独的切点功通确定。
-
用户可以使用spring提供的ComposablePointcut把两个切点组合起来,通过切点的复合运算表示。ComposablePointcut可以将多个切点以并集或交集的方式组合起来,提供了切点之间复合运算功能。
-
ComposablePointcut本身也是一个切点,实现了Pointcut接口。
ComposablePointcut的源码如下:
import java.io.Serializable;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.util.Assert;
/**
* Convenient class for building up pointcuts. All methods return
* ComposablePointcut, so we can use a concise idiom like:
*
* {@code
* Pointcut pc = new ComposablePointcut().union(classFilter).intersection(methodMatcher).intersection(pointcut);
* }
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
* @since 11.11.2003
* @see Pointcuts
*/
public class ComposablePointcut implements Pointcut, Serializable {
/** use serialVersionUID from Spring 1.2 for interoperability */
private static final long serialVersionUID = -2743223737633663832L;
private ClassFilter classFilter;
private MethodMatcher methodMatcher;
/**
* Create a default ComposablePointcut, with {@code ClassFilter.TRUE}
* and {@code MethodMatcher.TRUE}.
*/
public ComposablePointcut() {
this.classFilter = ClassFilter.TRUE;
this.methodMatcher = MethodMatcher.TRUE;
}
/**
* Create a ComposablePointcut based on the given Pointcut.
* @param pointcut the original Pointcut
*/
public ComposablePointcut(Pointcut pointcut) {
Assert.notNull(pointcut, "Pointcut must not be null");
this.classFilter = pointcut.getClassFilter();
this.methodMatcher = pointcut.getMethodMatcher();
}
/**
* Create a ComposablePointcut for the given ClassFilter,
* with {@code MethodMatcher.TRUE}.
* @param classFilter the ClassFilter to use
*/
public ComposablePointcut(ClassFilter classFilter) {
Assert.notNull(classFilter, "ClassFilter must not be null");
this.classFilter = classFilter;
this.methodMatcher = MethodMatcher.TRUE;
}
/**
* Create a ComposablePointcut for the given MethodMatcher,
* with {@code ClassFilter.TRUE}.
* @param methodMatcher the MethodMatcher to use
*/
public ComposablePointcut(MethodMatcher methodMatcher) {
Assert.notNull(methodMatcher, "MethodMatcher must not be null");
this.classFilter = ClassFilter.TRUE;
this.methodMatcher = methodMatcher;
}
/**
* Create a ComposablePointcut for the given ClassFilter and MethodMatcher.
* @param classFilter the ClassFilter to use
* @param methodMatcher the MethodMatcher to use
*/
public ComposablePointcut(ClassFilter classFilter, MethodMatcher methodMatcher) {
Assert.notNull(classFilter, "ClassFilter must not be null");
Assert.notNull(methodMatcher, "MethodMatcher must not be null");
this.classFilter = classFilter;
this.methodMatcher = methodMatcher;
}
/**
* Apply a union with the given ClassFilter.
* @param other the ClassFilter to apply a union with
* @return this composable pointcut (for call chaining)
*/
public ComposablePointcut union(ClassFilter other) {
this.classFilter = ClassFilters.union(this.classFilter, other);
return this;
}
/**
* Apply an intersection with the given ClassFilter.
* @param other the ClassFilter to apply an intersection with
* @return this composable pointcut (for call chaining)
*/
public ComposablePointcut intersection(ClassFilter other) {
this.classFilter = ClassFilters.intersection(this.classFilter, other);
return this;
}
/**
* Apply a union with the given MethodMatcher.
* @param other the MethodMatcher to apply a union with
* @return this composable pointcut (for call chaining)
*/
public ComposablePointcut union(MethodMatcher other) {
this.methodMatcher = MethodMatchers.union(this.methodMatcher, other);
return this;
}
/**
* Apply an intersection with the given MethodMatcher.
* @param other the MethodMatcher to apply an intersection with
* @return this composable pointcut (for call chaining)
*/
public ComposablePointcut intersection(MethodMatcher other) {
this.methodMatcher = MethodMatchers.intersection(this.methodMatcher, other);
return this;
}
/**
* Apply a union with the given Pointcut.
* <p>Note that for a Pointcut union, methods will only match if their
* original ClassFilter (from the originating Pointcut) matches as well.
* MethodMatchers and ClassFilters from different Pointcuts will never
* get interleaved with each other.
* @param other the Pointcut to apply a union with
* @return this composable pointcut (for call chaining)
*/
public ComposablePointcut union(Pointcut other) {
this.methodMatcher = MethodMatchers.union(
this.methodMatcher, this.classFilter, other.getMethodMatcher(), other.getClassFilter());
this.classFilter = ClassFilters.union(this.classFilter, other.getClassFilter());
return this;
}
/**
* Apply an intersection with the given Pointcut.
* @param other the Pointcut to apply an intersection with
* @return this composable pointcut (for call chaining)
*/
public ComposablePointcut intersection(Pointcut other) {
this.classFilter = ClassFilters.intersection(this.classFilter, other.getClassFilter());
this.methodMatcher = MethodMatchers.intersection(this.methodMatcher, other.getMethodMatcher());
return this;
}
@Override
public ClassFilter getClassFilter() {
return this.classFilter;
}
@Override
public MethodMatcher getMethodMatcher() {
return this.methodMatcher;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof ComposablePointcut)) {
return false;
}
ComposablePointcut otherPointcut = (ComposablePointcut) other;
return (this.classFilter.equals(otherPointcut.classFilter) &&
this.methodMatcher.equals(otherPointcut.methodMatcher));
}
@Override
public int hashCode() {
return this.classFilter.hashCode() * 37 + this.methodMatcher.hashCode();
}
@Override
public String toString() {
return "ComposablePointcut: " + this.classFilter + ", " +this.methodMatcher;
}
}
但是 ComposablePointcut 并没有提供对切点的交并集操作,这个可以借助类org.springframework.aop.support.Pointcuts来实现
public static Pointcut union(Pointcut pc1, Pointcut pc2) {
return new ComposablePointcut(pc1).union(pc2);
}
public static Pointcut intersection(Pointcut pc1, Pointcut pc2) {
return new ComposablePointcut(pc1).intersection(pc2);
}
定义类
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.ControlFlowPointcut;
import org.springframework.aop.support.NameMatchMethodPointcut;
import org.springframework.aop.support.Pointcuts;
public class GreetingComposablePointcut {
public Pointcut getPointcut(){
ComposablePointcut cp = new ComposablePointcut();
Pointcut pointcut = new ControlFlowPointcut(WaiterDelegate.class,"service");
NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut();
nameMatchMethodPointcut.addMethodName("greetingTo");
Pointcut intersection = Pointcuts.intersection(pointcut, nameMatchMethodPointcut);
return cp.intersection(intersection);
}
}
xml配置:
<bean id="waiterTarget" class="com.ghq.cloud.advisor.Waiter"/>
<bean id="gcp" class="com.ghq.cloud.advisor.GreetingComposablePointcut"/>
<bean id="greetingBeforeAdvice" class="com.ghq.cloud.advisor.GreetingBeforeAdvice"/>
<bean id="defaultPointcutAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
p:pointcut="#{gcp.pointcut}"
p:advice-ref="greetingBeforeAdvice"/>
<bean id="parent" abstract="true"
class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="defaultPointcutAdvisor"
p:proxyTargetClass="true" />
<bean id="waiter" parent="parent" p:target-ref="waiterTarget"/>
main方法:
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:beans5.xml");
Waiter waiter = (Waiter) context.getBean("waiter");
WaiterDelegate delegate = new WaiterDelegate();
delegate.setWaiter(waiter);
waiter.greetingTo("aaa");
waiter.serveTo("bbb");
System.out.println("----------------");
delegate.service("cccc");
}
输出结果:只对Waiter#greetingTo的方法进行了增强处理
greeting to :aaa
serving to :bbb
----------------
How are you! Mr.cccc
greeting to :cccc
serving to :cccc