Spring源码之@Lazy和预实例化
懒加载优缺点
优点:懒加载,对象使用的时候才去创建;启动速度快,节省资源
缺点:不利于提前发现错误;初次请求getBean时慢
三种情况
- 只有一个@Lazy注解的类
- 一个Singleton类,依赖@Lazy的类
- 两个@Lazy的类互相依赖
只有一个@Lazy注解的类分析
@Lazy注解的类在容器初始化时,不执行getBean
singleton 的bean初始化是通过调用AbstractApplicationContext的finishBeanFactoryInitialization方法完成。
当用@Lazy注解时,执行到DefaultListableBeanFactory的preInstantiateSingletons方法时,不满足条件,故在容器初始化时,不会进行预实例化。不会调用后面getBean方法。
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
......
}
调用链:
SpringApplication#run() --> SpringApplication#refreshContext() --> SpringApplication#refresh() --> ServletWebServerApplicationContext#refresh() --> AbstractApplicationContext#refresh() --> AbstractApplicationContext#finishBeanFactoryInitialization() --> DefaultListableBeanFactory#preInstantiateSingletons()
preInstantiateSingletons源码:
@Override
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("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.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
//!bd.isLazyInit()为false
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
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 {
getBean(beanName);
}
}
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
.tag("beanName", beanName);
SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
smartInitialize.end();
}
}
}
第一次对@Lazy修饰的类调用getBean方法
一种写法:
public String getLazyBean() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(LazyConfig.class);
Object lazyConfig = context.getBean("lazyConfig");
return lazyConfig.toString();
}
@Component
@Lazy
public class LazyConfig {
......
}
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(LazyConfig.class);会调用AnnotationConfigApplicationContext初始化方法,进行refresh()
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
而@Lazy注解的类真正初始化则在context.getBean("lazyConfig");过程,调用到AbstractApplicationContext类getBean方法
@Override
public Object getBean(String name) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name);
}
然后调用到AbstractBeanFactory#doGetBean,后面和预实例化中过程一样,最后调用到AbstractAutowireCapableBeanFactory#doCreateBean();
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
// 实例化对象
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
......
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//属性注入
populateBean(beanName, mbd, instanceWrapper);
//初始化对象
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
......
return exposedObject;
}
调用链:
AbstractApplicationContext#getBean() --> AbstractBeanFactory#getBean() --> AbstractBeanFactory#doGetBean() --> AbstractAutowireCapableBeanFactory#createBean()
--> AbstractAutowireCapableBeanFactory#doCreateBean() --> AbstractAutowireCapableBeanFactory#createBeanInstance()、populateBean()、initializeBean()
一个Singleton类,依赖@Lazy的类
一个例子
@Service
public class LazyServiceImpl implements LazyService {
@Autowired
private LazyConfig lazyConfig;
@Override
public void lazyDependent() {
......
}
}
@Component
@Lazy
public class LazyConfig {
......
}
分析
在容器初始化时,preInstantiateSingletons会对上面的LazyServiceImpl进行getBean的处理,执行到AbstractAutowireCapableBeanFactory类populateBean方法进行属性注入时,通过如下调用链对上面的LazyConfig类进行getBean处理
AbstractAutowireCapableBeanFactory#populateBean() --> AutowiredAnnotationBeanPostProcessor#postProcessProperties() --> InjectionMetadata#inject() --> AutowiredAnnotationBeanPostProcessor#inject() --> DefaultListableBeanFactory#resolveDependency() --> DefaultListableBeanFactory#doResolveDependency() --> DependencyDescriptor#resolveCandidate() --> AbstractBeanFactory#getBean() --> AbstractBeanFactory#doGetBean()
从而一个Singleton类,依赖@Lazy的类,这个被依赖的@Lazy注释的类,也会被初始化
两个@Lazy的类互相依赖
容器初始化时都不调用getBean进行初始化,在其中一个getBean时,后面和singleton的循环依赖一样处理,详见前文。
Spring源码之@Lazy和预实例化的更多相关文章
- 【Spring源码分析】原型Bean实例化过程、byName与byType及FactoryBean获取Bean源码实现
原型Bean加载过程 之前的文章,分析了非懒加载的单例Bean整个加载过程,除了非懒加载的单例Bean之外,Spring中还有一种Bean就是原型(Prototype)的Bean,看一下定义方式: & ...
- Spring 源码学习 - 单例bean的实例化过程
本文作者:geek,一个聪明好学的同事 1. 简介 开发中我们常用@Commpont,@Service,@Resource等注解或者配置xml去声明一个类,使其成为spring容器中的bean,以下我 ...
- Bean实例化(Spring源码阅读)-我们到底能走多远系列(33)
我们到底能走多远系列(33) 扯淡: 各位: 命运就算颠沛流离 命运就算曲折离奇 命运就算恐吓着你做人没趣味 别流泪 心酸 更不应舍弃 ... 主题: Spring源码阅读还在继 ...
- Spring 源码分析之 bean 实例化原理
本次主要想写spring bean的实例化相关的内容.创建spring bean 实例是spring bean 生命周期的第一阶段.bean 的生命周期主要有如下几个步骤: 创建bean的实例 给实例 ...
- 【转载】Spring 源码分析之 bean 实例化原理
本次主要想写spring bean的实例化相关的内容.创建spring bean 实例是spring bean 生命周期的第一阶段.bean 的生命周期主要有如下几个步骤: 创建bean的实例 给实例 ...
- Spring源码-IOC部分-Bean实例化过程【5】
实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...
- Spring 源码学习——Aop
Spring 源码学习--Aop 什么是 AOP 以下是百度百科的解释:AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程通过预编译的方式和运行期动态代理实 ...
- Spring源码分析之`BeanFactoryPostProcessor`调用过程
前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 本文内容: AbstractApplicationContext#refresh前部分的一点小内容 ...
- Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点
Spring源码学习笔记12--总结篇,IOC,Bean的生命周期,三大扩展点 参考了Spring 官网文档 https://docs.spring.io/spring-framework/docs/ ...
随机推荐
- MeteoInfo家族的新产品:MeteoInfoLab
为了更方便地处理各种数据并绘图,尝试开发了一个新的软件产品MeteoInfoLab,软件设计上参考了MatLab和Spider.软件以脚本程序和命令行交互为主,基于MeteoInfo库并利用Jytho ...
- PHP之Trait详解 转
php从以前到现在一直都是单继承的语言,无法同时从两个基类中继承属性和方法,为了解决这个问题,php出了Trait这个特性 用法:通过在类中使用use 关键字,声明要组合的Trait名称,具体的Tra ...
- kubernetes:用kubeadm管理token(kubernetes 1.18.3)
一,token的用途: 1,token是node节点用来连接master节点的令牌字串, 它和ca证书的hash值是把一台node节点加入到kubernetes集群时要使用的凭证 2, 通过kubea ...
- centos8平台用ffprobe获取视频文件信息(ffmpeg4.2.2)
一,ffprobe的作用 ffprobe是强大的视频分析工具, 用于从多媒体流中获取相关信息或查看文件格式信息, 并以可读的方式打印 说明:刘宏缔的架构森林是一个专注架构的博客,地址:https:// ...
- utf-8和utf-8-sig的区别
前言:在写入csv文件中,出现了乱码的问题. 解决:utf-8 改为utf-8-sig 区别如下: 1."utf-8" 是以字节为编码单元,它的字节顺序在所有系统中都是一样的,没有 ...
- MySQL 日志之 binlog 格式 → 关于 MySQL 默认隔离级别的探讨
开心一刻 产品还没测试直接投入生产时,这尼玛... 背景问题 在讲 binlog 之前,我们先来回顾下主流关系型数据库的默认隔离级别,是默认隔离级别,不是事务有哪几种隔离级别,别会错题意了 1.Ora ...
- Qlik Sense学习笔记之插件开发
date: 2019-05-06 13:18:45 updated: 2019-08-09 15:18:45 Qlik Sense学习笔记之插件开发 1.开发前的基础工作 1.1 新建插件 dev-h ...
- Linux部署常用命令
1../startup.sh 开启tomcat服务 2.rm -f file1 删除文件 3.rmdir dir xxx 删除目录 4.Tab按钮 自动补全文件名称功能 5.gzip file1 ...
- ABP 数据访问 - IRepository 仓储
ABP系列,这个系列来的比较晚,很多大佬其实已经分析过,为什么现在我又来一轮呢? 1.想自己来完整的学习一轮ABP 2.公司目前正在使用ABP,准备迁移Core 基于以上的目的,开始这个系列 ABP ...
- Luogu P4172 [WC2006]水管局长
题意 给定一个 \(n\) 个点 \(m\) 条边的图和 \(q\) 次操作,每次操作分为以下两种: 1 u v:查询 \(u\) 到 \(v\) 的一条路径使得边权最大的边的权值最小. 2 u v: ...