当前位置: 首页 > news >正文

深入解析Spring AOP核心原理

一 Spring-AOP

1.对SpringAOP理解

        AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低提高程序的可重用性,同时提高了开发的效率。

AOP(Aspect-Oriented Programming:面向切面编程):将哪些与业务无关,却为业务模块所共同调用的逻辑(例如事务处理,日志管理,权限控制)封装抽成一个可重用的模块,这个模块被命名为“切面”,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可拓展性和可维护性。

SpringAOP基于动态代理实现:

  •         如果被代理的对象,已经实现某个接口,则SpringAOP会使用JDK Proxy(反射),基于接口的方式,创建代理对象(JDK动态代理的核心是InvocationHandler接口和Proxy类);
  •         如果被代理的对象,没有实现某个接口,就无法使用JDK Proxy去处理代理了,这时候Spring会使用Cglib,基于继承的方式,生成一个被代理类对象的子类来作为代理(Cglib动态代理的核心是MethodInterceptor接口和Enhancer类)。

2.AOP通知

        概念:AOP将抽取出来的共性功能称为通知;

        通知类型:以通知在上下文中的具体位置作为划分

        解释:通知就是需要增强的方法内容以及执行位置的结合。

        前置通知-before,返回通知-after-returning,异常通知-after-throwing,后置通知-after,环绕通知-around.

<!--    aop配置--><aop:config>
<!--        配置切面--><aop:aspect id="mian" ref="logger">
<!--            配置切点--><aop:pointcut id="cut" expression="execution(public * com.itheima.service.*.*(..))"></aop:pointcut>
<!--            配置通知-->
<!--            前置通知--><aop:before method="beforeLog" pointcut-ref="cut"></aop:before>
<!--            后置通知--><aop:after method="afterLog" pointcut-ref="cut"></aop:after>
<!--            返回通知--><aop:after-returning method="afterReturningLog" pointcut-ref="cut"></aop:after-returning>
<!--            异常通知--><aop:after-throwing method="afterThrowingLog" pointcut-ref="cut"></aop:after-throwing><aop:around method="aroundLog" pointcut-ref="cut"></aop:around></aop:aspect></aop:config>

3.AOP连接点

        AOP将所有的方法都视为连接点,不管是接口里面的抽象方法,还是实现类里面的重写方法,都是连接点。

        解释:具备添加通知能力的方法位置,就是连接点,也就是所有类的所有方法

4.AOP切点

        AOP将可能被抽取共性功能的方法称为切入点,切入点是连接点的子集。

        解释:成功添加了通知方法的位置,就是切点。

<aop:pointcut id="dian" expression="execution(public * com.apesource.service.*.*(..))"/>

5.AOP目标对象

        就是挖掉功能的方法对应的类生的对象,这种对象是无法直接完成最终工作的

        解释:被代理对象,就是目标对象

6.AOP织入

        就是将挖掉的功能回填的动态过程

        解释:将通知添加到切点的过程ing,就是织入。

补充:AOP切面:切点+通知


7.SpringAOP+AspectJ实现

        1.坐标

        <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.7</version></dependency>

        2.配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="accountSErviceImp" class="com.itheima.service.AccountServiceImp"></bean><bean id="logger" class="com.itheima.util.Logger"></bean>
<!--    aop配置--><aop:config>
<!--        配置切面--><aop:aspect id="mian" ref="logger">
<!--            配置切点--><aop:pointcut id="cut" expression="execution(public * com.itheima.service.*.*(..))"></aop:pointcut>
<!--            配置通知-->
<!--            前置通知--><aop:before method="beforeLog" pointcut-ref="cut"></aop:before>
<!--            后置通知--><aop:after method="afterLog" pointcut-ref="cut"></aop:after>
<!--            返回通知--><aop:after-returning method="afterReturningLog" pointcut-ref="cut"></aop:after-returning>
<!--            异常通知--><aop:after-throwing method="afterThrowingLog" pointcut-ref="cut"></aop:after-throwing><aop:around method="aroundLog" pointcut-ref="cut"></aop:around></aop:aspect></aop:config>
</beans>

切点表达式配置语法

        execution(修饰符 返回值 包名称.类名称.方法名称(参数列表))

1.修饰符可以省略代表任意
execution(返回值 包名称.类名称.方法名称(参数列表))
2.返回值可以使用“*”代表任意
execution(* 包名称.类名称.方法名称(参数列表))
3.包名可以使用“*”代表任意名称
execution(* *.*.*.类名称.方法名称(参数列表))
eg:execution(void *.*.*.ServiceImp.findAll())
4.包名可以使用“..”代表任意个数
execution(* *..类名称.方法名称(参数列表))
eg:execution(void *..ServiceImp.findAll())
5.类名与方法名可以使用“*”代表任意
execution(* *...*.*(参数列表))
6.参数列表可以使用".."代表任意个数任意类型
execution(* *...*.*(..))

    如果有参数
int======>int
String===>java.lang.String

二 Spring中bean的一生

