Spring底层原理深入分析

目录
  • bean生命周期
  • 推断构造方法的底层原理
    • 1、使用哪个构造方法
    • 2、如果有参把哪个bean对象赋值给入参
  • AOP实现原理
  • spring事务
  • @Configuration
  • 循环依赖
    • 为什么会出现循环依赖
    • 提前AOP
    • 第一级缓存singletonObjects
    • 第二级缓存earlySingletonObjects
    • 第三级缓存singletonFactories

bean生命周期

userService.class--->推断构造方法--->对象--->依赖注入--->初始化前(@postConstruct)--->初始化(@afterPropertiesSet)--->初始化后(AOP)--->放入Map(单例池)--->Bean对象

推断构造方法的底层原理

1、使用哪个构造方法

@Component
public class OrderService {
    private UserService userService;
    @Override
    public OrderService (UserService userService) {
       this.userService =  userService;
    }
    @Override
    public void test) {
        System.out.println(userService);
    }
}

在上述例子中,因为写了一个有参构造方法,所以无参构造方法不能用了。

这个时候在userService属性上面没有加@Autowired注解,但是打印发现这个userService对象存在。

orderService是一个bean,spring想去创造这个bean,就要去用构造方法,发现构造方法是有参的,就回去找一个userService对象赋给这个属性。

当加上无参构造方法之后,spring就会去用无参的构造方法,这时候userService没有值;当有多个构造方法的时候,没有明确告知的情况下(告知是用@Autowired注解),spring会去找无参的构造方法,如果没有无参构造方法就直接报错。

对于第一种情况:“orderService是一个bean,spring想去创造这个bean,就要去用构造方法,发现构造方法是有参的,就回去找一个userService对象赋给这个属性。”的userService对象spring是从哪里找出来赋值给属性的呢?

spring首先会去单例池根据beanName即userService找有没有相应的bean对象,如果有就直接赋值给属性。如果没有,就去创建(前提是orderService是一个bean,但是没来得及创建,但如果是多例bean就直接去创建)。

但是如果是去创建的话就有可能出现循环依赖,考虑在userService中有orderService属性,并有一个有参构造方法:

@Component
public class UserService{
    private OrderService orderService ;
    @Override
    public UserService(OrderService orderService ) {
       this.orderService =  orderService ;
    }
    @Override
    public void test) {
        System.out.println(orderService );
    }
}

这时候在创建orderService时需要用到有参构造方法,因为没有userService,这时候就要去创建,创建userService就要用到构造方法,可是完蛋,这是又需要orderService,可是orderService本身就在创建,也就是发生了循环依赖。

2、如果有参把哪个bean对象赋值给入参

假设单例池中有userService对象,可以直接拿出来用,但是怎么在单例池中拿到这个bean呢?因为参数名是可以随意设定的,所以不能直接拿参数名去找,所以要根据类型去单例池找,如果只有一个该类型的bean对象,直接赋值。但是有可能在单例池中存在多个同类型的对象(不同的beanName),这时候再根据参数名去匹配,如果找到了就直接赋值,匹配不上就报错。(先byType,再byName)

AOP实现原理

开启AOP动态代理之后,原本例子中的userService没有值了,因为AOP是发生在初始化之后,而初始化之后拿到的动态代理对象是不会去再去做依赖注入,直接放入了单例池,所以即使属性上面有@Autowired注解也没用。

cglib是基于父子类实现的,代理对象实质是继承了普通对象,并且代理对象中会有一个普通对象的属性、以及被增强的方法,在被增强方法中会先执行切面逻辑,再执行普通对象的方法,而普通对象中是有值的

spring事务

根据上面的AOP实现,事务是基于AOP的实现,生成的是代理类,如果有@Transactional就开启spring事务切面:

1、事务管理器会新建数据库连接,并且设置conn.autocommit = false,因为不管是mybatis还是jdbctemplate都是自动提交,这样就算出现异常,也已经提交了。在新建之后,当target即普通对象去执行test方法市,不管是mybatis还是jdbctemplate操作数据库都要拿到这个连接才能执行sql

