Spring AOP源码解析(一)——核心概念

目录

Pointcut

ClassFilter

MethodMatcher 

Advisor

 IntroductionAdvisor 

PointcutAdvisor 

AbstractPointcutAdvisor

Advice  

AspectJ Advice

AbstractAspectJAdvice

Interceptor

AdvisorAdapter

TargetSource


在了解Spring事务源码时,根据调用跟踪,最后发现是TransactionAttributeSourcePointcut判断是否应用了@Transactional注解,来进行AOP。TransactionAttributeSourcePointcutPointcut接口的一个实现。

Pointcut

Pointcut的唯一作用就是筛选要拦截的目标方法,Pointcut只是一种筛选规则(或者叫过滤器)。

Pointcut由ClassFilter(类过滤器)和MethodMatcher(方法匹配器)两个组件组成。ClassFilter检查当前筛选规则与目标类是否匹配,MethodMatcher检查当前筛选规则与目标方法是否匹配。两个组件的共同作用,可以筛选出一个符合既定规则的方法的集合。通过Advisor(通知器)和Advice(通知)和Pointcut(切点)组合起来,就可以把指定的通知应用到指定的方法集合上。

        Pointcut定义源码如下:

public interface Pointcut {

	/**
	 * 返回当前切点持有的类过滤器,不能返回null值。
	 * @return
	 */
	ClassFilter getClassFilter();

	/**
	 * 返回当前切点持有的方法匹配器,不能返回null值
	 * @return
	 */
	MethodMatcher getMethodMatcher();

	/**
	 * 一个最简单切点的实现。
	 * 这个切点由ClassFilter.TRUE和MethodMatcher.TRUE两个组件组成
	 * 它匹配所有的目标类和目标方法。
	 */
	Pointcut TRUE = TruePointcut.INSTANCE;

}

Spring Framework实现了以下PointCut。

其中比较重要的有:

  • AnnotationMatchingPointcut:

注解匹配切点。根据类上或方法上是否存在指定的注解判断切点的匹配性,如果没有显示指定注解,则匹配所有。

  • DynamicMethodMatcherPointcut:

动态方法匹配器切点。它本质上是一个方法匹配器,但同时具有了切点的功能。

  • ComposablePointcut:

可组合的切点。这种切点可以与或逻辑,任意组合其他的Pointcut、ClassFilter和MethodMatcher。其本质是通过ClassFilters和MethodMatchers两个工具类进行Pointcut内部组件的组合。

  • JdkRegexpMethodPointcut:

JDK正则表达式切点,即使用正则表达式描述方法的拦截规则和排除规则。

  • StaticMethodMatcherPointcut

静态方法匹配器切点。包括TransactionAttributeSourcePointcut(事务相关)

  • AspectJExpressionPointcut:

AspectJ切点表达式切点,ExpressionPointcut的一个具体实现。顾名思义,使用AspectJ的切点表达式描述筛选规则。表达式基本语法如下(非完整语法):

execution(<方法修饰符>? <方法返回值类型> <包名>.<类名>.<方法名>(<参数类型>) [throws <异常类型>]?)

ClassFilter

 ClassFilter,用于约束一个Advisor,与指定的targetClass是否匹配。只有匹配的前提下,Advisor才能使用其内部持有的Advice对targetClass进行增强。

@FunctionalInterface
public interface ClassFilter {

	/**	 * 判断指定接口或类是否匹配*/
	boolean matches(Class<?> clazz);
	ClassFilter TRUE = TrueClassFilter.INSTANCE;

}

ClassFilter有4中简单方式的实现:

  1. TypePatternClassFilter:基于AspectJ的类型匹配实现
  2. AnnotationClassFilter:通过检查目标类是否存在指定的注解,决定是否匹配
  3. RootClassFilter:通过判断目标类是否是指定类型(或其子类型),决定是否匹配

        除此之外,还有两种特殊方式的实现:

  1. DefaultIntroductionAdvisor:默认的引介通知器,它是一种通知器,但同时兼具了类过滤器的功能,且matches总返回true。它的作用是给所有bean追加指定接口。
  2. AspectJExpressionPointcut:AspectJ表达式切点(通过解析XML配置文件中的<aop:pointcut>元素生成的就是此类型的bean)。它是一种切点,但与一般的切点不同。一般的切点需要持有单独的ClassFilter和MethodMatcher。但是AspectJ表达式切点本身就兼具了这两个组件的功能。因为切点表达式,就是用来描述要代理的目标类和目标方法的。