1.bean实例化的基本流程-底层

        Spring容器在进行初始化时,会将xml配置的信息封装成一个BeanDefinition对象,所有的BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去,Spring框架在对该Map进行遍历,使用反射创建bean实例对象,创建号的Bean对象存储在一个名为singletonObject的Map集合中,当调用getBean方法时,则最终从该Map集合中取出Bean实例对象返回。

  Bean 实例化的基本流程

加载xml配置文件,解析获取配置中的每个的信息,封装成一个个的BeanDefinition对象;

BeanDefinition存储在一个名为beanDefinitionMap的Map中;

ApplicationContext底层遍历beanDefinitionMap,创建Bean实例对象; 创建好的Bean实例对象,被存储到一个名为singletonObjects的Map中;

当执行applicationContext.getBean(beanName)时,从singletonObjects去匹配Bean实例返回。

2.Spring 还有其他类型的 “后处理器”

后处理器接口作用阶段核心功能
BeanFactoryPostProcessorBean 定义加载后,Bean 实例化前修改 BeanDefinition(Bean 的元信息),例如:
- 动态修改 Bean 的属性值、作用域
- 新增 Bean 定义(如 Spring 的PropertyPlaceholderConfigurer用于解析${}占位符)
BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor 的增强版,更早执行向容器中注册新的 BeanDefinition(比BeanFactoryPostProcessor有更高的优先级),例如:
- 动态扫描并注册 Bean(Spring Boot 的 @ComponentScan 底层用到类似机制)
InstantiationAwareBeanPostProcessorBean 实例化前后(比BeanPostProcessor更早)干预 Bean 的实例化过程,例如:
- 阻止默认实例化,返回自定义 Bean 对象
- 处理属性注入前的逻辑(如 @Autowired 的依赖查找)
DestructionAwareBeanPostProcessorBean 销毁前处理 Bean 销毁前的逻辑,例如:
- 资源释放、状态清理等

Spring的后处理器是Spring对外开放的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,以及动态修改Bean的作用。

Spring主要有两种后处理器

  •         BeanFactoryPostProcessor:Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例话之前执行;
  • BeanPostProcessor:Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行;

3.Bean工厂后处理器 – BeanFactoryPostProcessor

BeanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么 Spring就会 回调该接口的方法,用于对BeanDefinition注册和修改的功能。

修改

        1. 创建BeanFactoryPostProcessor实现类并重写方法

        2. 注入实现类

注册

        1. 创建BeanFactoryPostProcessor实现类并重写方法

        2. 注入实现类

Spring 提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用 于注册BeanDefinition操作

public class MyBeanFactoryPostProcessor2 implements 
BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory 
configurableListableBeanFactory) throws BeansException {}@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry 
beanDefinitionRegistry) throws BeansException {BeanDefinition beanDefinition = new RootBeanDefinition();beanDefinition.setBeanClassName("com.apesource.pojo.Student");beanDefinitionRegistry.registerBeanDefinition("stu",beanDefinition);}
}

4.Bean后处理器-BeanPostProcessor

        Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程。

public class MyBeanPostProcessor implements BeanPostProcessor{public Object postProcessBeforeInitialization(Object bean, String beanName){System.out.println("初始化之前执行");return bean;}public Object postProcessAfterInitialization(Object bean, String beanName){System.out.println("初始化之后执行");return bean;}

5.spring-bean的生命周期

        从Bean实例化之后,及通过反射创建出对象之后,到Bean成为一个完整对象最终存储到单例池中,这个过程被成为SPringBean的生命周期。

大致分为三个阶段

  • Bean的实例化阶段:spring框架会取出BeanDefinition的信息进行判断当前bean的范围是否是singleton的,是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化。
  • Bean的初始化阶段:Bean创建之后还仅仅是个”半成品“,还需要对Bean的实例进行填充,执行一些aware接口方法,执行BeanPostProcessor方法,执行InitializingBean接口的初始化方法。执行自定义初始化init方法等。该阶段是Spring最具技术含量和复杂度的阶段。
  • Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池sinletonObjects去了,及完成了SPringBean的整个生命周期。

6.Spring Bean的初始化过程涉及如下几个过程:

  • Bean实例的属性填充

  • Aware接口属性注入

  • BeanPostProcessorbefore()方法回调

  • InitializingBean接口的初始化方法回调 自定义初始化方法init回调

  • BeanPostProcessorafter()方法回调

8.常用的Aware接口

        Aware接口是一种框架辅助属性注入的一种思想,其他框架中也可以看到类似的接口。框架具备高度封装性,我们接 触到的一般都是业务代码,一个底层功能API不能轻易的获取到,但是这不意味着永远用不到这些对象,如果用到了 ,就可以使用框架提供的类似Aware的接口,让框架给我们注入该对象。

三 循环依赖-解决

      1.  Spring在进行属性注入时,会分为如下几种情况:

                注入普通属性,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去;

                注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,在进行注入操作;

                注入双向对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)。

