Spring AOP概述

本节回顾一下AOP的概念

AOP是面向切面的编程思想,百科的概述为:

Aspect是一种新的模块化机制,用来描述分散在对象、类或函数中的横切关注点。从关注点中分离出横切关注点是面向切面的程序设计的核心概念。分离关注点是解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑的代码不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通过切面来封装、维护,这样原本分散在整个应用程序的变动就可以很好的管理起来。


Advice通知定义在连接点做什么,为切面增强提供织入接口。在Spring AOP中,主要描述Spring AOP围绕方法调用而注入的切面行为。Advice是AOP联盟定义的一个接口,具体的接口定义在org.aopalliance.aop.Advice中。具体的通知类型比如BeforeAdvice、AfterAdvice、AfterReturningAdvice、ThrowsAdvice等。

下面从BeforeAdvice看一下类层次关系:


可以看到MethodBeforeAdvice为待增强的目标方法设置的前置增强接口:

public interface MethodBeforeAdvice extends BeforeAdvice {

	void before(Method method, Object[] args, @Nullable Object target) throws Throwable;

}

作为回调函数,before方法的实现在Advice中被配置到目标方法前,会在调用目标方式时被回调。

具体的调用参数:Method对象,这个参数是目标方法的反射对象;Object[]对象数组,这个对象数组中包含目标方法的输入参数。以CountingBeforeAdvice为例子,CountingBeforeAdvice是MethodBeforeAdvice的具体实现:

@SuppressWarnings("serial")
public class CountingBeforeAdvice extends MethodCounter implements MethodBeforeAdvice {

	@Override
	public void before(Method m, Object[] args, Object target) throws Throwable {
		count(m);
	}

}

CountingBeforeAdvice是完成统计被调用的方法次数。作为切面增强实现,会根据调用方法的方法名进行统计,把统计结果根据方法名和调用刺水作为键值对放入一个map中。

count方法在基类MethodCounter中实现,它在对象中维护一个哈希表,用来存储统计数据。在统计过程中,首先通过目标方法的反射对象得到方法名,然后进行累加,把统计结果放到维护的哈希表中。

public class MethodCounter implements Serializable {

	/** 这个hashMap存储方法名和调用次数的键值对 */
	private HashMap<String, Integer> map = new HashMap<>();

	private int allCount;//所有的调用次数

	protected void count(Method m) {
		count(m.getName());
	}

	protected void count(String methodName) {//根据目标方法的方法名进行统计调用次数
		Integer i = map.get(methodName);
		i = (i != null) ? new Integer(i.intValue() + 1) : new Integer(1);
		map.put(methodName, i);
		++allCount;
	}

	public int getCalls(String methodName) {//根据方法名获得调用的次数
		Integer i = map.get(methodName);
		return (i != null ? i.intValue() : 0);
	}

	public int getCalls() {//取得所有方法的调用次数
		return allCount;
	}

	/**
	 * A bit simplistic: just wants the same class.
	 * Doesn't worry about counts.
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object other) {
		return (other != null && other.getClass() == this.getClass());
	}

	@Override
	public int hashCode() {
		return getClass().hashCode();
	}

}


Pointcut切点决定Advice通知应该作用于哪个连接点,也就是说通过Pointcut定义需要增强的方法的集合,这些集合的选取可以按照一定的规则来完成。

public interface Pointcut {

	/**
	 * Return the ClassFilter for this pointcut.
	 * @return the ClassFilter (never {@code null})
	 */
	ClassFilter getClassFilter();

	/**
	 * Return the MethodMatcher for this pointcut.
	 * @return the MethodMatcher (never {@code null})
	 */
	MethodMatcher getMethodMatcher();


	/**
	 * Canonical Pointcut instance that always matches.
	 */
	Pointcut TRUE = TruePointcut.INSTANCE;

}

在接口的定义可以看到,需要返回一个MethodMatcher。对于Ponitcut的匹配判断功能,具体由这个返回的MethodMatcher完成,也就是由MethodMatcher判断是否需要对当前方法进行增强,或者是否需要对当前方法应用配置好的Advice通知。

切点在Spring AOP中的类继承体系:


下面以正则表达式切点JdkRegexpMethodPointcut为实例,了解一下Pointcut的工作原理。

通过基类StaticMethodMatcherPointcut可以看到,getMethodMatcher返回的是StaticMethodMatcher。

在JdkRegexpMethodPointcut可以看到一个matches方法,这个方法是使用正则表达式对方法名进行匹配的地方。

	@Override
	protected boolean matches(String pattern, int patternIndex) {
		Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);
		return matcher.matches();
	}
此外还有通过方法名匹配进行Advice匹配的NameMatchMethodPointcut
	@Override
	public boolean matches(Method method, @Nullable Class<?> targetClass) {
		for (String mappedName : this.mappedNames) {
			if (mappedName.equals(method.getName()) || isMatch(method.getName(), mappedName)) {
				return true;
			}
		}
		return false;
	}


Advisor通知器,完成对目标方法的切面增强设计和关注点的设计以后,需要一个对象结合起来,完成这个作用的就是Advisor通知器。

我们以DefaultPointcutAdvisor为例子,来了解Advisor的工作原理。

在DefaultPointcutAdvisor中,有两个属性,分别是advice和pointcut。通过这两个属性,可以分别配置Advice和Pointcut:

@SuppressWarnings("serial")
public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {

	private Pointcut pointcut = Pointcut.TRUE;

	public DefaultPointcutAdvisor() {
	}

	public DefaultPointcutAdvisor(Advice advice) {
		this(Pointcut.TRUE, advice);
	}

	
	public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
		this.pointcut = pointcut;
		setAdvice(advice);
	}


	public void setPointcut(@Nullable Pointcut pointcut) {
		this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);
	}

	@Override
	public Pointcut getPointcut() {
		return this.pointcut;
	}

	@Override
	public String toString() {
		return getClass().getName() + ": pointcut [" + getPointcut() + "]; advice [" + getAdvice() + "]";
	}

}

在DefaultPointcutAdvisor中,Pointcut默认是Pointcut.True

Pointcut TRUE = TruePointcut.INSTANCE;

TruePointcut的INSTANCE是一个单例。可以看到采用了单例模式

class TruePointcut implements Pointcut, Serializable {

	public static final TruePointcut INSTANCE = new TruePointcut();

	private TruePointcut() {
	}

	@Override
	public ClassFilter getClassFilter() {
		return ClassFilter.TRUE;
	}

	@Override//对任何方法的匹配都返回true
	public MethodMatcher getMethodMatcher() {
		return MethodMatcher.TRUE;
	}

	private Object readResolve() {
		return INSTANCE;
	}

	@Override
	public String toString() {
		return "Pointcut.TRUE";
	}

}
class TrueMethodMatcher implements MethodMatcher, Serializable {

	public static final TrueMethodMatcher INSTANCE = new TrueMethodMatcher();

	private TrueMethodMatcher() {
	}


	@Override
	public boolean isRuntime() {
		return false;
	}

	@Override
	public boolean matches(Method method, @Nullable Class<?> targetClass) {
		return true;
	}

	@Override
	public boolean matches(Method method, @Nullable Class<?> targetClass, Object... args) {
		// Should never be invoked as isRuntime returns false.
		throw new UnsupportedOperationException();
	}


	@Override
	public String toString() {
		return "MethodMatcher.TRUE";
	}

	/**
	 * Required to support serialization. Replaces with canonical
	 * instance on deserialization, protecting Singleton pattern.
	 * Alternative to overriding {@code equals()}.
	 */
	private Object readResolve() {
		return INSTANCE;
	}

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章