1.@EnableAspectJAutoProxy(proxyTargetClass = true)

就是弄了个"org.springframework.aop.config.internalAutoProxyCreator"::AnnotationAwareAspectJAutoProxyCreator.class的Bean到容器中

2.分析AnnotationAwareAspectJAutoProxyCreator.class类结构

得出了哪些是重要的几个。

3.查看AnnotationAwareAspectJAutoProxyCreator源码

=》查看父类AbstractAutoProxyCreator,有setBeanFactory等等各种继承来的方法

4.又是一顿整理得出了一个图(???)如下

pic1->AOP功能代码分析图

5.分析AbstractAutoProxyCreator。

setBeanFactory有个O标志被重写了,打开,有个initBeanFactory方法,打开,创建了个advisorRetrievalHelper切面工具,又是O重写 创建了aspectJAdvisorsBuilder

6.pic1图里明显看出有两个关键性方法:

postProcessBeforeInstantiation和postProcessAfterInitialization

显然是前后处理逻辑了。肯定先看前逻辑postProcessBeforeInstantiation啊

7.postProcessBeforeInstantiation

断点走起,居然是从这里进去的

所以这个之前分析IOC的createBean里的resolveBeforeInstantiation是AOP实现的关键。

8.分析postProcessBeforeInstantiation细节

8.1 isInfrastructureClass判断是否基础Bean

就是那种AOP本身的比如Advice、Pointcut、AopInfrastructureBean等,基础的这种直接返回不需要代理

8.2 shouldSkip是否要跳过(包含找出切面)

8.2.1找出增强器(那种切面里的@After、@Before的方法)

跟随断点跳跃到之前setBeanFactory创建的advisorRetrievalHelper里去了帮忙实现的找增强器

实际上这里面又分两部分了,一个是找事务的(findCandidateAdvisors)(本次断点容器和缓存中都就没用了直接返回空,不是事务,具体可以看aop源码instan...图)

另一部分是this.aspectJAdvisorsBuilder.buildAspectJAdvisors()

跳进去看了下,它把所有的beanname拿出来找,去找切面类 找到了我写的MyLogAspect

(打了切面切面注解)

然后找到了切面类,调用相关封装的getAdvisors方法去拿增强器了。

其实就是获取切面类的所有方法(getAdvisorMethods)把每个包含增强那些注解的方法包装成Advice 放进缓存。

protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
for (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) {
AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz);
if (foundAnnotation != null) {
return foundAnnotation;
}
}
return null;
}

private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};

然后

switch (aspectJAnnotation.getAnnotationType()) {
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
}

switch判断注解类型 创建不同类型Advice 放进缓存

最终由this.aspectJAdvisorsBuilder.buildAspectJAdvisors()找到了我写的所有切面的增强方法

最终把整个切面类放到了advisorsCache的ConcurrentHashMap,以切面类的beanname作为key,增强器方法advice集合作为value

9.postProcessAfterInitialization

条件断点,当beanname = "calculate"时停住。找出增强器后,看看后续aop是怎么和目标类结合的。

10.postProcessAfterInitialization细节

断点已达。老办法,看看是从哪调来的:

原来是这里,ioc创建完对象并给属性赋完值之后。

回到postProcessAfterInitialization本身,就调了个wrapIfNecessary。逻辑都在里面了

10.1 找对应的增强器

打开断点继续,先主要是个getAdvicesAndAdvisorsForBean然后调findEligibleAdvisors然后就是以前跑过的找事务略。然后主要是findAdvisorsThatCanApply(名字也看得出,它能用的 这个bean对应的切面),进去又是个canApply(candidate, clazz, hasIntroductions),这就是具体判断了:

获取所有方法,用匹配器match去匹配是否匹配上了

循环记下,全部找出来,找增强器就结束了!

10.2 织入生成代理对象

回到wrapIfNecessary开始创建代理对象了:

这里面框起来是涉及注解配置的,对应你配置的:

@EnableAspectJAutoProxy(exposeProxy = true)

@EnableAspectJAutoProxy(proxyTargetClass = true)

如果这两个设置了的话,就会处理实现。

