首页 > 编程语言 >【Spring AOP】【七】Spring AOP源码解析-代理对象执行过程

【Spring AOP】【七】Spring AOP源码解析-代理对象执行过程

时间:2023-02-21 20:47:45浏览次数:43  
标签:拦截器 return Spring advice AOP 源码 advisor MethodInterceptor method

1  前言

不知道你现在清晰了没,我们一路从AOP的基础概念,到AOP配置的解析,到AOP在Bean的生命周期的切入时机以及创建代理的过程一步步走下来,脑瓜还清晰不,那我们是不是就该到了真正去调用某个对象方法的时候,就是调用了代理对象的某个方法,是不是就是执行代理类的增强逻辑代码啦。我们还是拿JDK为例哈,是不是就是真正执行JDK代理里的invoke逻辑,那我们本节就看下invoke里是怎么串联多个通知器的,又是怎么去执行的哈。

2  源码分析

2.1  方法通读

JdkDynamicAopProxy的invoke开始走起:

/**
 * 看之前 问:这三个参数都是什么?
 * @param proxy   代理对象
 * @param method  当前执行的方法
 * @param args    方法的参数
 * @return
 * @throws Throwable
 */
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;
    /**
     * 问:advised.targetSource 放的啥?
     * 答:放的就是我们的原始对象
     */
    TargetSource targetSource = this.advised.targetSource;
    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;
        // 这个懂了没 就是要不要暴露出去代理对象 里边其实就是个ThreadLocal
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
        // target 就是原始对象
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);
        // 这个筛选出适合当前方法的拦截器 并对我们的Advisor进行了转换成了MethodInterceptor类型的
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        // 如果拦截器链为空
        if (chain.isEmpty()) {
            // 反射执行目标方法
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // 创建方法调用器 并要执行的拦截器传进去
            MethodInvocation invocation =
                    new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // 执行拦截器链  也就是说proceed()这个方法里会循环调用或者递归调用
            retVal = invocation.proceed();
        }
        // 获取返回值类型
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target &&
                returnType != Object.class && returnType.isInstance(proxy) &&
                !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            // 如果方法返回值为 this,即 return this; 则将代理对象 proxy 赋值给 retVal
            retVal = proxy;
        }
        // 如果返回值类型为基础类型,比如 int,long 等,当返回值为 null,抛出异常
        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 {
        // 这两个咱没看懂  为什么释放源  AopContext怎么又塞一遍
        if (target != null && !targetSource.isStatic()) {
            // Must have come from TargetSource.
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

可以看到整体的大概执行流程是:

  1. 基础校验
  2. 是否开启暴漏代理对象即expose-proxy 是否为 true,开启的话放进AOP上下文对象里
  3. 筛选出当前方法适合的拦截器
  4. 拦截器为空就执行目标方法,否则的话创建调用链ReflectiveMethodInvocation,并执行调用链proceed()
  5. 处理返回值,并返回

好,那我们重点看下第3、第4点。

2.2  getInterceptorsAndDynamicInterceptionAdvice获取拦截器

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
    // 缓存
    MethodCacheKey cacheKey = new MethodCacheKey(method);
    List<Object> cached = this.methodCache.get(cacheKey);
    if (cached == null) {
        // 调用这里
        cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                this, method, targetClass);
        this.methodCache.put(cacheKey, cached);
    }
    return cached;
}
/**
 *
 * @param config the AOP configuration in the form of an Advised object
 * @param method the proxied method
 * @param targetClass the target class (may be {@code null} to indicate a proxy without
 * target object, in which case the method's declaring class is the next best option)
 * @return
 */
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
        Advised config, Method method, @Nullable Class<?> targetClass) {
    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
    // 我们的通知器列表
    Advisor[] advisors = config.getAdvisors();
    // 返回结果
    List<Object> interceptorList = new ArrayList<>(advisors.length);
    Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
    Boolean hasIntroductions = null;
    // 遍历每个通知器
    for (Advisor advisor : advisors) {
        // PointcutAdvisor类型的  我们基本上都是这种类型的
        if (advisor instanceof PointcutAdvisor) {
            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
            // 调用 ClassFilter 对 bean 类型进行匹配,无法匹配则说明当前通知器,不适合应用在当前 bean 上
            if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                // 方法匹配 具体匹配过程我们仔细看  先看大流程
                MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                boolean match;
                if (mm instanceof IntroductionAwareMethodMatcher) {
                    if (hasIntroductions == null) {
                        hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
                    }
                    match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
                }
                else {
                    match = mm.matches(method, actualClass);
                }
                // 如果匹配到了 把当前的advisor要转换成MethodInterceptor
                if (match) {
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    // 若 isRuntime 返回 true,则表明 MethodMatcher 要在运行时做一些检测
                    if (mm.isRuntime()) {
                        for (MethodInterceptor interceptor : interceptors) {
                            // 又把interceptor 包装成 InterceptorAndDynamicMethodMatcher
                            interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                        }
                    }
                    else {
                        interceptorList.addAll(Arrays.asList(interceptors));
                    }
                }
            }
        }
        else if (advisor instanceof IntroductionAdvisor) {
            IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
            if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
                // 把当前的advisor要转换成MethodInterceptor
                Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }
        else {
            Interceptor[] interceptors = registry.getInterceptors(advisor);
            interceptorList.addAll(Arrays.asList(interceptors));
        }
    }
    return interceptorList;
}