循环依赖

        含义:多个实体之间相互依赖并形成闭环的情况就叫做"循环依赖",也叫做"循环引用"。

        Spring提供了 三级缓存 存储完整Bean实例半成品Bean实例,用于解决循环引用问题

        在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map

public class DefaultSingletonBeanRegistry ... {//1、最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为"一级缓存"    Map<String, Object> singletonObjects = new ConcurrentHashMap(256);//2、早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为"二级缓存"    Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);//3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三级缓存"    
Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}

注:将对象保存至三级缓存的时候,会包装成ObjectFactory对象录入,未来通过此接口对应的get方法再次提取对象.

UserService和UserDao循环依赖的过程结合上述三级缓存描述一下

  1. UserService 实例化对象,但尚未初始化,将UserService存储到三级缓存
  2. UserService 属性注入,需要UserDao,从缓存中获取,没有UserDao
  3. UserDao实例化对象,但尚未初始化,将UserDao存储到到三级缓存;
  4. UserDao属性注入,需要UserService,从三级缓存获取UserService,UserService三级缓存移入二级缓存
  5. UserDao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存; UserService 注入UserDao;
  6. UserService执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存。

文章转载自:

http://tb2fPeur.jrqbr.cn
http://wD8ClCJJ.jrqbr.cn
http://N1UoIw69.jrqbr.cn
http://A8ZAdRRr.jrqbr.cn
http://3RC5dyJA.jrqbr.cn
http://AJKqX3U5.jrqbr.cn
http://QyNp8xuM.jrqbr.cn
http://fHe6ZWh9.jrqbr.cn
http://FeCeyirM.jrqbr.cn
http://OH4056E3.jrqbr.cn
http://WMXy64b6.jrqbr.cn
http://eumUzLuJ.jrqbr.cn
http://NxmxcxpT.jrqbr.cn
http://PmWsRaPL.jrqbr.cn
http://hBNr5Lnr.jrqbr.cn
http://teUMeCOw.jrqbr.cn
http://01GYarwD.jrqbr.cn
http://fnENh4Fe.jrqbr.cn
http://q0B3AVUB.jrqbr.cn
http://BAsuxYQ5.jrqbr.cn
http://WZR5WgUK.jrqbr.cn
http://Z9dzriZS.jrqbr.cn
http://Doc98Fye.jrqbr.cn
http://hEliyY2x.jrqbr.cn
http://iGMCxEiT.jrqbr.cn
http://1l1os4kp.jrqbr.cn
http://GcMvyLsc.jrqbr.cn
http://juZxEwQn.jrqbr.cn
http://2XCocovp.jrqbr.cn
http://8oZAISqU.jrqbr.cn
http://www.dtcms.com/a/378616.html

相关文章:

  • 虫情测报仪:通过自动化、智能化的手段实现害虫的实时监测与预警
  • Python快速入门专业版(二十二):if语句进阶:嵌套if与条件表达式(简洁写法技巧)
  • 研发文档分类混乱如何快速查找所需内容
  • Java Web实现“十天内免登录”功能
  • CH347使用笔记:CH347在Vivado下的使用教程
  • 【linux内存管理】【基础知识 1】【pgd,p4d,pud,pmd,pte,pfn,pg,ofs,PTRS概念介绍】
  • 详解mcp以及agent java应用架构设计与实现
  • 硬件开发2-ARM裸机开发2-IMX6ULL
  • 电商网站被DDoS攻击了怎么办?
  • Java NIO的底层原理
  • QT 常用控件(概述、QWidget核心属性、按钮类控件、显示类控件、输入类控件、多元素控件、容器类控件、布局管理器)
  • MATLAB2-结构化编程和自定义函数-台大郭彦甫视频
  • 鸿蒙的编程软件的介绍
  • 鸿蒙审核问题——Scroll中嵌套了List/Grid时滑动问题
  • REDPlayer 鸿蒙原生视频播放库组件介绍与使用指南
  • HarmonyOS 应用开发深度解析:ArkUI 声明式 UI 与现代化状态管理最佳实践
  • redis 入门-1
  • Json-rpc通信项目(基于C++ Jsoncpp muduo库)
  • TODO的面试(dw三面、sqb二面、ks二面)
  • Vibe Coding实战项目:用Qwen3-Coder做了个AI跳舞视频生成器
  • Vue 封装Input组件 双向通信
  • 【混合开发】进阶到【大前端++】
  • ZooKeeper Java客户端与分布式应用实战
  • 【复习】计网每日一题---传输层无连接不可靠服务
  • 2025年秋招答疑:AI面试如何破解在线作弊难题?
  • KafKa01:在Windows系统上安装Kafka
  • 【Big Data】Amazon S3 专为从任何位置检索任意数量的数据而构建的对象存储
  • C++:模版进阶
  • 【Canvas与旗帜】圆角红面白边蓝底梅花五星旗
  • 不同局域网远程桌面连接:设置让外网电脑直接windows自带远程桌面访问内网计算机,简单3步实现通用详细教程