Spring学习小结_2

news2024/10/5 15:31:57

文章目录

  • 篇1
  • 12 Bean的生命周期
    • Bean实例属性填充
    • 循环引用
    • Aware接口
    • Spring IoC 整体流程总结
  • 13 Spring整合MyBatis剖析


篇1

Spring学习小结_1
https://blog.csdn.net/m0_58730471/article/details/130075657?spm=1001.2014.3001.5501

12 Bean的生命周期

Spring Bean的生命周期是从 Bean 实例化之后(在内存中开辟空间),即通过反射创建出对象之后,到Bean成为一个完整对象,最终存储到单例池中,这个过程被称为Spring Bean的生命周期(完整的说还要加上消亡)。

Spring Bean的生命周期大体上分为三个阶段:
Bean的实例化阶段:Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是singleton的,是否是延迟加载的,是否是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化;
Bean的初始化阶段:Bean创建之后还仅仅是个"半成品",还需要对Bean实例的属性进行填充、执行一些Aware接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法、执行自定义初始化init方法等。
Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池singletonObjects中去了,即完成了Spring Bean的整个生命周期。

Bean的初始化阶段是Spring最具技术含量和复杂度的阶段,Aop增强功能,后面要学习的Spring的注解功能等、spring高频面试题Bean的循环引用问题都是在这个阶段体现的,所以重点阐述初始化阶段。

Bean的初始化阶段

Spring Bean的初始化过程涉及如下几个过程:
⚫ Bean实例的属性填充
⚫ Aware接口属性注入
⚫ BeanPostProcessor的before()方法回调
⚫ InitializingBean接口的初始化方法回调
⚫ 自定义初始化方法init回调
⚫ BeanPostProcessor的after()方法回调

Bean实例属性填充

Spring在进行属性注入时,会分为如下几种情况:
注入普通属性,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去;

    <bean class="com.hyl.service.impl.UserServiceImpl" id="userService">
        <property name="name" value="hyl"/>
    </bean>

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

    <bean class="com.hyl.mapper.impl.UserDaoImpl" id="userDao"/>
    
    <bean class="com.hyl.service.impl.UserServiceImpl" id="userService">
        <property name="userDao" ref="userDao"/>
    </bean>

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

	<bean class="com.hyl.mapper.impl.UserDaoImpl" id="userDao">
        <property name="userService" ref="userService"/>
    </bean>
    
    <bean class="com.hyl.service.impl.UserServiceImpl" id="userService">
        <property name="userDao" ref="userDao"/>
    </bean>

循环引用

多个实体之间相互依赖并形成闭环的情况就叫做"循环依赖",也叫做"循环引用"
在这里插入图片描述

    <bean class="com.hyl.service.impl.UserServiceImpl" id="userService">
        <property name="userDao" ref="userDao"/>
    </bean>

	<bean class="com.hyl.mapper.impl.UserDaoImpl" id="userDao">
        <property name="userService" ref="userService"/>
    </bean>

在这里插入图片描述

执行创建过程:
在这里插入图片描述

Spring提供了三级缓存存储 完整Bean实例半成品Bean实例 ,用于解决循环引用问题
在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
	//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);

}

UserService和UserDao循环依赖的过程结合上述三级缓存描述一下
UserService 实例化对象,但尚未初始化,将UserService存储到三级缓存;
UserService 属性注入,需要UserDao,从缓存中获取,没有UserDao;
UserDao实例化对象,但尚未初始化,将UserDao存储到到三级缓存;
UserDao属性注入,需要UserService,从三级缓存获取UserService,UserService从三级缓存移入二级缓存;
UserDao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存;
UserService 注入UserDao;
UserService执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存。

图解:

按上述配置文件中的bean书写顺序,容器创建,先去实例化id=userService的bean(在内从中开辟空间),当前属于半成品bean,存入三级缓存中,之后进行属性的注入(需要注入id=userDao的bean),现在容器中还没有id=userDao的bean,所以暂定userService的后续创建,拐去创建userDao,同理也是先实例化userDao,当前也属于半成品bean,也存入三级缓存

//3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三级缓存"
Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);

由源码可知放入三级缓存中的半成品bean外边是套了一层ObjectFactory的。

在这里插入图片描述

userDao在实例化存入三级缓存后,进行属性的注入。唉,发现,他需要注入userService,就先去一级缓存(单利池)中找,没有找到,再去二级缓存中找,还是没有,就去三级缓存中找,发现有需要的userService,就将其注入,并将三级缓存中的userService删除,在将userService存入二级缓存。

