spring源码系列8:AOP源码解析之代理的创建(上)

简介: spring源码系列8:AOP源码解析之代理的创建(上)

回顾


首先回顾:

JDK动态代理与CGLIB动态代理

Spring中的InstantiationAwareBeanPostProcessor和BeanPostProcessor的区别

我们得知 JDK动态代理两要素:Proxy+InvocationHandler CGLB动态代理两要素:Enhancer + MethodInterceptor(Callback)

springAOP底层是通过动态代理和CGLB代理实现的。也就是spring最终的落脚点还应该是在Proxy+InvocationHandler 或者Enhancer + MethodInterceptor上。

带着这个期待我们看看spring是如何组织AOP的,并在动态代理之上建立一个AOP体系的。


概念的理解:


  • 通知:  通知可以理解增强器, 就是做的额外工作.
  • 连接点: 可以允许切面插入的点, 这个点可以是方法调用时, 抛出异常时等.
  • 切点: 并不是所有的连接点都需要被切面插入, 切点就是定义哪些连接点可以插入切面.

总结为:从所有连接点中选出部分连接点,  进行拦截,执行额外操作。


组件的理解:


AOP最早由AOP联盟的组织提出的,制定了一套规范.Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范.

注意:各种组件非常之多. 我只把自认为涉及到主线理解的组件列出


AOP联盟规范定义组件

通知(增强器):   不要纠结于太多. 就统一理解为拦截器或者增强器。

image.png

spring定义组件

1.Pointcut定义哪些连接点可以插入切面。提供ClassFilter 与MethodMatcher两种匹配器,匹配连接点。

2.Advisor :  包装Advice和Pointcut .语义:  在哪里拦截, 插入什么样的增强器(在哪里做什么)Advisor=Advice+Pointcut分为

  • IntroductionAdvisor: 引介增强,在不修改某个类的情况下, 让其具备某接口的功能。
  • PointcutAdvisor: 与切点有关的Advisor

3.TargetSource: 对目标对象的封装。

4.Advised接口 : 包装 Advisor  与 TargetSource。 实现类AdvisedSupport:从其两个属性上,真切看出其封装了  Advisor  与 TargetSource

  • TargetSource targetSource = EMPTY_TARGET_SOURCE; 表示目标对象
  • private List advisors = new ArrayList(); 存储Advisor

5.AopProxyAOP代理对象

  • 分JDK代理(JdkDynamicAopProxy)
  • Cglib代理(CglibAopProxy)


image.png

JdkDynamicAopProxy与CglibAopProxy都实现了AopProxy的getProxy()方法,用于返回真正的代理对象。

6.AopProxyFactoryAOP代理策略工厂类。根据条件选择使用JDK还是Cglib的方式来创建代理对象


image.png

7.ProxyCreatorSupport:  从其语义上理解,对创建代理对象的支持。我们从其继承关系上看看他是如何提供支持的。

  • ProxyConfig:  提供aop配置属性。
  • AdvisedSupport:继承ProxyConfig,实现了Advised接口。封装通知(Advise)和TargetSource,并提供对他们的操作
  • ProxyCreatorSupport: 包含AopProxyFactory,AopProxyFactory策略工厂提供对JdkDynamicAopProxy与CglibAopProxy的创建


image.png


8.AbstractAutoProxyCreator: 从其继承关系。我们分析下这个组件。

  • 首先他是一个(BeanPostProcessor)InstantiationAwareBeanPostProcessor 所以他会在spring的Bean生产线createBean()过程中被执行。在BeanDefinition创建Bean的过程中进行拦截,根据要求创建出代理对象。
  • Aware:  可以获取到BeanFactory仓库, 与BeanClassLoader加载器。
  • ProxyCreatorSupport,继承了创建代理的功能。

关于BeanPostProcessor与InstantiationAwareBeanPostProcessor的理解,还是深刻理解Spring中的InstantiationAwareBeanPostProcessor和BeanPostProcessor的区别


image.png

小结

如果总结下组件之间的关系:

开发人员定义切面信息--》spring解析出Advice,切点等配置信息--》AopProxyFactory根据配置创建代理对象--》代理方法执行。

这也是springAOP大部分工作内容。

笼统来看:

  • 《1》定义的解析:
  • 《2》代理的创建:
  • 《3》代理的执行:

下面我们重点看看定义的解析与代理的创建。


代理的生成过程:


代理的执行过程: 我们还是以springboot环境下的AOP实现来讲讲AOP的过程.

1.配置AOP的扩展器,拦截BeanDefiniiton创建Bean的过程:AnnotationAwareAspectJAutoProxyCreator:

首先配置AOP环境

@EnableAspectJAutoProxy注解上的@Import(AspectJAutoProxyRegistrar.class)引入AspectJAutoProxyRegistrar类.AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口. 所以其registerBeanDefinitions()会有一些注入BeanDefiniiton的操作 . registerBeanDefinitions()方法会向仓库中注入一个AnnotationAwareAspectJAutoProxyCreator.

@Override
  public void registerBeanDefinitions(
      AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//注入AnnotationAwareAspectJAutoProxyCreator扩展器
    AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    AnnotationAttributes enableAspectJAutoProxy =
        AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
    if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
      AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
    }
    if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
      AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
    }
  }

AnnotationAwareAspectJAutoProxyCreator 间接继承了AOP扩展工具AbstractAutoProxyCreator, 是一个BeanPostProcessor。