2、如果执行完没有抛异常就执行conn.commit

3、

在a方法上的注解加了never,原本应该是要抛出异常的,但是还是顺利写进了数据库,原因是执行a方法的还是userService的普通对象(没有经过AOP增强的对象),就识别不了注解。为什么第一个test方法可以识别?因为一开始是被spring管理的bean对象userService执行,会有相应的逻辑代码去识别注解,识别到注解后生成了代理类和代理对象,然后去运行的test方法,但是执行a方法的时候相当于是 new userService,没有对应的逻辑代码去识别注解。

解决办法:把userService拆出一个新的类,把a方法写进新类

@Configuration

一开始没有加@configuration注解回滚失败。

jdbcTemplate是拿事务管理器新建的数据库连接conn。jdbcTemplate是通过ThreadLocal<Map<DataSource, conn>,线程可能会执行很多方法,可能会有执行不同的datasource,所以是一个map。

因为语法逻辑中jdbcTemplate和事务管理器中是返回新new出来的datasource对象,这样如果没有@configuration,那么jdbcTemplate和事务管理器拿到的是两个不同的datasource对象,那么jdbcTemplate去Map里面找不到对应的conn,只能自己创建新的连接,这样就不能被spring事务管理。

而如果加上了@configuration,那么AppConfig会基于动态代理产生AppConfig代理对象

AppConfig代理对象会先执行自己的代理逻辑,然后去执行普通对象的jdbcTemplate方法,进到父类的jdbcTemplate方法后会执行dataSource方法,但是都是代理对象在执行。代理对象执行dataSource方法的时候先执行代理逻辑:先去spring容器有没有dataSource这个bean,如果没有就创建,如果有就直接返回。

那么就能拿到一样的datasource对象。

循环依赖

为什么会出现循环依赖

首先上面这个例子考虑打破循环依赖。

可以添加一个map<"对象名",对象>,并把实例化AService得到的普通对象放入这个map中,这样在B填充A属性的时候就把AService普通对象注入,B就可以完成创建并放入了单例池,A也就能把单例池中的B对象注入。

但是存在的问题是如果AService在初始化后需要进行AOP,那么最终放入单例池里面的会是AService的代理对象,但是BService拿到的是AService普通对象,因为AService是单例bean,所以只能有一个对象在单例池中,又因为进行了AOP,所以只能是AService的代理对象,并且在其他地方如果依赖了AService,那么应该拿到的是AService的代理对象。

提前AOP

解决方法上述打破循环依赖出现的问题的方法是在把AOP提前,让B创建注入A属性的时候拿到的是AService的代理对象,即提前AOP。如果出现了循环依赖,那么就提前AOP,否则还是在初始化后进行AOP。

如何判断出现了循环依赖?创建一个creatingSet<beanName>,放入正在创建的bean的名称,代表该bean正在创建,后续可以在属性注入的步骤中,如果在单例池中找不到对应的bean对象而在creatingSet中找到了,就可以判定出现了循环依赖。

在判定出现循环依赖之后进行了提前AOP,那么应该什么时候创建AService的代理对象使得BService注入属性的时候拿到的是AService的代理并放入单例池呢?跳到二级缓存

第一级缓存singletonObjects

即单例池

第二级缓存earlySingletonObjects

二级缓存的作用是为了保证单例性:用于出现循环依赖的情况下,会提前产生一个没有经过完整生命周期的早期bean对象,并保存在二级缓存中。否则可能多次创建同一个类型的bean对象。

考虑如下例子:在上述例子中AService再加入一个CService属性,并且在CService也依赖AService属性

在进行bService的生命周期注入aService时会先去二级缓存中根据beanName找有没有aService的bean对象,如果没有就进行AOP并创建aService的代理对象放入二级缓存。

当进行cService的生命周期注入aService时就去二级缓存中找,发现已经有了aService,只可以直接取得

但是二级缓存中存放的不是完整的生命周期的bean对象,所以完成属性填充等动作之后从二级缓存中拿到aService的代理对象放入单例池中。