在这里插入图片描述

userDao在属性填充完后,继续执行后续的其他初始化流程,都执行完毕,称为一个完整的bean后,就从三级缓存中删除原来的userDao,将完整的userDao放入一级缓存(单利池)中。

在这里插入图片描述

之后userService结束暂停状态,从一级缓存中查找userDao,现在userDao已经存在了,就进行对应的属性注入,之后在执行完其他的初始化流程,得到一个完整的userService,就将二级缓存中的userService删除,将完整的userService也存入一级缓存(单利池)中。

在这里插入图片描述

Aware接口

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

Aware接口回调方法作用
ServletContextAwaresetServletContext(ServletContext context)Spring框架回调方法注入ServletContext对象,web环境下才生效
BeanFactoryAwaresetBeanFactory(BeanFactory factory)Spring框架回调方法注入beanFactory对象
BeanNameAwaresetBeanName(String beanName)Spring框架回调方法注入当前Bean在容器中的beanName
ApplicationContextAwaresetApplicationContext(ApplicationContext applicationContext)Spring框架回调方法注入applicationContext对象

Spring IoC 整体流程总结

在这里插入图片描述

13 Spring整合MyBatis剖析

xml整合形式
导入坐标

		<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
       <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.26</version>
        </dependency>

applicationConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">
       
    <!--读取外部数据库配置文件-->
    <context:property-placeholder location="classpath:db.properties"/>
    
    <!--创建userService对应的bean-->
    <bean class="com.hyl.service.impl.UserServiceImpl" id="userService" >
        <property name="userMapper" ref="userMapper"/>
    </bean>
    
	<!--配置数据源,使用el表达式,从db.properties中加载-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driverClassName}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
    </bean>
    
    <!--创建SqlSessionFactory存入容器中-->
    <bean id="sessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!--扫描指定路径下的mapper接口,自动创建对象,还是通过
    			this.getSqlSession().getMapper(this.mapperInterface)-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.hyl.mapper"/>
    </bean>

</beans>

原理剖析
整合包里提供了一个SqlSessionFactoryBean和一个扫描Mapper的配置对象,SqlSessionFactoryBean一旦被实例化,就开始扫描Mapper并通过动态代理产生Mapper的实现类存储到Spring容器中。相关的有如下四个类:

  1. SqlSessionFactoryBean:需要进行配置,用于提供SqlSessionFactory;
  2. MapperScannerConfigurer:需要进行配置,用于扫描指定mapper注册BeanDefinition;
  3. MapperFactoryBean:Mapper的FactoryBean,获得指定Mapper时调用getObject方法;
  4. ClassPathMapperScanner:definition.setAutowireMode(2) 修改了自动注入状态,所以
    MapperFactoryBean中的setSqlSessionFactory会自动注入进去。

配置SqlSessionFactoryBean作用是向容器中提供SqlSessionFactory
SqlSessionFactoryBean实现了FactoryBean和InitializingBean两个接口,所以会自动执行getObject() 和afterPropertiesSet()方法

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
	...
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(this.dataSource, "Property 'dataSource' is required");
        Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
        Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
        this.sqlSessionFactory = this.buildSqlSessionFactory();
    }

    public SqlSessionFactory getObject() throws Exception {
        if (this.sqlSessionFactory == null) {
            this.afterPropertiesSet();
        }

        return this.sqlSessionFactory;
    }
    ....
}

