SpringBoot预加载与懒加载实现方法超详细讲解

目录
  • 预加载
  • getMergedLocalBeanDefinition
  • 循环创建bean
  • 懒加载
    • @Lazy
    • 全局懒加载
    • 为什么需要全局懒加载
  • 全局懒加载的好处与问题

预加载

bean在springBoot启动过程中就完成创建加载

在AbstractApplicationContext的refresh方法中

		// Instantiate all remaining (non-lazy-init) singletons.
		beanFactory.preInstantiateSingletons();
    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();
                }
            }
        }
    }

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);
}

具体的逻辑

    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 parent=""> 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;
        }
    }

总结这里的合并是指将 父级的Bean 合并到子 中,例如 user extends superUser

也就说讲 super中的 属性 合并到 user 中,父类的BeanDefinition会被子类的BeanDefinition继承。

循环创建bean

if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->

对于非抽象,单例,非懒加载的bean分别调用getBean方法

getBean方法比较复杂,简单总结下就创建bean的对象并且创建bean依赖的对象并且注入到当前bean完成对bean的初始化

懒加载

@Lazy

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

@Service("helloServiceB")
@Lazy
public class HelloServiceB {
············

懒加载指的是,初始化时不会创建实例,在真正被使用到的时候再进行加载。

看了前面的预加载可以知道,在preInstantiateSingletons方法中会跳过懒加载的bean。

如果懒加载的bean被依赖会怎么样?

比如又有serviceA依赖了ServiceB

@Service("helloServiceA")
public class HelloServiceA implements HelloService {
    @Autowired
    private HelloService helloServiceB;

那么此时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

原理

在SpringApplication#prepareContext方法中

if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}

如果开启了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);
		}
	}

对于全局懒加载

个别 bean 可以通过设置 @Lazy(false) 排除,设置为启动时加载

@Lazy(false)
@Configuration
public class DemoConfig {}

当然也可以指定规则实现 LazyInitializationExcludeFilter 规则实现排除

@Bean LazyInitializationExcludeFilter integrationLazyInitExcludeFilter() {<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E--> return LazyInitializationExcludeFilter.forBeanTypes(DemoConfig.class); }

全局懒加载的好处与问题

当项目比较大时。开发人员本地调试时,并不需要使用到全部的bean,那么开启全局懒加载可以节省很多启动项目的时间

通过设置全局懒加载,我们可以减少启动时的创建任务从而大幅度的缩减应用的启动时间。但全局懒加载的缺点可以归纳为以下两点:

  • Http 请求处理时间变长。 这里准确的来说是第一次 http 请求处理的时间变长,之后的请求不受影响
  • 错误不会在应用启动时抛出,不利于早发现、早解决、早下班。

