lazy init控制加载在Spring中如何实现源码分析

目录
  • 一、lazy-init说明
  • 二、lazy-init 属性被设置的地方
  • 三、lazy-init发挥作用的地方
  • 四、问答

一、lazy-init说明

ApplicationContext实现的默认行为就是在启动时将所有singleton bean提前进行实例化(也就是依赖注入)。

提前实例化意味着作为初始化过程的一部分,ApplicationContext实例会创建并配置所有的singleton bean。

通常情况下这是件好事,因为这样在配置中的任何错误就会即刻被发现(否则的话可能要花几个小时甚至几天)。

<bean id="testBean" class="com.fhx.TestBean">

该bean默认的设置为:

<bean id="testBean" class="com.fhx.TestBean" lazy-init="false">

lazy-init="false" 立退加载, 表示spring启动时,立刻进行实例化。

(lazy-init 设置只对scop属性为singleton的bean起作用)

有时候这种默认处理可能并不是你想要的。如果你不想让一个singleton bean在ApplicationContext实现在初始化时被提前实例化,那么可以将bean设置为延迟实例化。

, lazy-init="true"> 延迟加载 ,设置为lazy的bean将不会在ApplicationContext启动时提前被实例化,而是在第一次向容器通过getBean索取bean时实例化的。

如果一个设置了立即加载的bean1,引用了一个延迟加载的bean2,那么bean1在容器启动时被实例化,而bean2由于被bean1引用,所以也被实例化,这种情况也符合延迟加载的bean在第一次调用时才被实例化的规则。 在容器层次中通过在元素上使用'default-lazy-init'属性来控制延迟初始化也是可能的。如下面的配置:

<beans default-lazy-init="true"><!-- no beans will be eagerly pre-instantiated... --></beans>

一般beans 和 bean 层次配置的默认值都是false;并且bean的优先级>beans的优先级 如果一个bean的scope属性为scope=“pototype“时,即使设置了lazy-init="false",容器启动时不实例化bean,而是调用getBean方法是实例化的;

现在我们通过源码来分析一下;

二、lazy-init 属性被设置的地方

并且优先级 bean>beans; 如果想看所有属性被设置的地方请看博文 Spring是如何解析xml中的属性到BeanDefinition中的

//解析bean的属性值
 public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, BeanDefinition containingBean, AbstractBeanDefinition bd) {
 // 省略
 //如果当前元素没有设置 lazyInit 懒加载;则去 this.defaults.getLazyInit();这个defaults是上一篇分析过的;整个xml文件全局的默认值;
        String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
        if (DEFAULT_VALUE.equals(lazyInit)) {
            lazyInit = this.defaults.getLazyInit();
        }
//省略....
}

三、lazy-init发挥作用的地方

@Override
	public void refresh() throws BeansException, IllegalStateException {
	// 忽略..
	// 实例化所有剩余非 lazy-init 为true的单例对象
		finishBeanFactoryInitialization(beanFactory);
	// 忽略..
	}