可以看到通知器转换为拦截器:

@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    List<MethodInterceptor> interceptors = new ArrayList<>(3);
    // advisor拿出来里面的Advice 就是我们的AfterAdvice什么的 你知道的
    Advice advice = advisor.getAdvice();
    // 本身就是MethodInterceptor类型的直接加进去
    if (advice instanceof MethodInterceptor) {
        interceptors.add((MethodInterceptor) advice);
    }
    /**
     * 我们往上看看 其实创建代理的过程中我们就用过这个三个适配器 它是把Advice包装成Advisor
     * 那么我们这里在此用到了 他们是把advisor包装成对应的MethodInterceptor
     */
    for (AdvisorAdapter adapter : this.adapters) {
        if (adapter.supportsAdvice(advice)) {
            interceptors.add(adapter.getInterceptor(advisor));
        }
    }
    if (interceptors.isEmpty()) {
        throw new UnknownAdviceTypeException(advisor.getAdvice());
    }
    return interceptors.toArray(new MethodInterceptor[0]);
}

以上就是获取拦截器的过程。这里简单总结一下以上源码的执行过程,如下:

  1. 从缓存中获取当前方法的拦截器链
  2. 若缓存未命中,则调用 getInterceptorsAndDynamicInterceptionAdvice 获取拦截器链
  3. 遍历通知器列表
  4. 对于 PointcutAdvisor 类型的通知器,这里要调用通知器所持有的切点(Pointcut)对类和方法进行匹配,匹配成功说明应向当前方法织入通知逻辑
  5. 调用 getInterceptors 方法对非 MethodInterceptor 类型的通知进行转换
  6. 返回拦截器数组,并在随后存入缓存中

对于通知器转换拦截器,我调试的时候发现有两个需要转换,一个是前置通知一个是返回通知,我们看下两者通过适配器的转换:

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof MethodBeforeAdvice);
    }

    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }

}
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {

    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof AfterReturningAdvice);
    }

    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
        return new AfterReturningAdviceInterceptor(advice);
    }

}

其实也就是拿对应的拦截器进行一个包装即可,这样我们即可得到当前方法应该执行的拦截器链了。

至于启动拦截器链的过程,以及先后的执行顺序,我打算单独拿一章出来讲,因为这个很重要,也比较细节,我们平时使用应该也能碰到,所以我们单独拿一张出来讲解。

3  小结

我们大致看到了invoke也就是实际代理对象执行的一个具体过程,那我们下节结合调试着重讲一下这个拦截器链的具体顺序和执行过程。

标签:拦截器,return,Spring,advice,AOP,源码,advisor,MethodInterceptor,method
From: https://www.cnblogs.com/kukuxjx/p/17139591.html

相关文章

  • AOP的实现方式一
    一.什么是AOPAOP为AspectOrientedProgramming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发......
  • AOP
    *概念:面向切面编程  OOP:面向对象编程*作用:在不惊动原始设计的基础上为其进行功能增强*spring理念:无侵入式编程*核心概念: ......
  • Spring框架3--Web
    Spring框架之WebJavaweb三大组件和四大域顺便:Javaweb中的四大域,作用范围如下:PageContext<Request<Session<ServletContext(Application)域对象属性的作用范围......
  • SpringBoot+Lock4j实现高性能分布式锁
    1.简介  在分布式业务开发中,很多场景都需要添加分布式锁。在具体实践过程中,研发人员都需要自行实现,导致实现方式不统一,代码风格迥异,难以维护。  在Mybatis-Plus生态......
  • springJdbc实战
     一、ShardingSphere二、ShardingJDBC实战1、核心概念:2、测试项目介绍3、快速实战4、ShardingJDBC的分片算法5、ShardingSphere的SQL使用限制6、分库分表带来的......
  • springboot读取配置信息,环境变量的方法
    前提配置文件一般是值resources目录下的application.properties或application.yml,其中保存着配置信息代码中实现配置注入的方法使用@Value注解@Value("${test.msg}")......
  • SpringBoot多数据源
    重点概念多数据源在配置之后是根据service的实现类上面的注解来确定生效范围的,一个实现类可以根据注解代表一个数据库ServiceImplimportcom.baomidou.dynamic.datasou......
  • 如何获取spring aop代理类的被代理类或者说是获取目标类
    @RestControllerpublicclassTestController{@AutowiredprivateConsulRetryRegistryconsulRetryRegistry;@RequestMapping("server/offline")......
  • 七、Spring整合MyBatis
    整合思路将SqlSessionFactory配置到Spring容器中<!--加载jdbc.properties--><context:property-placeholderlocation="classpath:jdbc.properties"/><!--配置数据源-......
  • Flutter异常监控 - 叁 | 从bugsnag源码学习如何追溯异常产生路径
    如果觉得文章对你有帮助,点赞、收藏、关注、评论,一键四连支持,你的支持就是我创作最大的动力。❤️本文原创​​听蝉​​公众号:码里特别有禅欢迎关注原创技术文章第一时间推......