Spring XML AOP

本文主要是以xml配置的形式来讲解Spring AOP的整个思想与处理过程,Spring AOP的整个过程其实是与IOC的初始化紧密的结合在一起的。大家在看本文之前最好看一看博主之前写的两篇文章,Spring AOP ConceptsSpring 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{

    @Override
    public Object getProxy() {
        // 获取代理对象
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        // 获取代理对象
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 代理对象调用方法
    }
}

从上面这个类我们可以得出以后的结论:

  • 实现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的功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值