首页 > 编程语言 >AOP切面的实现原理【底层源码】

AOP切面的实现原理【底层源码】

时间:2024-06-17 17:30:08浏览次数:21  
标签:parserContext element 源码 切面 AOP 解析 public elt BeanDefinition

AOP是基于IOC的Bean加载来实现的,将切面类的所有切面方法根据使用的注解生成对应的Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor,为后续交给代理增强实现做准备

在这里插入图片描述

这里我们可以很明确的知道,AOP也是在Bean容器中被Spring管理的,根据初始化过程打断点定位到如下:

 @Nullable
public BeanDefinition parseCustomElement(Element ele) {
   
  return this.parseCustomElement(ele, (BeanDefinition)null);
}

@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
   
  String namespaceUri = this.getNamespaceURI(ele);
  if (namespaceUri == null) {
   
    return null;
  } else {
   
    //这里的handler就是AopNamespaceHandler,也就是aop-autoproxy对应的handler
    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    if (handler == null) {
   
      this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
      return null;
    } else {
   
      return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }
  }
}

aop配置标签的解析

AopNamespaceHandler可以理解为注册BeanDefinition的解析器BeanDefinitionParser,将aop:xxx配置标签交给指定的parser来处理

public class AopNamespaceHandler extends NamespaceHandlerSupport {
   
    public AopNamespaceHandler() {
   
    }

    public void init() {
   
        //注册解析<aop:config>配置
        this.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
        //注册解析<aop:aspectj-autoproxy>配置
        this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
        //注册解析<aop:scoped-proxy>配置
        this.registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
        //注册解析<aop:spring-configured>配置
        this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
    }
}

config配置标签的解析

aop:config/由ConfigBeanDefinitionParser这个类处理,作为parser类最重要的就是parse方法

<aop:config>
    <aop:aspect ref="logAspect">
        <aop:pointcut id = pointCutMethod> expression="xxxxxxx"/>
    </aop:aspect>
</aop:config>
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
   
  CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
  parserContext.pushContainingComponent(compositeDef);
  this.configureAutoProxyCreator(parserContext, element);
  List<Element> childElts = DomUtils.getChildElements(element);
  Iterator var5 = childElts.iterator();

  while(var5.hasNext()) {
   
    Element elt = (Element)var5.next();
    String localName = parserContext.getDelegate().getLocalName(elt);
    //如果<aop:config>的下级节点标签为<aop:pointcut>则执行parsePointcut(elt,parserContext)方法
    if ("pointcut".equals(localName)) {
   
      this.parsePointcut(elt, parserContext);
      //如果<aop:config>的下级节点标签为<aop:advisor>则执行parseAdvisor(elt,parserContext)方法
    } else if ("advisor".equals(localName)) {
   
      this.parseAdvisor(elt, parserContext);
      //如果<aop:config>的下级节点标签为<aop:advisor>则执行parseAspect(elt,parserContext)方法
    } else if ("aspect".equals(localName)) {
   
      this.parseAspect(elt, parserContext);//一步步去解析元素,不做过多代码的解析
    }
  }

  parserContext.popAndRegisterContainingComponent();
  return null;
}

aspectj-autoproxy配置标签的解析

<aop:aspectj-autoproxy/>则由AspectJAutoProxyBeanDefinitionParser这个类处理的,我们看下parse 方法

