作者:石臻臻,CSDN 博客之星 Top5、Kafka Contributor、nacos Contributor、华为云 MVP,腾讯云 TVP,滴滴 Kafka 技术专家、 KnowStreaming, 《Kafka 运维与实战宝典》电子书作者。领取《Kafka 运维与实战宝典》PDF 请联系石臻臻
KnowStreaming 是滴滴开源的Kafka运维管控平台, 有兴趣一起参与参与开发的同学,但是怕自己能力不够的同学,可以联系我,当你导师带你参与开源! 。
一、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)来实例化;
评论