Spring XML AOP

来源:互联网 发布:smo算法 编辑:程序博客网 时间:2024/05/17 22:37

本文主要是以xml配置的形式来讲解Spring AOP的整个思想与处理过程,Spring AOP的整个过程其实是与IOC的初始化紧密的结合在一起的。大家在看本文之前最好看一看博主之前写的两篇文章,Spring AOP Concepts 和 Spring Extensible XML 这两篇文章。最开始本来想直接写这篇文章的,怕写了大家看了迷惑。就写了前两篇给大家入门。

注意:本文旨在告诉大家AOP的整个过程,很多细节都没有展开,大家有兴趣可以依次展开。

其实Spring AOP的整个过程可以分为三步:

  1. 解析xml配置创建Advise,Aspect,Advisor和Join point对象。
  2. IOC初始化过程中,根据策略创建代理对象以及代理配置信息Advised。
  3. 方法调用,找到生成代理对象与配置信息进行代理调用

1、解析xml

在解析xml配置的时候,会隐式的注册AspectJAwareAdvisorAutoProxyCreator,这个BeanPostProcessor,关于BeanPostProcess这个对象如果大家不熟悉可以查看之前的blog – Spring Container Extension,通知Spring可能需要创建代理对象,并且创建Advise,Aspect和Advisor对象。

1) 注册AspectJAwareAdvisorAutoProxyCreator
下面是Spring AOP注册AspectJAwareAdvisorAutoProxyCreator的时序图:

这里写图片描述

在解析XML的时候会把AspectJAwareAdvisorAutoProxyCreator以BeanDefinitioin的形式注册到Spring容器中,依赖注入的时候会把它解析。

2) 解析XML,生成代理需要的对象

使用AopNamespaceHandler中的ConfigBeanDefinitionParser解析配置文件中的Aspect,Pointcut,Advice。注意这里创建的都是BeanDefinition对象,而不是我们真正需要的对象,Spring最终创建对象都是在依赖注入的步骤。也就是由BeanFactory.getBean()方法触发。

  • Aspect:你想对于目标方法进行增强的方法对应的类,使用SimpleBeanFactoryAwareAspectInstanceFactory创建。
  • Pointcut:切入点,在xml里面一般使用AspectJExpressionPointcut。
  • Advice:通知,你对切入点需要增强的类型,Spring支持以下类型。

    1)Before advice,前置通知,在方法执行前执行
    2)After returning advice,在方法正常执行之后执行(不包含异常)
    3)After throwing advice, 在方法执行的时候包含异常的通知
    4)After (finally) advice,不管执行方法是否正常执行,都会后置的通知
    5)Around advice,环绕通知。

Advice这个对象创建的时候是使用spring ioc当中的构造器来创建:

具体调用位置:

org.springframework.aop.config.ConfigBeanDefinitionParser#createAdviceDefinition

index[0] = METHOD_INDEX,增强的方法,也就是通知对应的方法。(doBefore()),Spring使用MethodLocatingFactoryBean类。
index[1] = POINTCUT_INDEX,AOP作用到的具体方法,就是切点,需要增强的方法。Spring根据配置找到对应的通知类。
index[2] = ASPECT_INSTANCE_FACTORY_INDEX, 切面实例,持有通知对应方法(也就是index[0]),对应的类。Spring使用SimpleBeanFactoryAwareAspectInstanceFactory类。
e.g.

  • AspectJMethodBeforeAdvice(Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif);
  • AspectJAfterAdvice(Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif);

Advisor:默认使用PointcutAdvisor接口(xml配置中具体使用AspectJPointcutAdvisor),它持有Pointcut和Advice对象。
每生成一个Advice都会生成一个对应的Advisor对象,然后Advisor可以根据这个Advice生成相应的advice与pointcut属性。

2、生成代理对象

在讲解生成代理对象之前先给大家阐述一下里面需要使用到的概念:

  • Advised 这个一个接口用于持有AOP动态代理的配置,这个配置包含Interceptors和advice,Advisor以及代理的接口
    注意:这里的Advisor也就是通知,包含这个实现类的所有通知,这个在真正方法调用的时候将会把这个找到真正match的Advisor.再进行调用。
  • AopProxyFactory 生成AopProxy基于AdvisedSupport的配置对象,分为JDK动态代理(JdkDynamicAopProxy)与Cglib动态代理(ObjenesisCglibAopProxy)
  • ProxyFactory 生成代理对象工厂类,用于生成代理对象