@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
   
    //注册AspectJAnnotationAutoProxyCreator
    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
    //扩展BeanDefinition
    this.extendBeanDefinition(element, parserContext);
    return null;
}
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {
   
    //核心业务处理
    BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    //代理类
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    //注册组件
    registerComponentIfNecessary(beanDefinition, parserContext);
}
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {
   
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

一层层解析,最终发现registerOrEscalateApcAsRequired方法是依赖于AnnotationAwareAspectJAutoProxyCreator类来完成的

AnnotationAwareAspectJAutoProxyCreator(注解切面代理创建类)

在这里插入图片描述

AnnotationAwareAspectJAutoProxyCreator实现了两类接口

  • BeanFactoryAware属于Bean级生命周期接口方法

  • InstantiationBeanPostProcessor和BeanPostProcessor这两个接口实现,一般称它们的实现类为后处理器,是容器级生命周期接口方法

具体参考:Bean的生命周期

根据Bean的生命周期,我们得知,AOP主要的初始化方法就是postProcessBeforeInstantiation和postProcessAfterInitialization中

如下图中红框所示:

在这里插入图片描述

postProcessBeforeInstantiation

解析AOP基础类的流程

AbstractAutoProxyCreator

public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
   
    Object cacheKey = this.getCacheKey(beanClass, beanName);
    if (!StringUtils.hasLength</

标签:parserContext,element,源码,切面,AOP,解析,public,elt,BeanDefinition
From: https://blog.csdn.net/qq_39052339/article/details/139749170

相关文章

  • AOP代理的创建【底层源码】
    代理的创建(源码)创建代理的方法是postProcessAfterInitialization:如果Bean被子类标识为代理,则使用配置的拦截器创建一个代理源码参考:AOP切面底层原理【底层源码】-postProcessAfterInitialization源码部分wrapIfNecessary方法主要用于判断是否需要创建代理,如果bean能......
  • 基于springboot的南门桥社区疫情防疫系统-48138(免费领源码+数据库)可做计算机毕业设计J
    Springboot南门桥社区疫情防疫系统的设计与实现摘 要信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克的课题。针对南门桥社区疫情防疫系统等问题,对南门桥社区......
  • 生成式 AI 服务应用之Langchain 和 DashScope Reranker 彻底改变您的文档检索过程(教程
    介绍在当今信息泛滥的时代,找到所需的确切文档似乎是一件不可能完成的事情。传统搜索引擎经常让您在无关内容中苦苦挣扎,浪费宝贵的时间。但不要担心,因为有一对强大的组合正在等待彻底改变您的搜索体验:Langchain和DashScopeReranker。推荐文章《如何使用CodeLlama......
  • 精选了10个Python实战项目(附源码),拿走即用!
    ① 猜字游戏在这个游戏中,你必须一个字母一个字母的猜出秘密单词。如果你猜错了一个字母,你将丢掉一条命。正如游戏名那样,你需要仔细选择字母,因为你的生命数量非常有限。importrandom#生命次数lives=3#神秘单词,随机选择words=['pizza','fairy','teeth','......
  • 基于Java+Vue的采购管理系统:实现采购业务数字化(全套源码)
    前言:采购管理系统是一个综合性的管理平台,旨在提高采购过程的效率、透明度,并优化供应商管理。以下是对各个模块的详细解释:一、供应商准入供应商注册:供应商通过在线平台进行注册,填写基本信息和资质文件。资质审核:系统对供应商提交的资质文件进行自动或人工审核,确保供应商符......
  • SpringBoot开发Activiti工作流实现审批流程(全套源码)
    前言activiti工作流引擎项目,企业erp、oa、hr、crm等企事业办公系统轻松落地,一套完整并且实际运用在多套项目中的案例,满足日常业务流程审批需求。一、项目形式springboot+vue+activiti集成了activiti在线编辑器,流行的前后端分离部署开发模式,快速开发平台,可插拔工作流服务。工......
  • 俄罗斯方块小游戏(附源码)
    游戏展示一.导包importturtleimportrandom二.定义一个Block类定义一个Block类,用于表示游戏中的方块,包含颜色和形状。classBlock:def__init__(self,color,tiles):self.color=colorself.tiles=tiles三.定义了7个不同的Block对象定......
  • ThreadLocal 核心源码分析
    ThreadLocal简介多线程访问同一个共享变量的时候容易出现并发问题,特别是多个线程对一个变量进行写入的时候,为了保证线程安全,一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线程安全性。ThreadLocal是除了加锁这种同步方式之外的一种保证和规避多线程访问出......
  • 基于springboot的球队训练信息管理系统源码数据库
    传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,在计算机上安装球队训练信息管理系统软件来发挥其高效地信息处理的作用,可以规范信息管理流程,让管理工作可以系统化和程序化,同时,球队训练信息管理系......
  • 基于springboot的青年公寓服务平台源码数据库
    传统信息的管理大部分依赖于管理人员的手工登记与管理,然而,随着近些年信息技术的迅猛发展,让许多比较老套的信息管理模式进行了更新迭代,房屋信息因为其管理内容繁杂,管理数量繁多导致手工进行处理不能满足广大用户的需求,因此就应运而生出相应的青年公寓服务平台。本青年公寓服务......