最终执行了 beanFactory.preInstantiateSingletons();

	@Override
	public void preInstantiateSingletons() throws BeansException {
		if (this.logger.isDebugEnabled()) {
			this.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.
		List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
		// 遍历所有bean
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			//1.bd的abstract属性是false;<bean abstract="false"> 不能被实例化,它主要作用是被用作被子bean继承属性用的;
			//2.单例对象并且 lazy-init为false
			//满足上面条件才行
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
			/**
			*如果实现了FactoryBean接口
			*1.先将FactoryBean的实现类实例化;
			*2.判断是否将FactoryBean实现类的getObject方法返回的实例对象也实例化;判断依据
			*  2.1如果当前bean实现了SmartFactoryBean接口,并且isEagerInit()返回true;才会调用工厂类的方法
			*/
				if (isFactoryBean(beanName)) {
					final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
					boolean isEagerInit;
					if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
						isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
							@Override
							public Boolean run() {
								return ((SmartFactoryBean<?>) factory).isEagerInit();
							}
						}, getAccessControlContext());
					}
					else {
						isEagerInit = (factory instanceof SmartFactoryBean &&
								((SmartFactoryBean<?>) factory).isEagerInit());
					}
					if (isEagerInit) {
						getBean(beanName);
					}
				}
				else {//如果不是FactoryBean接口之间实例化
					getBean(beanName);
				}
			}
		}
		// 调用所有SmartInitializingSingleton类型的实现类的afterSingletonsInstantiated方法;通过名字可以知道它表示 单例对象实例化后需要做的操作
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged(new PrivilegedAction<Object>() {
						@Override
						public Object run() {
							smartSingleton.afterSingletonsInstantiated();
							return null;
						}
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
	}

四、问答

1.Ioc容器在实例化bean的时候,Ioc会主动调用FactoryBean类型的的getObject方法来为我们生成对象吗?

答: 一般情况下是不会的,一般情况碰到FactoryBean类型的是调用 getBean(&beanName),但是有一种情况例外,如果这个FactoryBean还实现了SmartInitializingSingleton接口的话,IOC就会帮我们主动调用getBean(beanName)来实例化;

以上就是lazy init控制加载在Spring中如何实现源码分析的详细内容,更多关于Spring lazy init控制加载的资料请关注我们其它相关文章!

(0)

相关推荐

  • 聊聊Kotlin 中 lateinit 和 lazy 的原理区别

    目录 lateinit 用法 原理 lazy 用法 原理 the end references 使用 Kotlin 进行开发,对于 latelinit 和 lazy 肯定不陌生.但其原理上的区别,可能鲜少了解过,借着本篇文章普及下这方面的知识. lateinit 用法 非空类型可以使用 lateinit 关键字达到延迟初始化. class InitTest() { lateinit var name: String ​ public fun checkName(): Boolean = name

  • Kotlin lateinit与by lazy案例详解

    lateinit 和 lazy 是 Kotlin 中的两种不同的延迟初始化的实现 lateinit 只用于变量 var,而 lazy 只用于常量 val lazy 应用于单例模式(if-null-then-init-else-return),而且当且仅当变量被第一次调用的时候,委托方法才会执行. lazy()是接受一个 lambda 并返回一个 Lazy <T> 实例的函数,返回的实例可以作为实现延迟属性的委托: 第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式并

  • 浅谈spring中的default-lazy-init参数和lazy-init

    在spring的配置中的根节点上有个  default-lazy-init="true"配置: 1.spring的default-lazy-init参数 此参数表示延时加载,即在项目启动时不会实例化注解的bean,除非启动项目时需要用到,未实例化的注解对象在程序实际访问调用时才注入调用 spring在启动的时候,default-lazy-init参数默认为false,会默认加载整个对象实例图,从初始化ACTION配置.到 service配置到dao配置.乃至到数据库连接.事务等等.这样

  • lazy init控制加载在Spring中如何实现源码分析

    目录 一.lazy-init说明 二.lazy-init 属性被设置的地方 三.lazy-init发挥作用的地方 四.问答 一.lazy-init说明 ApplicationContext实现的默认行为就是在启动时将所有singleton bean提前进行实例化(也就是依赖注入). 提前实例化意味着作为初始化过程的一部分,ApplicationContext实例会创建并配置所有的singleton bean. 通常情况下这是件好事,因为这样在配置中的任何错误就会即刻被发现(否则的话可能要花几个小

  • JDK1.8中的ConcurrentHashMap源码分析

    一.容器初始化 1.源码分析 在jdk8的ConcurrentHashMap中一共有5个构造方法,这四个构造方法中都没有对内部的数组做初始化, 只是对一些变量的初始值做了处理 jdk8的ConcurrentHashMap的数组初始化是在第一次添加元素时完成 // 没有维护任何变量的操作,如果调用该方法,数组长度默认是16 public ConcurrentHashMap() { } // 传递进来一个初始容量,ConcurrentHashMap会基于这个值计算一个比这个值大的2的幂次方数作为初始

  • java 中RandomAccess接口源码分析

    java 中RandomAccess接口源码分析 RandomAccess是一个接口,位于java.util包中. 这个接口的作用注释写的很清楚了: /** * Marker interface used by <tt>List</tt> implementations to indicate that * they support fast (generally constant time) random access. The primary * purpose of this

  • PHP通过反射动态加载第三方类和获得类源码的实例

    使用反射动态加载第三方类 用反射加载第三方类用处在于: 使用XML或其他配文件配置要加载的类,从而和系统源代码分离. 对加载的类进行类检查,是加载的类符合自己定义的结构. <?php abstract class Module { #核心Module类库 function baseFunc() { echo "I am baseFunc"; } abstract function execute(); } class ModuleRunner { private $configD

  • 深入解析spring AOP原理及源码

    目录 @EnableAspectJAutoProxy 找切面 代理对象的创建 代理方法的执行 ExposeInvocationInterceptor#invoke 环绕通知的执行 前置通知的执行 后置通知的执行 返回后通知的执行 异常通知的执行 @EnableAspectJAutoProxy @EnableAspectJAutoProxy注解用于开启AOP功能,那么这个注解底层到底做了什么呢? 查看@EnableAspectJAutoProxy的源码,发现它使用@Import注解向Spring容

  • java 将数据加载到内存中的操作

    将数据加载到内存中 1.建立InitListener.java package app.util; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.ServletException; import javax.servlet.http.

  • springboot 加载 META-INF/spring.factories方式

    目录 springboot 加载 META-INF/spring.factories 用户应用程序Application 建立META-INF/spring.factories文件的意义何在 平常我们如何将Bean注入到容器当中 springboot 加载 META-INF/spring.factories 用户应用程序Application ConfigurableApplicationContext context = SpringApplication.run(NacosSpringBoo

  • SpringBoot实现加载yml文件中字典数据

    将字典数据,配置在 yml 文件中,通过加载yml将数据加载到 Map中 Spring Boot 中 yml 配置.引用其它 yml 中的配置.# 在配置文件目录(如:resources)下新建application-xxx 必须以application开头的yml文件, 多个文件用 "," 号分隔,不能换行 项目结构文件 application.yml server: port: 8088 application: name: VipSoft Env Demo spring: pro

  • Android中ListView异步加载图片错位、重复、闪烁问题分析及解决方案

    Android ListView异步加载图片错位.重复.闪烁分析以及解决方案,具体问题分析以及解决方案请看下文. 我们在使用ListView异步加载图片的时候,在快速滑动或者网络不好的情况下,会出现图片错位.重复.闪烁等问题,其实这些问题总结起来就是一个问题,我们需要对这些问题进行ListView的优化. 比如ListView上有100个Item,一屏只显示10个Item,我们知道getView()中convertView是用来复用View对象的,因为一个Item的对应一个View对象,而Ima

  • 使用ajax加载的页面中包含的javascript的解决方法

    1.[使用iframe] 在需要加载的页面中添加一个iframe,如下 复制代码 代码如下: <iframe style="display:none" onload="javascript: close_ticket_onload()"></iframe> onload中添加你所要调用的函数,如果加载的页面在弹出层中,此函数需要放在弹出此层的父页面! 2.[使用eval] 如果使用innerHTML填充XMLHttp取得的数据.如:xxx.i

随机推荐