spring aop 切面

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值