レイジーローディングメカニズムの原理に関するSpring5.0ソースコード学習シリーズ(7)

前書き

付録:Springソースコード学習コラム

研究前章、我々はビーンの作成の大まかな理解しているし、この記事では、学習のためのより重要な知識ポイントビーン遅延ロードを選びます

1.レイジーローディングとは何ですか?

レイジー初期化Bean:レイジーロードモードでは、スプリングコンテナが起動したときではなく、最初に呼び出されたときにBeanがインスタンス化されます。これは、デフォルトではオンになっていません(レイジー初期化されたBeanは、IoCコンテナにBeanを作成するように指示します)。起動時ではなく、最初に要求されたときのインスタンス。)構成を変更することにより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サーバー:Git fro window 2.8.3
    • Gitクライアント: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.レイジーローディングメカニズムの原理

Spring IoCコンテナをlazy-initに設定した後、起動したときに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()チェックがあり、iocコンテナがロードされた状態でBeanが開始されない場合はlazy-init = true例

誘導の学習

要約すれば:

  • lazy-init = false(デフォルト)として変更されたBeanの場合、Springコンテナーの初期化フェーズに依存関係が挿入され、インスタンスがシングルトンプールに配置されます。
    レイジーロードされたBeanの場合、コンテナーがキャッシュから取得されるため、context.getBeanがキャッシュから取得されます。初期化フェーズでは、Beanは初期化されてキャッシュされていますが、インスタンス化されていません。最初に呼び出されたときにインスタンス化されます。

おすすめ

転載: blog.csdn.net/u014427391/article/details/109645944