• Spring5.0源码学习系列之浅谈懒加载机制原理


    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 已经初始化完成并缓存了起来,但是还没被实例,第一次调用时候就会被实例
  • 相关阅读:
    Spring AOP总结(三)
    Spring AOP源码解析(二)
    java9新特性
    BeanFactory和ApplicationContext的区别总结
    Elasticsearch7.X为什么移除类型(type)
    elasticsearch性能优化(二)
    elasticsearch性能优化(一)
    elasticsearch的master选举机制
    自动化构建和部署应用系统平台
    关系型数据库之mysql-01
  • 原文地址:https://www.cnblogs.com/xinxihua/p/13982732.html
Copyright © 2020-2023  润新知