MethodMatcher 

方法匹配器。判断目标类的方法是否匹配。

两个参数的matches()被称为静态匹配,在匹配条件不是太严格时使用,可以满足大部分场景的使用,称之为静态的主要是区分为三个参数的matches()方法需要在运行时动态的对参数的类型进行匹配;两个方法的分界线就是boolean isRuntime()方法,进行匹配时先用两个参数的matches()方法进行匹配,若匹配成功,则检查boolean isRuntime()的返回值,若为true,则调用三个参数的matches()方法进行匹配(若两个参数的都匹配不中,三个参数的必定匹配不中)

public interface MethodMatcher {

	/**静态检查目标类的方法是否匹配。*/
	boolean matches(Method method, Class<?> targetClass);

	/**
	 * 此MethodMatcher 是否动态的。*/
	boolean isRuntime();

	boolean matches(Method method, Class<?> targetClass, Object... args);

	/**
	 * Canonical instance that matches all methods.
	 */
	MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}

 MethodMatcher 的几种主要实现:

  • DynamicMethodMatcher:动态方法匹配器。isRuntime()一直返回true。
  • StaticMethodMatcher:静态方法匹配器。isRuntime()一直返回false。
  • IntersectionMethodMatcher:And 匹配器,2个匹配器都匹配才匹配。
  • UnionMethodMatcher:Or匹配器,1个匹配器都匹配就匹配。
  • IntroductionAwareMethodMatcher:引介匹配器。

Advisor

把Advice(通知)应用到目标类。

public interface Advisor {

	Advice EMPTY_ADVICE = new Advice() {};

	Advice getAdvice();

	/**是与特定的实例关联还是所有实例共享。一般返回true,关联指定实例。
	*/
	boolean isPerInstance();

}

Advisor分两大类:IntroductionAdvisor(引介通知器)和PointcutAdvisor(切点通知器)。两类Advisor都是为了增强targetClass,但是作用不一样。IntroductionAdvisor主要为了给targetClass追加接口(或者说追加更多的方法),这种增强属于类级别的增强;而PointcutAdvisor主要为了拦截方法,这种增强属于方法级别的增强

        正是由于两类Advisor的增强级别不同,而导致了对ClassFilter的使用方式不同。IntroductionAdvisor进行类级别增强,因此只需要直接持有ClassFilter即可;而PointcutAdvisor进行方法级别增强,因此需要同时使用ClassFilter和MethodMatcher(方法匹配器)。PointcutAdvisor内部持有一个Pointcut,而Pointcut就是由ClassFilter和MethodMatcher组成的。

 IntroductionAdvisor 

public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
	ClassFilter getClassFilter();
	void validateInterfaces() throws IllegalArgumentException;

}

PointcutAdvisor 

public interface PointcutAdvisor extends Advisor {
	Pointcut getPointcut();

}

AbstractPointcutAdvisor

AbstractPointcutAdvisor继承Ordered接口,增加了Advice(实现了Ordered)排序功能。

	public int getOrder() {
		if (this.order != null) {
			return this.order;
		}
		Advice advice = getAdvice();
		if (advice instanceof Ordered) {
			return ((Ordered) advice).getOrder();
		}
		return Ordered.LOWEST_PRECEDENCE;
	}

Advice  

AOP的目的在于对目标类或目标方法的逻辑增强(如:日志逻辑、统计逻辑、访问控制逻辑等),那么Advice就代表要增强的具体逻辑。Advice接口由AOP联盟(aopalliance.org)定义,它只是一个标记接口,用来强调概念,没有定义任何功能(或者说没有定义增强方式或增强内容)。