到此这篇关于SpringBoot预加载与懒加载实现方法超详细讲解的文章就介绍到这了,更多相关SpringBoot预加载与懒加载内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 解析javascript图片懒加载与预加载的分析总结

    本篇文章主要介绍了懒加载和预加载两种技术的解析,废话不多说,一起来看吧. 懒加载也叫延迟加载:前一篇文章有介绍:JS图片延迟加载 延迟加载图片或符合某些条件时才加载某些图片. 预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染. 两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载.懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力. 懒加载的意义及实现方式有: 意义: 懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数. 实现方式:

  • JavaScript懒加载与预加载原理与实现详解

    目录 1.懒加载 1.1.什么是懒加载 1.2.为什么要使用懒加载 1.3.懒加载的优点 1.4.懒加载的原理 1.5.懒加载的实现步骤 1.6.懒加载的实现方式 2.预加载 2.1.什么是预加载 2.2.为什么要使用预加载 2.3.实现预加载的方法 1.懒加载 1.1.什么是懒加载 懒加载也可以叫做延迟加载,当访问一个页面的时候,先把img元素伙食其他元素的背景图片路径替换成一张大小为1*1px图片的路径(这样只需要请求一次,俗称占位图): 只有当图片出现在浏览器的可视区域内时,才这只图片真正

  • vue前端性能优化之预加载和懒加载示例详解

    目录 预加载 图片预加载 JS预加载 js的加载方式 preload prefetch Preload & Prefetch 的区别 不同资源加载的优先级规则 懒加载 图片懒加载 路由懒加载 组件懒加载 最后 预加载 预加载简单来说就是将所有所需的资源提前请求加载到本地,这样后面在需要用到时就直接从缓存取资源:我们使用该技术预先告知浏览器,等下某些资源可能要被使用,先把资源下载下来,不要等使用的时候再下载,可以看出这样的加载技术会增加服务器的压力,但是用户的体验会比较好,因为可以较快的看到后面的

  • javascript实现图片预加载和懒加载

    本文实例为大家分享了javascript实现图片预加载和懒加载的具体代码,供大家参考,具体内容如下 预加载 预加载是预先加载好后面需要用到的资源, 后面使用的时候直接去缓存里取.举个栗子, 比如一个网站的开场动画, 这些动画是由很多图片组成的, 假如不预先加载好, 那就会造成动画不流畅产生闪动白屏.图片是提高用户体验的一个很好方法.图片预先加载到浏览器中,保证了图片快速.无缝地发布,使用户在浏览你网站内容时获得更好的用户体验. //这里我把图片数量写死了,而且对图片名也有要求必须是阿拉伯数字后缀

  • webpack的懒加载和预加载详解

    目录 正常加载 懒加载 预加载 没有使用预加载 使用预加载 总结 正常加载 为了看的方便,index.js中的代码非常简单 console.log('index.js执行了') import { test } from './test.js' document.getElementById('btn-wrap').onclick = function () { test() } test.js console.log('test.js执行了') export function test() {

  • SpringBoot预加载与懒加载实现方法超详细讲解

    目录 预加载 getMergedLocalBeanDefinition 循环创建bean 懒加载 @Lazy 全局懒加载 为什么需要全局懒加载 全局懒加载的好处与问题 预加载 bean在springBoot启动过程中就完成创建加载 在AbstractApplicationContext的refresh方法中 // Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons()

  • SpringBoot新特性之全局懒加载机制

    关于延迟加载 在 Spring 中,默认情况下所有定的 bean 及其依赖项目都是在应用启动时创建容器上下文是被初始化的.测试代码如下: @Slf4j @Configuration public class DemoConfig { public DemoConfig() { log.warn(" > > > demoConfig 被初始化 > > >"); } } 启动应用日志: [ main] o.a.c.c.C.[Tomcat].[localh

  • 在springboot中实现个别bean懒加载的操作

    懒加载---就是我们在spring容器启动的是先不把所有的bean都加载到spring的容器中去,而是在当需要用的时候,才把这个对象实例化到容器中. @Lazy 在需要懒加载的bean上加上@Lazy就可以了 补充知识:springboot组件懒加载的坑及加载规则 什么是懒加载? 懒加载的意思是不在项目启动的时候实例出来这个组件 @RestController public class ApiController { @Autowired Skill kobSkillImpl; @Request

  • Android在多种设计下实现懒加载机制的方法

    前言 前段时间在自己的练习项目中想用到懒加载机制,查看了大多数资料只介绍了在 View Pager + Fragment 组合的情况下实现的懒加载,但是现在大多数App更多的是 Fragmentmanager 去管理主页面多个 Fragment 的显示与隐藏,然后主界面的某个或多个 Fragment 里又嵌套了多个 Fragment + ViewPager (详细见下图 ),对于这种情况,适用于第一种的方式是不能直接解决第二种的情况的,所以写下这篇文章,记录一下踩的几个坑,希望对同像我一样的初学

  • JS图片懒加载的优点及实现原理

    这篇文章主要介绍了JS图片懒加载的优点及实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 图片懒加载优势: 增强用户体验 优化代码 减少http的请求 减少服务器端压力 服务器的按需加载 图片懒加载原理: 先将图片的src设置为同一张图片或者不设置,同时给img标签设置一个特殊属性,例如:data-src用于存放图片的真实预览地址:若图片未进入可视区域时,展示同一张图片或者直接不展示图片,此时就不会发生http请求,当图片进入可视区域时,

  • Vue通过懒加载提升页面响应速度

    概述 项目的目的是要通过数据透视表和Excel公式来分析公司的各项运营数据.不过在集成后,在开发环境页面运行流畅,大量数据加载处理也很快.但是发布生产后,在用户每次打开页面时,加载时间上相较开发阶段均有所降低,经过排查速度变慢是由于发布包的vendor.js变大所导致的,这个文件加载每次都需300毫秒左右,由于小的Vue项目并没有做模块划分,所以所有的代码都直接打包到了vendor中,在集成了新功能后,发布包也随之变大了. 既然找到了原因,就开始着手优化,在前端对于需加载较大资源时,一般都采用懒

  • 解析iOS应用的UI开发中懒加载和xib的简单使用方法

    懒加载 1.懒加载基本 懒加载--也称为延迟加载,即在需要的时候才加载(效率低,占用内存小).所谓懒加载,写的是其get方法. 注意:如果是懒加载的话则一定要注意先判断是否已经有了,如果没有那么再去进行实例化 2.使用懒加载的好处: (1)不必将创建对象的代码全部写在viewDidLoad方法中,代码的可读性更强 (2)每个控件的getter方法中分别负责各自的实例化处理,代码彼此之间的独立性强,松耦合 3.代码示例 复制代码 代码如下: // //  YYViewController.m //

随机推荐