配置MapperScannerConfigurer作用是扫描Mapper,向容器中注册Mapper对应的MapperFactoryBean
MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor和InitializingBean两个接口,会在postProcessBeanDefinitionRegistry方法中向容器中注册MapperFactoryBean

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {

    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {
            this.processPropertyPlaceHolders();
        }

        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        scanner.set....
        ...
        scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
    }
}

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
		...
	    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
        if (beanDefinitions.isEmpty()) {
            this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
            this.processBeanDefinitions(beanDefinitions);
        }

        return beanDefinitions;
    }
   ...
   private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
	   ....
		//设置Mapper的beanClass是org.mybatis.spring.mapper.MapperFactoryBean
		definition.setBeanClass(this.mapperFactoryBeanClass);
		.....
		//设置MapperBeanFactory 进行自动注入
		//autowireMode取值:1是根据名称自动装配,2是根据类型自动装配
		definition.setAutowireMode(2); 
	}
}
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
	public int scan(String... basePackages) {
	    ...
		this.doScan(basePackages);
		...
	}
	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		...
		//将扫描到的类注册到beanDefinitionMap中,此时beanClass是当前类全限定名
		this.registerBeanDefinition(definitionHolder, this.registry);
		...
		return beanDefinitions;
	}
}
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
	public MapperFactoryBean(Class<T> mapperInterface) {
		this.mapperInterface = mapperInterface;
	}
	public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
		this.sqlSessionTemplate = this.createSqlSessionTemplate(sqlSessionFactory);
	}
	public T getObject() throws Exception {
		return this.getSqlSession().getMapper(this.mapperInterface);
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/464383.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

当下火爆出圈的 ChatGPT ,你了解多少?

ChatGPT 是什么&#xff1f;ChatGPT 有什么特点&#xff1f;ChatGPT 可以做什么&#xff1f;ChatGPT 初体验 当下 AI 聊天程序 ChatGPT 可谓如火如荼&#xff0c;因它给出的答案通常更为合理且更有人情味&#xff0c;全网讨论度非常高。 ChatGPT 是什么&#xff1f; ChatGPT 是…

仪表板展示 | X-lab开放实验室GitHub开源项目洞察大屏

背景介绍 X-lab开放实验室是一个开源软件产业开放式创新的共同体&#xff0c;由来自国内外著名高校、创业公司、部分互联网与IT企业的专家学者与工程师所构成&#xff0c;目前已在包括开源治理标准制定、开源社区行为度量与分析、开源社区流程自动化、开源全域数据治理与洞察等…

CSS - 实现容器溢出后隐藏滚动条并且能正常滚动,盒子高度超出后不显示滚动条但是能正常滚动(附带详细示例,完美解决方案)

前言 网上很多都会使用 JS 来实现&#xff0c;其实纯 CSS 就能完成。 本文实现了 当外层容器盒子溢出时&#xff0c;去掉滚动条的显示&#xff08;但能正常滚动&#xff09;&#xff0c;适用于 Vue、React 等全部前端项目&#xff0c; 您可以直接复制示例源码&#xff0c;运行…

Android性能优化—ViewPagers + Fragment缓存优化

大家看标题&#xff0c;可能会有点儿懵&#xff0c;什么是ViewPagers&#xff0c;因为在很久之前&#xff0c;我们使用的都是ViewPager&#xff0c;但是现在更多的是在用ViewPager2&#xff0c;因此用ViewPagers&#xff08;ViewPager、ViewPager2&#xff09;来代替两者&#…

第10届蓝桥杯省赛真题剖析-2019年3月24日Scratch编程初中级组

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第126讲。 第10届蓝桥杯省赛&#xff0c;这是2019年3月24日举办的省赛Scratch考试真题&#xff0c;比赛是在线下举办的…

分组双轴图:揭示数据中的关联性和趋势变化

简介 分组双轴图是一种数据可视化图表&#xff0c;指有多个&#xff08;≥2&#xff09;Y轴的数据图表&#xff0c;多为分组柱状图折线图的结合&#xff0c;图表显示更为直观&#xff0c;可以很好地展示不同指标之间的关系&#xff0c;帮助用户更好地理解数据&#xff0c;做出…

深度学习 - 44.MMOE 与 Gate 之多目标学习

目录 一.引言 二.摘要 Abstract 三.介绍 Introduction 四.相关工作 RELATED WORK 1.DNN 中的多任务学习 2.SubNet 集成与 Expert 混合 3.多任务学习应用 五.建模方法 MODELING APPROACHES 1.Shared-bottom Multi-task Model 2.Mixture-of-Experts 3.Multi-gate Mixt…

美团B端“加速度”

配图来自Canva可画 一提起本地生活服务&#xff0c;相信绝大多数人并不会感到陌生&#xff0c;人们经常使用的餐饮外卖&#xff0c;便是本地生活服务的重要组成部分之一。而在消费者线上消费习惯逐渐养成、本地生活服务需求日渐增长等多方因素的共同影响下&#xff0c;本地生活…

BUUCTF pwn——picoctf_2018_rop chain

checksec && 运行 ida main函数调用vuln函数 名为vuln的函数存在溢出 名为flag的函数&#xff0c;通过校验可直接getflag 具体校验过程看图&#xff0c;只有win1和win2均为真&#xff0c;并且a1的值等于0xDEADBAAD才能getflag 变量win2的真假性通过win_function2函…

三个练手的软件测试实战项目(附全套视频跟源码)偷偷卷死他们

项目一&#xff1a;12306抢票项目 项目测试目的 学会Selenium定位web元素的方法 熟练浏览器调试工具使用 项目主体步骤 1&#xff09; 人工走一遍流程&#xff0c;对自动化的流程心中有数 2&#xff09; 按步骤拆分&#xff0c;然后对每一个小步骤编写自动化脚本 3&#xff…

FreeRTOS(三)——应用开发(一)

文章目录 0x01 FreeRTOS文件夹FreeRTOSConfig.h文件内容上面定义的宏决定FreeRTOS.h文件中的定义0x02 创建任务创建静态任务过程configSUPPORT_STATIC_ALLOCATION创建动态任务过程configSUPPORT_DYNAMIC_ALLOCATION 0x03 FreeRTOS启动流程启动流程概述 0x04 任务管理任务调度器…

python基于轻量级YOLOv5的生猪检测+状态识别分析系统

在我之前的一篇文章中有过生猪检测盒状态识别相关的项目实践&#xff0c;如下&#xff1a; 《Python基于yolov4实现生猪检测及状态识》 感兴趣的话可以自行移步阅读&#xff0c;这里主要是基于同样的技术思想&#xff0c;将原始体积较大的yolov4模型做无缝替换&#xff0c;使…

关于python异常的总结

Python异常是在程序执行时发生的错误&#xff0c;可能会导致程序终止运行。 在Python中&#xff0c;异常处理是一种机制&#xff0c;它允许开发人员在程序发生异常时捕获、处理和报告这些异常&#xff0c;以便程序可以继续运行或在出现异常时进行优雅的退出。 在Python中&…

大数据之入门开发流程介绍

目录&#xff1a; 1、大数据的开发大致流程2、技术导图 1、大数据的开发大致流程 1.1 数据收集 大数据处理的第一步是数据的收集。现在的中大型项目通常采用微服务架构进行分布式部署&#xff0c;所以数据的采集需要在多台服务器上进行&#xff0c;且采集过程不能影响正常业务的…

Domino的线程ID和操作系统的进程ID对应关系

大家好&#xff0c;才是真的好。 很多时候&#xff0c;在Domino中运行的任务出现一些错误提示&#xff0c;如果能够准确定位到和提示信息相关任务时&#xff0c;对我们排错有着巨大的帮助&#xff0c;也能节省很多时间。 例如&#xff0c;我们可能在Domino实时控制台上看到以…

RedHat8配置本地YUM源

目录&#xff1a; RedHat8配置本地YUM源1、创建规则文件2、创建挂载点3、挂载ISO镜像(1).将iso镜像连接到虚拟机再进行挂载a.将ISO镜像连接虚拟机b.挂载镜像到挂载点c.使用df -h查看当前系统设备挂载情况 (2)将iso镜像上传至服务器再进行挂载a.将ISO镜像通过ftp工具上传b.挂载镜…

Spring Boot——优雅的参数校验

&#x1f388; 概述 当我们想提供可靠的 API 接口&#xff0c;对参数的校验&#xff0c;以保证最终数据入库的正确性&#xff0c;是 必不可少 的活。比如下图就是 我们一个项目里 新增一个菜单校验 参数的函数&#xff0c;写了一大堆的 if else 进行校验&#xff0c;或者基础校…

C#简单向:textbox添加提示内容

项目场景&#xff1a; 向C#窗体项目的textbox内添加提示内容&#xff0c;如下图所示效果&#xff1a; 具体实现&#xff1a; 首先&#xff1a; 1.到所要操作的文件(/xx.cs/xx.Designer.cs),这里我是到Form3.cs/Form3.Designer.cs文件 2.找到你所要操作的textBox&#xff0c…

数据结构与算法(一):基础数据结构(算法概念、数组、链表、栈、队列)

算法概念、数组、链表、栈、队列 判断一个数是否是2的N次方&#xff1f; N & (N-1) 0 (N > 0)算题&#xff1a; 力扣 https://leetcode.cn/POJ http://poj.org/ 算法 算法概念 算法代表&#xff1a; 高效率和低存储 内存占用小、CPU占用小、运算速度快 算法的高…

C# HttpClient使用JWT请求token调用接口,解决返回HTML网页的异常信息

一.项目目的&#xff1a; 1.使用JWT获取token&#xff0c;调用外部提供的接口&#xff0c;解决返回HTML错误信息。 错误缘由&#xff0c;接口服务器未能识别token&#xff0c;token信息不准确。 二.项目工具&#xff1a; Visual Studio&#xff08;开发工具&#xff09;&…