//org.aopalliance.aop;
public interface Advice {

}

AspectJ Advice

 AspectJ Advice是使用AspectJ注解的Advice。对应的Advice接口都是标记接口。

AbstractAspectJAdvice

AspectJ Advice,调用Advice方法:invokeAdviceMethod。

	protected Object invokeAdviceMethod(
			@Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)
			throws Throwable {

		return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
	}


//最终调用。object参数为代理对象。
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);

Interceptor

 AOP联盟在Advice的基础上扩展定义了子接口——Interceptor(拦截器)。拦截器定义了通知的增强方式,也就是通过对Joinpoint(连接点)的拦截。

public interface Interceptor extends Advice {

}

Interceptor接口也在强调概念而非功能,也是一个标记接口。 由Interceptor扩展出的ConstructorInterceptor和MethodInterceptor两个子接口,才具体定义了拦截方式。它们一个用于拦截构造方法,一个用于拦截普通方法。

public interface ConstructorInterceptor extends Interceptor  {
	Object construct(ConstructorInvocation invocation) throws Throwable;

}
@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
	Object invoke(MethodInvocation invocation) throws Throwable;

}

spring框架并没有支持AOP联盟对构造方法的拦截,原因很简单,spring框架本身,通过BeanPostProcessor的定义,对bean的生命周期扩展已经很充分了。

        MethodInterceptor只定义了增强方式,我们可以通过实现此接口,自定义具体的增强内容。当然,spring框架也提供了3种预定义的增强内容——BeforeAdvice(前置通知)、AfterAdvice(后置通知)和DynamicIntroductionAdvice(动态引介通知)。BeforeAdvice和AfterAdvice更确切地说是定义了增强内容的执行时机(方法调用之前还是之后);而DynamicIntroductionAdvice比较特殊,它可以编辑目标类要实现的接口列表。最后,spring预定义的通知还是要通过对应的适配器,适配成MethodInterceptor接口类型的对象(如:MethodBeforeAdviceInterceptor负责适配MethodBeforeAdvice)。

重点介绍几个常用拦截器:

  1. MethodBeforeAdviceInterceptor:
            MethodBeforeAdvice(前置通知,其父接口是BeforeAdvice)接口的适配器,用于支持spring预定义的前置通知,在目标方法调用前调用MethodBeforeAdvice.before()。
  2. AspectJAfterAdvice :
            AspectJ框架的后置通知实现,在目标方法执行结束后,return之前,调用配置指定的方法(注意:此方法调用被写在finally块中,无论如何都会得到执行)。
  3. AfterReturningAdviceInterceptor :
            AfterReturningAdvice接口的适配器,用于支持spring预定义的后置通知,在目标方法执行结束后,return之前,调用AfterReturningAdvice.afterReturning()执行(注意:如果目标方法抛出异常,则不会执行这个方法)。
  4. AspectJAfterThrowingAdvice :
            AspectJ框架的异常通知,当目标方法执行时产生异常的时候,指定配置指定的方法。
  5. AspectJAroundAdvice :
            AspectJ框架的环绕通知,直接执行配置指定的方法。
  6. ThrowsAdviceInterceptor :
            spring框架预定义的异常通知的适配器,此适配器接受任意类型的对象,但是要求对象所在类必须声明public的名称为afterThrowing,且参数个数为1个或4个,且最后一个参数为Throwable类型的方法。该适配器会保存该Throwable对象的实际类型到该方法之间的映射,当目标方法执行产生异常时,根据产生的异常类型找到对应的通知方法进行调用。
  7. DelegatingIntroductionInterceptor :
            通过构造方法传入指定的引介对象,每当调用的目标方法是引介接口定义的方法时,都会调用该引介对象的对应方法。
  8. DelegatePerTargetObjectIntroductionInterceptor :
            通过构造函数传入指定的引介接口和接口对应的实现类,该拦截器会为每个目标对象创建新的引介对象(通过调用实现类的默认无参构造)。当调用的方法是引介接口定义的方法时,则调用该新建的引介对象对应的方法。

