Spring5.0源码学习系列之浅谈懒加载机制原理
前言介绍
附录:Spring源码学习专栏
在上一章的学习中,我们对Bean的创建有了一个粗略的了解,接着本文挑一个比较重要的知识点Bean的懒加载进行学习
1、什么是懒加载?
懒加载(Lazy-initialized beans):懒加载模式是bean在第一次调用时候被实例,而不是spring容器启动时候,默认是不开启的,( A lazy-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at startup.),通过改配置lazy-init="true"
2、实验环境准备
实验环境:
- SpringFramework版本
- Springframework5.0.x
- 开发环境
- JAR管理:gradle 4.9/ Maven3.+
- 开发IDE:IntelliJ IDEA 2018.2.5
- JDK:jdk1.8.0_31
- Git Server:Git fro window 2.8.3
- Git Client:SmartGit18.1.5(可选)
3、lazy-init使用
import org.springframework.beans.factory.InitializingBean;
/**
* <pre>
* SpringBean
* </pre>
*
* <pre>
* @author mazq
* 修改记录
* 修改后版本: 修改人: 修改日期: 2020/11/05 10:50 修改内容:
* </pre>
*/
public class SpringBean implements InitializingBean {
public SpringBean(){
System.out.println("SpringBean构造函数");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("SpringBean afterPropertiesSet");
}
}
xml配置方式,在applicationContext.xml加载配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<bean id="springBean" lazy-init="true" class="com.example.bean.SpringBean" ></bean>
</beans>
注解方式,使用@Lazy即可
import com.example.bean.SpringBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.example.bean.A;
import org.springframework.context.annotation.Lazy;
/**
* <pre>
* AppConfiguration
* </pre>
*
* <pre>
* @author mazq
* 修改记录
* 修改后版本: 修改人: 修改日期: 2020/11/05 10:26 修改内容:
* </pre>
*/
@Configuration
public class AppConfiguration {
@Bean
@Lazy // 开启懒加载
// @Lazy(value = false) 默认
public SpringBean springBean() {
return new SpringBean();
}
}
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// lazy-init开启的bean,context.getBean调用时候才会被实例
SpringBean springBean = context.getBean(SpringBean.class);
System.out.println(springBean);
4、lazy加载机制原理
为什么设置为lazy-init之后,Spring IoC容器启动时候bean不会被实例?可以基于上一章内容找到答案
{@link org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization}
/**
* Finish the initialization of this context's bean factory,
* initializing all remaining singleton beans.
*/
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
// 初始化ConversionService,这个bean用于将前端传过来的参数和后端的 controller 方法上的参数进行绑定
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
// 先初始化LoadTimeWeaverAware 类型的Bean
// AspectJ 的内容,IoC的源码学习,先跳过
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
// 冻结配置,不让bean 定义解析、加载、注册
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
// 实例所有非懒加载的单例Bean
beanFactory.preInstantiateSingletons();
}
找到关键代码beanFactory.preInstantiateSingletons();
@Override
public void preInstantiateSingletons() throws BeansException {
if (logger.isDebugEnabled()) {
logger.debug("Pre-instantiating singletons in " + this);
}
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
// 获取beanName列表,this.beanDefinitionNames 保存了所有的 beanNames
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
// 触发所有非懒加载的单例bean初始化操作(lazy-init=false)
for (String beanName : beanNames) {
// 合并rootBean中的配置, <bean id="a" class="a" parent="p" />
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 非抽象(abstract = false)、非懒加载(lazy-init=false)的单例Bean(scope=singleton)
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 处理FactoryBean,注意对比BeanFactory和FactoryBean
if (isFactoryBean(beanName)) {
// factoryBean调用在beanName加载前缀符号‘&’
// 为什么要加‘&’,应该是做下标记,不过在bean创建过程要进行转换,详情请看下文
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
// FactoryBean是SmartFactoryBean 的基类
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(
(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
// 普通的Bean,调这个方法进行实例,往下跟
getBean(beanName);
}
}
}
// Trigger post-initialization callback for all applicable beans...
// SmartInitializingSingleton 的基类在这里回调
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
代码里有isLazyInit()的校验,所以设置lazy-init=true的bean都不会随着ioc容器启动而被实例加载
学习归纳
综上所述:
- 对于被修饰为lazy-init=false(默认情况)的bean Spring 容器初始化阶段会被依赖注入,并且实例到单例池里
- 对于懒加载的bean,context.getBean的时候会从缓存里获取,因为容器初始化阶段 Bean 已经初始化完成并缓存了起来,但是还没被实例,第一次调用时候就会被实例
Spring5.0源码学习系列之浅谈懒加载机制原理的更多相关文章
- Spring5.0源码学习系列之浅谈BeanFactory创建
Spring5.0源码学习系列之浅谈BeanFactory创建过程 系列文章目录 提示:Spring源码学习专栏链接 @ 目录 系列文章目录 博客前言介绍 一.获取BeanFactory主流程 二.r ...
- Spring5.0源码学习系列之浅谈循环依赖问题
前言介绍 附录:Spring源码学习专栏 在上一章的学习中,我们对Bean的创建有了一个粗略的了解,接着本文浅谈Spring循环依赖问题,这是一个面试比较常见的问题 1.什么是循环依赖? 所谓的循环依 ...
- Spring5.0源码学习系列之事务管理概述
Spring5.0源码学习系列之事务管理概述(十一),在学习事务管理的源码之前,需要对事务的基本理论比较熟悉,所以本章节会对事务管理的基本理论进行描述 1.什么是事务? 事务就是一组原子性的SQL操作 ...
- Spring5.0源码学习系列之Spring AOP简述
前言介绍 附录:Spring源码学习专栏 在前面章节的学习中,我们对Spring框架的IOC实现源码有了一定的了解,接着本文继续学习Springframework一个核心的技术点AOP技术. 在学习S ...
- WorldWind源码剖析系列:星球球体的加载与渲染
WorldWind源码剖析系列:星球球体的加载与渲染 WorldWind中主函数Main()的分析 在文件WorldWind.cs中主函数Main()阐明了WorldWind的初始化运行机制(如图1所 ...
- Spring 源码学习(4)—— bean的加载part 1
前面随笔中,结束了对配置文件的解析工作,以及将配置文件转换成对应的BeanDefinition存储在容器中.接下来就该进行bean的加载了. public Object getBean(String ...
- 【requireJS源码学习03】细究requireJS的加载流程
前言 这个星期折腾了一周,中间没有什么时间学习,周末又干了些其它事情,这个时候正好有时间,我们一起来继续学习requireJS吧 还是那句话,小钗觉得requireJS本身还是有点难度的,估计完全吸收 ...
- Spring源码学习(5)—— bean的加载 part 2
之前归纳了从spring容器的缓存中直接获取bean的情况,接下来就需要从头开始bean的加载过程了.这里着重看单例的bean的加载 if(ex1.isSingleton()) { sharedIns ...
- CI框架源码阅读笔记9 CI的自动加载机制autoload
本篇并不是对某一组件的详细源码分析,而只是简单的跟踪了下CI的autoload的基本流程.因此,可以看做是Loader组件的分析前篇. CI框架中,允许你配置autoload数组,这样,在你的应用程序 ...
随机推荐
- 《流畅的Python》 第一部分 序章 【数据模型】
流畅的Python 致Marta,用我全心全意的爱 第一部分 序幕 第一章 Python数据模型 特殊方法 定义: Python解释器碰到特殊句法时,使用特殊方法激活对象的基本操作,例如python语 ...
- 【开源】Springboot API 一键生成器
Springboot API 一键生成器 写这个项目,最大的想法就是:不做CRUD 程序猿 Springboot 在我们平时开发项目当中,是如此的常用.然而,比如平时我们写的一些: XX 管理系统 X ...
- ansible-playbook-jinja2管理nginx配置文件
1. 案例1:创建jinja2的nginx的主配置文件 1) 编写jinja2的nginx的主配置文件 1 [root@test-1 jinja2]# vim /ansible/jinja2/tes ...
- 多测师讲解 _接口自动化框架设计_高级讲师肖sir
背景:因为把传入接口参数.组建测试用例.执行测试用例和发送报告,都放入一个.py文件对于接口的使用非常不灵活就需要数据和接口业务进行分离让代码之间的 耦合性降低.和实现接口的分层管理,所以需要对代码进 ...
- windows.h头文件中改变光标位置的函数——SetConsoleCursorPosition
COORD 具体为 typedef struct COORD{ short X; short Y; } COORD,*PCOORD; 可以用来记录坐标. #include <iostre ...
- 在VC6.0下运行C语言程序,以及编程入门必备的常识类小知识!
今天给大家分享在VC6.0环境下编写C语言程序的基本步骤,为初学者打开学习C语言的第一道门.具体步骤如下(如果需要软件资源,可以留言): 1)新建工作区 依次点击 文件--新建--工作区 或是Ctrl ...
- 调试与优化:一次数据中心看板 T+1 改 T+0 优化过程
背景 团队目前在做一个用户数据看板(下面简称看板),基本覆盖用户的所有行为数据,并生成分析数据,用户行为数据来源于多个数据源(餐饮.生活日用.充值消费.交通出行.通讯物流.交通出行.医疗保健.住房物业 ...
- 利用Image对象,建立Javascript前台错误日志记录
手记:摘自Javascript高级程序设计(第三版),利用Image对象发送请求,确实有很多优点,有时候这也许就是一个创意点,再次做个笔记供自己和大家参考. 原文: 开发 Web 应用程序过程中的一种 ...
- centos8平台yum无法安装一些常用软件的解决,如:screen,iftop,nethogs
一,例如:安装screen时报错: [root@localhost liuhongdi]# yum install screen 上次元数据过期检查:17:39:58 前,执行于 2020年03月18 ...
- php+nginx改为socket
使用socket方式连接Nginx优化php-fpm性能 Nginx连接fastcgi的方式有2种:TCP和unix domain socket 什么是Unix domain socket?-- 维基 ...