AbstractAutoProxyCreator:

  • 是一个BeanPostProcessor,可以拦截Bean创建过程
  • 继承了ProxyCreatorSupport具有代理类的创建能力。

这样:AOP环境有了。


2.拦截生产过程,创建AOP对象:

createBean()生产线在创建Bean的过程中. 会执行所有的BeanPostProcessor. AnnotationAwareAspectJAutoProxyCreator(AbstractAutoProxyCreator)作为一个BeanPostProcessor也在此时执行.

Spring中的InstantiationAwareBeanPostProcessor和BeanPostProcessor的区别讲过. 实现了InstantiationAwareBeanPostProcessor接口的BeanPostProcessor的会有5个方法伴随Bean创建过程且执行顺序。postProcessBeforeInstantiation() -->postProcessAfterInstantiation-->postProcessPropertyValues-->postProcessBeforeInitialization()-->postProcessAfterInitialization()

过程:

(2.1)首先执行的是postProcessBeforeInstantiation 实例化前置处理方法

@Override
  public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
    Object cacheKey = getCacheKey(beanClass, beanName);
    if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
      if (this.advisedBeans.containsKey(cacheKey)) {
        return null;
      }
      if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return null;
      }
    }
    if (beanName != null) {
      TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
      if (targetSource != null) {
        this.targetSourcedBeans.add(beanName);
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
      }
    }
    return null;
  }

主要分为两部分: (2.1.1)先判断是否应该代理

  • isInfrastructureClass(beanClass)如果是AOP基础设施类, 不代理。指的是Advice、Pointcut、Advisor等AOP相关定义类不应该创建代理
  • shouldSkip()应该跳过的类,不代理。判断的依据:默认是都不跳过的. 子类可能重写其判断依据.AnnotationAwareAspectJAutoProxyCreator的父类AnnotationAwareAspectJAutoProxyCreator重写了shouldSkip方法(《1》并伴随切面定义的解析)。

重点看看shouldSkip()

/*
1.首先查找适于当前Bean的Advisor .通findCandidateAdvisors()去查找
    findCandidateAdvisors()会经过两个渠道获取Advisors.
      > BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans():是从仓库中找实现了Advisor类的Advisor
      > BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors()会从仓库中取出@Aspect注解的切面类,解析出Advisor
   重点说一下其中buildAspectJAdvisors
        >遍历BeanFactory仓库中的BeanDefinition,根据其类型判断其是否是isAspect(): 判断条件是类是否被@Aspect注释,并且没有被aspectj编译过
        >如果是, 根据当前BeanDefinition创建一个(AspectJAdvisorFactory)BeanFactoryAspectInstanceFactory工厂对象.
        >使用AspectJAdvisorFactory 工厂创建List<Advisor>
        >上文已经说过Advisor=Advice+Pointcut, Advice 就是对通知的封装(@Before...), Pointcut 是对切点的封装.
  小结:这次知道我们定义一个切面类, 被解析什么样子了吧
2.判断找到的CandidateAdvisors中有没有当前BeanDefinition,有的话就跳过。
*/
@Override
  protected boolean shouldSkip(Class<?> beanClass, String beanName) {
    // TODO: Consider optimization by caching the list of the aspect names
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    for (Advisor advisor : candidateAdvisors) {
      if (advisor instanceof AspectJPointcutAdvisor) {
        if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
          return true;
        }
      }
    }
    return super.shouldSkip(beanClass, beanName);
  }

(2.1.2) 判断是否有自定义的 TargetSource。 如果有自定义TargetSource ,将当前beanName放入targetSourcedBeans缓存中,直接走创建代理的分支,不会走createBean去创建Bean,这里就是给一个机会。 关于自定义TargetSource这个分支暂时不讲。

if (beanName != null) {
      TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
      if (targetSource != null) {
        this.targetSourcedBeans.add(beanName);
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
      }
    }

总结postProcessBeforeInstantiation工作内容:

  • 解析切面定义
  • 提供一个机会直接返回代理对象,不走createBean()流水线。自定义TargetSource

(2.2)postProcessAfterInstantiation

  • return true ;

(2.3)postProcessBeforeInitialization

  • 返回Bean没有做处理

(2.4)postProcessAfterInitialization() (敲黑板,重点


相关文章
|
2月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
6月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
399 70
|
7月前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
187 4
|
7月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
7月前
|
负载均衡 JavaScript 前端开发
分片上传技术全解析:原理、优势与应用(含简单实现源码)
分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
Java 程序员 索引
Spring的三种创建方式和各种属性的注入(二)
Spring的三种创建方式和各种属性的注入(二)
144 0
Spring的三种创建方式和各种属性的注入(二)
|
3月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
747 0
|
7月前
|
前端开发 Java 数据库
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——Thymeleaf 介绍
本课介绍Spring Boot集成Thymeleaf模板引擎。Thymeleaf是一款现代服务器端Java模板引擎,支持Web和独立环境,可实现自然模板开发,便于团队协作。与传统JSP不同,Thymeleaf模板可以直接在浏览器中打开,方便前端人员查看静态原型。通过在HTML标签中添加扩展属性(如`th:text`),Thymeleaf能够在服务运行时动态替换内容,展示数据库中的数据,同时兼容静态页面展示,为开发带来灵活性和便利性。
325 0

推荐镜像

更多
  • DNS