show: stepversion: 1.0
enable_checker: true
浅谈SpringFramework懒加载机制原理
前言介绍
在前面的学习中,我们对Bean的创建有了一个粗略的了解,接着本文挑一个比较重要的知识点Bean的懒加载进行学习
什么是懒加载?
懒加载(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”
实验环境准备
实验环境:
- 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(可选)
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构造函数");}@Overridepublic 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>*/@Configurationpublic 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);
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.// 实例所有非懒加载的单例BeanbeanFactory.preInstantiateSingletons();}
找到关键代码beanFactory.preInstantiateSingletons();
@Overridepublic 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 保存了所有的 beanNamesList<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和FactoryBeanif (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 已经初始化完成并缓存了起来,但是还没被实例,第一次调用时候就会被实例