以JDK动态代理为例:
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable{}

实现AopProxy,具有获取Proxy的功能。
实现InvocationHandler,具有了创建动态代理的能力。
持有AdvisedSupport类,这个类是实现了Advised接口了,所以它获取动态代理类里面的配置功能。

整个代理对象创建的触发是AspectJAwareAdvisorAutoProxyCreator的父类:AbstractAutoProxyCreator。

这里写图片描述

下面就大概说一下每一步的作用:

  • 第一步,入口方法
  • 第二步,判断这个对象是否需要被代理
  • 第三步是找到匹配当前目标类的所有Advisor(还得上面所说的吗?这个Advisor其实使用的是PointcutAdvisor,包含Pointcut与Advise信息)
  • 第四步,获取到代理对象,并且Spring AOP会为代理对象创建Advised对象其实也就是ProxyFactory,用于代理调用时使用。

3、方法调用

这里以JdkDynamicAopProxy这个生成的代理类为例。我们都知道JDK动态代理最后调用方法会调用InvocationHandler的invoke方法。而我们就可以看看JdkDynamicAopProxy这个类的invoke方法到底干了哪些事:

JdkDynamicAopProxy#invoke

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    MethodInvocation invocation;    Object oldProxy = null;    boolean setProxyContext = false;    TargetSource targetSource = this.advised.targetSource;    Class<?> targetClass = null;    Object target = null;    try {        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {            return equals(args[0]);        }        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {            return hashCode();        }        else if (method.getDeclaringClass() == DecoratingProxy.class) {            return AopProxyUtils.ultimateTargetClass(this.advised);        }        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&                method.getDeclaringClass().isAssignableFrom(Advised.class)) {            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);        }        Object retVal;        if (this.advised.exposeProxy) {            oldProxy = AopContext.setCurrentProxy(proxy);            setProxyContext = true;        }        target = targetSource.getTarget();        if (target != null) {            targetClass = target.getClass();        }        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);        if (chain.isEmpty()) {fancy proxying.            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);        }        else {            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);            retVal = invocation.proceed();        }        Class<?> returnType = method.getReturnType();        if (retVal != null && retVal == target &&                returnType != Object.class && returnType.isInstance(proxy) &&                !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {            retVal = proxy;        }        else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {            throw new AopInvocationException(                    "Null return value from advice does not match primitive return type for: " + method);        }        return retVal;    }    finally {        if (target != null && !targetSource.isStatic()) {            // Must have come from TargetSource.            targetSource.releaseTarget(target);        }        if (setProxyContext) {            // Restore old proxy.            AopContext.setCurrentProxy(oldProxy);        }    }}

1、使用上一步生成的Advised对象根据方法与目标对象获取拦截器chain.
这样就把AOP里面的Advise与方法结合了起来。

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

2、判断拦截器chain是否为空

1)空执行原来方法

retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);

这个方法就是通过反射调用方法,没有什么可讲的。

2)不为空,执行拦截器chain.

创建ReflectiveMethodInvocation对象调用proceed方法。

ReflectiveMethodInvocation#proceed

public Object proceed() throws Throwable {    //  We start with an index of -1 and increment early.    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {        return invokeJoinpoint();    }    Object interceptorOrInterceptionAdvice =            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {        // Evaluate dynamic method matcher here: static part will already have        // been evaluated and found to match.        InterceptorAndDynamicMethodMatcher dm =                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {            return dm.interceptor.invoke(this);        }        else {            // Dynamic matching failed.            // Skip this interceptor and invoke the next in the chain.            return proceed();        }    }    else {        // It's an interceptor, so we just invoke it: The pointcut will have        // been evaluated statically before this object was constructed.        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);    }}

从这个方法中我们可以看到,会依次遍历拦截器链,然后使用proceed()这个方法递归调用来拦截器来。这样就达到了AOP的功能。

0 0
原创粉丝点击