• SpringBoot预加载与懒加载


    预加载

    bean在springBoot启动过程中就完成创建加载
    在AbstractApplicationContext的refresh方法中

    
    		// Instantiate all remaining (non-lazy-init) singletons.
    		beanFactory.preInstantiateSingletons();
    
    • 1
    • 2
    • 3
        public void preInstantiateSingletons() throws BeansException {
            if (logger.isTraceEnabled()) {
                logger.trace("Pre-instantiating singletons in " + this);
            }
    
            //所有要进行初始的Bean
            List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
    
            // 对所有Bean进行初始化,除了懒加载
            for (String beanName : beanNames) {
                // 去合并Bean
                RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
                //非抽象、单例、懒加载才会进行注册
                if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                    //判断是不是 FactoryBean,简单说是我们这个 Bean 实现了
                    if (isFactoryBean(beanName)) {
                        // 是不是 FactoryBean,获取 FactoryBean 的方式就是  前缀+beanName
                        Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                        // 判断是否 FactoryBean
                        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 {
                                // 判断 是不是 这个类的,如果是就去创建 SmartFactoryBean 属于 FactoryBean 子接口,拥有更加细粒度操作原数据的方式,
                                isEagerInit = (factory instanceof SmartFactoryBean &&
                                        ((SmartFactoryBean<?>) factory).isEagerInit());
                            }
                            if (isEagerInit) {
                                getBean(beanName);
                            }
                        }
                    } else {
                        //获取具体的Bean
                        getBean(beanName);
                    }
                }
            }
    
            // Trigger post-initialization callback for all applicable beans...
    
            for (String beanName : beanNames) {
                Object singletonInstance = getSingleton(beanName);
                // 执行所有 单例Bean 的回调,当然Bean 需要实现这个接口~~~
                if (singletonInstance instanceof SmartInitializingSingleton) {
                    SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
                    if (System.getSecurityManager() != null) {
                        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                            smartSingleton.afterSingletonsInstantiated();
                            return null;
                        }, getAccessControlContext());
                    } else {
                        smartSingleton.afterSingletonsInstantiated();
                    }
                }
            }
        }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    getMergedLocalBeanDefinition

    这里是去合并Bean去了,这里其实有两个动作

    将取出来的 BeanDefinition 进行合并
    将BeanDefinition 转换成 RottBeanDefinition 也就说顶级的 Bean ,此处的顶级Bean 指的就是User extends SuperUser,可以认为是是 User,也可以认为是 SuperUser,如果是 User 就代表了已经进行了合并,如果 SuperUser 由于其本身就是顶级类,所以不需要合并,这里会排除掉 Object
    需要注意的是第一次 从 mergedBeanDefinitions 是从 当前的 BeanFactory 中查找

    protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
    
        //从当前 BeanFactory 中的缓存中获取
        RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
        // 不是NUll 并且  没有过期的话,如果过期了或者修改了 会重新去查找
        if (mbd != null && !mbd.stale) {
            //返回当前的
            return mbd;
        }
        // 先去 beanDefinitionMap 中去获取
        BeanDefinition thisBeanDefinition=   getBeanDefinition(beanName);
        // 缓存中未找到,就到 BeanFactory 中寻找
        return getMergedBeanDefinition(beanName, thisBeanDefinition);
    }
    protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
        throws BeanDefinitionStoreException {
        //传递Name 和BeanDefinition
        return getMergedBeanDefinition(beanName, bd, null);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 具体的逻辑
        protected RootBeanDefinition getMergedBeanDefinition(
                String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
                throws BeanDefinitionStoreException {
    
            // 将 mergedBeanDefinitions 防止线程安全,为什么?
            // 虽然 ConcurrentHashMap 是线程安全的,但是下面的业务并不一定是线程安全的,所以要加锁
            synchronized (this.mergedBeanDefinitions) {
                RootBeanDefinition mbd = null;
                RootBeanDefinition previous = null;
    
                // 这里为空,代表的是 当前的 BeanDefinition 是顶层的 Bean 不存在 嵌套Bean
                if (containingBd == null) {
                    // 获取当前Bean,为什么又一次获取了呢?因为如果在多线程操作下 可能 这个Bean已经被修改了,所以重新获取一次
                    mbd = this.mergedBeanDefinitions.get(beanName);
                }
                
                //如果缓存中没有,或者过期了,则会重新创建一个
                if (mbd == null || mbd.stale) {
                    previous = mbd;
                    //如果 父 parentName 为空
                    if (bd.getParentName() == null) {
                        // 如果当前类型就是 RootBeanDefinition
                        if (bd instanceof RootBeanDefinition) {
                            // 进行克隆~~~
                            mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
                        } else {
                            // 如果不是就会将 其他 BeanDefinition 转换成 RootBeanDefinition 代表的是当前 BeanDefinition 为顶级 Bean
                            mbd = new RootBeanDefinition(bd);
                        }
                    }
                    //  如果父 BeanDefinition 不为 空,也就代表了 当前类存在继承,如果不理解这段的话,可以看一下 bean标签中的 paretn 属性,
                    else {
                        // 子bean定义:需要与父bean合并。
                        BeanDefinition pbd;
                        try {
                            String parentBeanName = transformedBeanName(bd.getParentName());
                            // 当前的 BeanName 不是 parentBeanName,会去获取 父BeanDefinition,否则则会去父工厂去获取
                            if (!beanName.equals(parentBeanName)) {
                                // 获取 parent BeanDefinition这里会进行一个递归操作,
                                pbd = getMergedBeanDefinition(parentBeanName);
                            } else {
                                // 如果当前的BeanName 和传递的 parentName 一模一样 则会去父 ParentBeanFactory 查找
                                BeanFactory parent = getParentBeanFactory();
                                // 如果当前是层次 BeanFactory 转换查找,如果不是 抛出异常
                                if (parent instanceof ConfigurableBeanFactory) {
                                    pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
                                } else {
                                    throw new NoSuchBeanDefinitionException(parentBeanName,
                                            "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
                                                    "': cannot be resolved without a ConfigurableBeanFactory parent");
                                }
                            }
                        } catch (NoSuchBeanDefinitionException ex) {
                            throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
                                    "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
                        }
                        // 进行合并
                        // 进行合并,这里的合并是指将 父级的Bean 合并到子 中,例如  user extends superUser
                        // 也就说讲 super中的 属性 合并到 user 中
                        // Deep copy with overridden values.
                        mbd = new RootBeanDefinition(pbd);
                        mbd.overrideFrom(bd);
                    }
    
    
                    //设置成单例,如果之前没设置的话~~~~
                    if (!StringUtils.hasLength(mbd.getScope())) {
                        // 默认为单例
                        mbd.setScope(SCOPE_SINGLETON);
                    }
    
                    // A bean contained in a non-singleton bean cannot be a singleton itself.
                    // Let's correct this on the fly here, since this might be the result of
                    // parent-child merging for the outer bean, in which case the original inner bean
                    // definition will not have inherited the merged outer bean's singleton status.
                    if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
                        mbd.setScope(containingBd.getScope());
                    }
    
                    // Cache the merged bean definition for the time being
                    // (it might still get re-merged later on in order to pick up metadata changes)
    
                    // 是否缓存Bean的元数据
                    if (containingBd == null && isCacheBeanMetadata()) {
                        // 将当前的 BeanDefinitions 放入到map中,进行缓存
                        this.mergedBeanDefinitions.put(beanName, mbd);
                    }
                }
                if (previous != null) {
                    copyRelevantMergedBeanDefinitionCaches(previous, mbd);
                }
                return mbd;
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95

    总结这里的合并是指将 父级的Bean 合并到子 中,例如 user extends superUser
    也就说讲 super中的 属性 合并到 user 中,父类的BeanDefinition会被子类的BeanDefinition继承。

    循环创建bean

    	if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
    
    • 1

    对于非抽象,单例,非懒加载的bean分别调用getBean方法
    getBean方法比较复杂,简单总结下就创建bean的对象并且创建bean依赖的对象并且注入到当前bean完成对bean的初始化

    懒加载

    @Lazy

    在类上加上@Lazy标签,那么就开启了懒加载

    @Service("helloServiceB")
    @Lazy
    public class HelloServiceB {
    ············
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    懒加载指的是,初始化时不会创建实例,在真正被使用到的时候再进行加载。
    看了前面的预加载可以知道,在preInstantiateSingletons方法中会跳过懒加载的bean。

    如果懒加载的bean被依赖会怎么样?
    比如又有serviceA依赖了ServiceB

    @Service("helloServiceA")
    public class HelloServiceA implements HelloService {
    
        @Autowired
        private HelloService helloServiceB;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    那么此时HelloServiceB懒加载会失效
    HelloServiceA没有@Lazy标签会在启动时预加载通过getBean方法创建。同时会注入其依赖的bean。serviceB也会被创建。
    因此要使懒加载生效,应该在HelloServiceA也加@Lazy注解

    全局懒加载

    一般情况程序在启动时时有大量的 Bean 需要初始化,例如 数据源初始化、缓存初始化等导致应用程序启动非常的慢。在 spring boot 2.2 之前的版本,我们对这些 bean 使用手动增加 @Lazy 注解,来实现启动时不初始化,业务程序在调用需要时再去初始化,如上代码修改为即可:

    为什么需要全局懒加载

    同上文中提到我们需要手动在 bean 增加 @Lazy 注解,这就意味着我们仅能对程序中自行实现的 bean 进行添加。但是现在 spring boot 应用中引入了很多第三方 starter ,比如 druid-spring-boot-starter 数据源注入、spring-boot-starter-data-redis 缓存等默认情况下, 引入即注入了相关 bean 我们无法去修改添加 @Lazy

    • spring boot 2.2 新增全局懒加载属性,开启后全局 bean 被设置为懒加载,需要时再去创建
    spring:
      main:
        lazy-initialization: true
    
    • 1
    • 2
    • 3

    原理
    在SpringApplication#prepareContext方法中

    if (this.lazyInitialization) {
    			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    	}
    
    • 1
    • 2
    • 3

    如果开启了lazy-initialization,那么添加LazyInitializationBeanFactoryPostProcessor
    LazyInitializationBeanFactoryPostProcessor执行,会将beanFactory中的bean设置lazyinit

    public final class LazyInitializationBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered {
    
    	@Override
    	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    		// Take care not to force the eager init of factory beans when getting filters
    		Collection<LazyInitializationExcludeFilter> filters = beanFactory
    				.getBeansOfType(LazyInitializationExcludeFilter.class, false, false).values();
    		for (String beanName : beanFactory.getBeanDefinitionNames()) {
    			BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
    			if (beanDefinition instanceof AbstractBeanDefinition) {
    				postProcess(beanFactory, filters, beanName, (AbstractBeanDefinition) beanDefinition);
    			}
    		}
    	}
    
    	private void postProcess(ConfigurableListableBeanFactory beanFactory,
    			Collection<LazyInitializationExcludeFilter> filters, String beanName,
    			AbstractBeanDefinition beanDefinition) {
    		Boolean lazyInit = beanDefinition.getLazyInit();
    		if (lazyInit != null) {
    			return;
    		}
    		Class<?> beanType = getBeanType(beanFactory, beanName);
    		if (!isExcluded(filters, beanName, beanDefinition, beanType)) {
    			beanDefinition.setLazyInit(true);
    		}
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    对于全局懒加载

    • 个别 bean 可以通过设置 @Lazy(false) 排除,设置为启动时加载
    @Lazy(false)
    @Configuration
    public class DemoConfig {}
    
    • 1
    • 2
    • 3
    • 当然也可以指定规则实现 LazyInitializationExcludeFilter 规则实现排除
    @Bean LazyInitializationExcludeFilter integrationLazyInitExcludeFilter() { return LazyInitializationExcludeFilter.forBeanTypes(DemoConfig.class); }
    
    • 1

    全局懒加载的好处与问题

    当项目比较大时。开发人员本地调试时,并不需要使用到全部的bean,那么开启全局懒加载可以节省很多启动项目的时间
    通过设置全局懒加载,我们可以减少启动时的创建任务从而大幅度的缩减应用的启动时间。但全局懒加载的缺点可以归纳为以下两点:

    • Http 请求处理时间变长。 这里准确的来说是第一次 http 请求处理的时间变长,之后的请求不受影响
    • 错误不会在应用启动时抛出,不利于早发现、早解决、早下班。
  • 相关阅读:
    Editplus同步阅览两个文件
    Ubuntu18.04安装nvidia显卡驱动
    React 钩子汇总
    【PPT技巧】如何将多张幻灯片打印在一张纸上?
    【无标题】
    牛客刷题——前端面试【三】谈一谈Promise、封装ajax、json数据使用
    【安卓】UI开发入门
    Jenkins详解
    《实验细节》上手使用PEFT库方法和常见出错问题
    shell脚本学习
  • 原文地址:https://blog.csdn.net/qq_37436172/article/details/127874865