AdvisorAdapter

Advisor的适配器。默认有3个实现:AfterReturningAdviceAdapter,MethodBeforeAdviceAdapter,ThrowsAdviceAdapter。

public interface AdvisorAdapter {
//是否支持
	boolean supportsAdvice(Advice advice);
//返回拦截器
	MethodInterceptor getInterceptor(Advisor advisor);
}

TargetSource

TargetSource(目标源)是被代理的target(目标对象)实例的来源。TargetSource被用于获取当前MethodInvocation(方法调用)所需要的target(目标对象),这个target通过反射的方式被调用(如:method.invode(target,args))。换句话说,proxy(代理对象)代理的不是target,而是TargetSource,这点非常重要!!!

        那么问题来了:为什么SpringAOP代理不直接代理target,而需要通过代理TargetSource(target的来源,其内部持有target),间接代理target呢?

        通常情况下,一个proxy(代理对象)只能代理一个target,每次方法调用的目标也是唯一固定的target。但是,如果让proxy代理TargetSource,可以使得每次方法调用的target实例都不同(当然也可以相同,这取决于TargetSource实现)。这种机制使得方法调用变得灵活,可以扩展出很多高级功能,如:target pool(目标对象池)、hot swap(运行时目标对象热替换),等等。

        接下来要说的另外一点,可能会颠覆你的既有认知:TargetSource组件本身与SpringIoC容器无关,换句话说,target的生命周期不一定是受spring容器管理的,我们以往的XML中的AOP配置,只是对受容器管理的bean而言的,我们当然可以手动创建一个target,同时使用Spring的AOP框架(而不使用IoC容器)。

public interface TargetClassAware {
	Class<?> getTargetClass();

}

public interface TargetSource extends TargetClassAware {

	/**返回targets类型。
	 */
	@Override
	@Nullable
	Class<?> getTargetClass();

	/**target不变则返回true。getTarget返回同一个对象。*/
	boolean isStatic();

	/**Return a target instance*/
	@Nullable
	Object getTarget() throws Exception;

	/**释放target对象(getTarget获取到的)*/
	void releaseTarget(Object target) throws Exception;

}

不同的TargetSource实现类主要解决的是每次调用getTarget怎么返回的对象实例。

TargetSource包含4个简单实现和3大类实现。

四个简单实现包括:

  1. EmptyTargetSource:
    静态目标源,当不存在target目标对象,或者甚至连targetClass目标类都不存在(或未知)时,使用此类实例。
  2. HotSwappableTargetSource:
    动态目标源,支持热替换的目标源,支持spring应用运行时替换目标对象。
  3. JndiObjectTargetSource:
    spring对JNDI管理bean的支持,static属性可配置。
  4. SingletonTargetSource:
    静态目标源,单例目标源。Spring的AOP框架默认为受IoC容器管理的bean创建此目标源。换句话说,SingletonTargetSource、proxy与目标bean三者的声明周期均相同。如果bean被配置为prototype,则spring会在每次getBean时创建新的SingletonTargetSource实例。

      三大类实现包括:

  1. AbstractBeanFactoryBasedTargetSource:
    此类目标源基于IoC容器实现,也就是说target目标对象可以通过beanName从容器中获取。此类又扩展出:
    (1)SimpleBeanTargetSource:简单实现,直接调用getBean从容器获取目标对象;
    (2)LazyInitTargetSource:延迟初始化目标源,子类可重写postProcessTargetObject方法后置处理目标对象;
    (3)AbstractPrototypeBasedTargetSource:原型bean目标源,此抽象类可确保beanName对应的bean的scope属性为prototype。其子类做了简单原型、池化原型、线程隔离原型这3种实现。
  2. AbstractRefreshableTargetSource:
    可刷新的目标源。此类实现可根据配置的刷新延迟时间,在每次获取目标对象时自动刷新目标对象。
  3. AbstractLazyCreationTargetSource:
    此类实现在调用getTarget()获取时才创建目标对象。

猜你喜欢

转载自blog.csdn.net/demon7552003/article/details/107952339