这时候就不需要在第四步进行AOP了,并且因为AOP的实质是在原有bean的基础上加入切面逻辑,并且AOP后生成的代理对象中还是会有target普通对象

所以在进行属性填充等动作的时候还是对AService的普通对象进行的,那么代理的对象中的普通对象还是可以拿到这些属性值,就相当于代理对象也拿到了

第三级缓存singletonFactories

打破循环依赖的关键,类似于上面提及的map,只是在spring的实现中key值是beanName,value是一个lamda表达式,三级缓存中不去判断是否出现循环依赖,而是只要是支持循环依赖并且是单例的,那么就会加入三级缓存

而lamda表达式返回的是一个对象,执行lamda方法的时候就执行了aop,所以返回的是一个代理对象

在底层源码中,通过第三级缓存来控制第四步中是否需要AOP

如果第三级缓存的map中remove出来是null,整明没有循坏依赖,就这时候进行AOP并返回增强后的代理对象,反之整明之前已经进行了AOP,不需要再进行AOP,直接返回普通对象。

到此这篇关于Spring底层原理深入分析的文章就介绍到这了,更多相关Spring底层原理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringMVC底层执行流程及原理解析

    一个简单的HelloSpringMVC程序 先在web,xml中注册一个前端控制器(DispatcherServlet) <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  • SpringBoot整合log4j日志与HashMap的底层原理解析

    一,SpringBoot与日志 1.springboot整合log4j日志记录 1.在resources目录下面创建日志文件,并引入: 代码如下(示例): #log4j.rootLogger=CONSOLE,info,error,DEBUG log4j.rootLogger=info,error,CONSOLE,DEBUG log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=o

  • Spring AOP底层原理及代理模式

    目录 Spring AOP底层原理代理模式 一.什么是 AOP 二.AOP 底层原理 1. 什么是代理? 2. 什么是静态代理 3. 什么是动态代理 Spring AOP底层原理代理模式 一.什么是 AOP AOP 就是面向切面编程,是 OOP(面向对象编程)的延续. 利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序可用性,同时也提高了开发效率. 通俗一点说,不用修改原代码,可以给原代码增加新的功能. 二.AOP 底层原理 AOP 底层原理是使用动

  • 简单了解SPRINGIOC的底层原理演变过程

    1.传统方式 UserService us = new UserService(); (UserService为一个java类,直接实例化成对象再进行操作) 2.面向接口 UserService us = new UserServiceImp(); (UserService为一个接口,UserServiceImp为接口实现类) 这样会导致web层和业务层产生耦合,程序设计应满足ocp原则 此时,若我想切换实现类,则我需要在代码中将UserServiceImp修改掉 3.工厂模式 创建工厂类,通过

  • 深入研究spring boot集成kafka之spring-kafka底层原理

    目录 前言 简单集成 引入依赖 添加配置 测试发送和接收 Spring-kafka-test嵌入式KafkaServer 引入依赖 启动服务 创建新的Topic 程序启动时创建TOPIC 代码逻辑中创建 PS:其他的方式创建TOPIC 引入依赖 api方式创建 命令方式创建 消息发送之KafkaTemplate探秘 获取发送结果 异步获取 同步获取 KAFKA事务消息 REPLYINGKAFKATEMPLATE获得消息回复 Spring-kafka消息消费用法探秘 @KAFKALISTENER的

  • Spring Boot 底层原理基础深度解析

    目录 1. 底层注解@Configuration 2. 底层注解@Import 3. 底层注解@Conditional 1. 底层注解@Configuration @Configuration 注解主要用于给容器添加组件(Bean),下面实践其用法: 项目基本结构: 两个Bean组件: User.java package com.menergy.boot.bean; /** * 用户 */ public class User { private String name; private Inte

  • spring aop底层原理及如何实现

    前言 相信每天工作都要用spring框架的大家一定使用过spring aop,aop的概念是面向切面编程,相对与传统的面向对象编程oop,aop更关注的是横向的逻辑,比如说一个大型系统中的日志记录,异常处理,性能监控等等,都是各个模块都需要的操作,那样代表着这些操作会散落在系统的各个地方,不易管理且杂乱无章,而aop就是关注的这些,aop将这些操作与业务代码分离,统一成一个个的切面,针对这些个切面进行编程处理.spring aop使得我们的aop开发工作变得简单,这次我就给大家讲讲spring

  • spring声明式事务@Transactional底层工作原理

    目录 引言 工作机制简述 事务AOP核心类释义 @Transactional TransactionAttribute SpringTransactionAnnotationParser AnnotationTransactionAttributeSource TransactionAttributeSourcePointcut TransactionInterceptor BeanFactoryTransactionAttributeSourceAdvisor ProxyTransaction

  • Spring底层原理深入分析

    目录 bean生命周期 推断构造方法的底层原理 1.使用哪个构造方法 2.如果有参把哪个bean对象赋值给入参 AOP实现原理 spring事务 @Configuration 循环依赖 为什么会出现循环依赖 提前AOP 第一级缓存singletonObjects 第二级缓存earlySingletonObjects 第三级缓存singletonFactories bean生命周期 userService.class--->推断构造方法--->对象--->依赖注入--->初始化前(@

  • SQL查询的底层运行原理深入分析

    前言 SQL 语言无处不在.SQL 已经不仅仅是技术人员的专属技能了,似乎人人都会写SQL,就如同人人都是产品经理一样.如果你是做后台开发的,那么CRUD就是家常便饭.如果你是做数仓开发的,那么写SQL可能占据了你的大部分工作时间.我们在理解 SELECT 语法的时候,还需要了解 SELECT 执行时的底层原理.只有这样,才能让我们对 SQL 有更深刻的认识.本文分享将逐步分解SQL的执行过程,希望对你有所帮助. 数据准备 本文旨在说明SQL查询的执行过程,不会涉及太复杂的SQL操作,主要涉及两

  • Spring框架IOC容器底层原理详解

    目录 1.什么是IOC 2.IOC容器的底层原理 3.那么上边提到的三种技术如何实现IOC的呢 4.IOC(接口) 1.什么是IOC IOC – Inverse of Control,控制反转,将对象的创建权力反转给Spring框架! 在java当中一个类想要使用另一个类的方法,就必须在这个类当中创建这个类的对象,那么可能会出现如下情况, 比如A类当中创建着B对象,B类当中有C对象,C类当中有A对象,这个如果一个类出了问题,那么可能会导致这个框架出现问题. Spring 将创建对象的权利给了IO

  • Spring boot整合tomcat底层原理剖析

    目录 本文结论 spring-boot-starter-web内部有什么? TomcatServletWebServerFactory的作用:获取WebServer对象 spring boot启动的时候启动tomcat 获取tomcat的配置 ServletWebServerFactoryCustomizer这个Bean是哪里的? 从源码层面理解spring boot的默认web容器,以及他们是如何关联起来的. 本文结论 源码基于spring boot2.6.6 项目的pom.xml中存在spr

  • Spring底层事务原理解析

    目录 一.@EnableTransactionManagement工作原理 二.Spring事务基本执行原理 四.Spring事务传播机制 五.Spring事务传播机制分类 六.Spring事务强制回滚 七.TransactionSynchronization 一.@EnableTransactionManagement工作原理 开启Spring事务本质上就是增加了一个Advisor,但我们使用 @EnableTransactionManagement注解来开启Spring事务是,该注解代理的功

  • Springboot内嵌tomcat应用原理深入分析

    目录 默认Servlet容器 切换Servlet容器 内嵌tomcat自动配置原理 tomcat自动配置类 tomcat工厂类 何时被调用 onRefresh() finishRefresh() springboot版本:2.2.9.RELEASE. 默认Servlet容器 springboot默认支持tomcat.jetty.undertow作为底层容器, 一旦引入spring-boot-starter-web模块,就默认使用tomcat. <dependency> <groupId&

随机推荐