第二个是启动优先cglib,第一个是把代理对象创建好之后暴露出去,这样就能让原本this.dadd去调用方法本来不能增强的情况 可以用

((Calculate) AopContext.currentProxy()).add(numA,numB);也触发增强效果

后面的话就是创建个代理工厂ProxyFactory,把增强器设置进去,最后调用getProxy

一路跳转:

这就是最后网上背背背的东西了。在没有接口和没打优先cglib的话用jdk创建否则cglib创建。

问题:

1.怎么得出来继承结构哪些是重要的、哪些是不需要看不重要的

2.怎么整理出来的?

Spring-AOP源码分析随手记(一)的更多相关文章

  1. spring AOP源码分析(三)

    在上一篇文章 spring AOP源码分析(二)中,我们已经知道如何生成一个代理对象了,那么当代理对象调用代理方法时,增强行为也就是拦截器是如何发挥作用的呢?接下来我们将介绍JDK动态代理和cglib ...

  2. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  3. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  4. Spring AOP 源码分析 - 筛选合适的通知器

    1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...

  5. Spring AOP 源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

  6. Spring AOP源码分析(三):基于JDK动态代理和CGLIB创建代理对象的实现原理

    AOP代理对象的创建 AOP相关的代理对象的创建主要在applyBeanPostProcessorsBeforeInstantiation方法实现: protected Object applyBea ...

  7. 5.2 Spring5源码--Spring AOP源码分析二

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

  8. 5.2 spring5源码--spring AOP源码分析二--切面的配置方式

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

  9. spring aop 源码分析(三) @Scope注解创建代理对象

    一.源码环境的搭建: @Component @Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON,proxyMode = ScopedP ...

  10. 最简 Spring AOP 源码分析!

    前言 最近在研究 Spring 源码,Spring 最核心的功能就是 IOC 容器和 AOP.本文定位是以最简的方式,分析 Spring AOP 源码. 基本概念 上面的思维导图能够概括了 Sprin ...

随机推荐

  1. C#简单的枚举及结构

    using System; namespace program { enum WeekDays { a, b, c = ,//11 赋值以后就变成11,不赋值就是2 d, e, f, g }//不能输 ...

  2. PHP作用域和文件夹操作

    1.作用域      1.1变量作用域      1.全局变量:在函数外面       2.局部变量:在函数里面,默认情况下,函数内部是不会访问函数外部的变量       3.超全局变量:可以在函数内 ...

  3. 定制Dynamics CRM标准导出功能:不能导出指定列的值

    关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复239或者20161203可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...

  4. Docker安装并运行mysql5.6数据库

    1.在/home目录下新建mysql目录 mysql目录中新建三个目录:conf目录.logs目录.data目录,建这些目录的目的是用来挂载docker中的mysql下的目录的. 结果如下: 1.1. ...

  5. 记录Flex布局的属性

    容器属性 flex-dirextion(主轴的方向):>>row(水平) | row-reverse(水平取反) | column(垂直) | column-reverse(垂直取反) f ...

  6. 获取BOM标准用量

    Select dbms_aw.eval_number(listagg(' 1' ||                                        sys_connect_by_pat ...

  7. emacs 帮助相关命令

    emacs 帮助相关命令 如下表: No. 键盘操作 键盘操作对应的函数 回答的问题 01 ctrl-h c describe-key-briefly 这个按键组合将运行哪个函数 02 ctrl-h ...

  8. Linux 查看端口机服务

    Linux如何查看端口 1.lsof -i:端口号 用于查看某一端口的占用情况,比如查看8000端口使用情况,lsof -i:8000 2.2.netstat -tunlp |grep 端口号,用于查 ...

  9. windows宿主机和docker容器设置挂载共享文件夹

    docker容器内的程序经常需要访问.调用宿主机目录中的数据,每次都要导入导出非常麻烦费力. 接下来,一步步实现将宿主机的指定文件夹挂载到docker容器中. 1. 打开Oracle VM Vitua ...

  10. 九、Swift对象存储服务(双节点搭建)

    九.Swift对象存储服务(双节点搭建) 要求:Controoler节点需要2块空盘 Compute节点需要再加2块空盘 本次搭建采用Controller 和 Compute双节点节点做swift组件 ...