了解Spring Boot的SmartInitializingSingleton:工作机制与实际应用

本文详细介绍了Spring中的SmartInitializingSingleton扩展点,包括其功能特性(用于启动后操作,仅对非懒加载单例生效)、实现方式(通过setter注入和接口实现)及工作原理(在Bean实例化后执行afterSingletonsInstantiated)。
摘要由CSDN通过智能技术生成

原创 凡夫贬夫 凡夫贬夫 2023-12-09 09:21 发表于河南

图片

前言

这篇文章会重点分析一下SmartInitializingSingleton扩展点的功能 特性、实现方式 、工作原理。SmartInitializingSingleton扩展点内只有一个扩展方法,且执行时机在Spring Bean的生命周期里比较靠后,很重要,但是也很简单。

功能特性

1、SmartInitializingSingleton主要用于在Spring容器启动完成时进行扩展操作,即afterSingletonsInstantiated();

2、实现SmartInitializingSingleton接口的bean的作用域必须是单例,afterSingletonsInstantiated()才会触发;

3、afterSingletonsInstantiated()触发执行时,非懒加载的单例bean已经完成实现化、属性注入以及相关的初始化操作;

3、afterSingletonsInstantiated()的执行时机是在DefaultListableBeanFactory#preInstantiateSingletons();


关于Spring bean有七种作用域:默认singleton(单例)、prototype、request、session、globalSession、application、websocket;

1、singleton(单例):Spring容器只会创建一个bean对象;

2、prototype:每次获取bean都会重新创建一个bean对象;

3、request:对于每一个http请求,在同一个请求内Spring容器只会创建一个bean对象,若请求结束,bean也随之销毁;

4、session:在同一个http会话里,Spring容器只会创建一个bean对象,若传话结束,也随之销毁;

5、globalSession:globalSession作用域的效果与session作用域类似,但是只适用于基于portlet的web应用程序中

6、application:在servlet程序中,该作用域的bean将会作为ServletContext对象的属性,被全局访问,与singleton的区别就是,singleton作用域的bean在Spring容器中只一;application作用域的bean在ServletContex中唯一;

7、websocket:为每个websocket对象创建一个实例。仅在Web相关的ApplicationContext中生效。


实现方式

这里用一个示例验证SmartInitializingSingleton的功能特性,并且通过debug分析其工作过程:

1、定义Dog类,以setter注入方式进行属性注入,同时Dog类实现SmartInitializingSingleton接口,重写afterSingletonsInstantiated(),并在方法内部打印日志,如果在实际业务开发过程中用到了这个扩展点,相关的扩展操作逻辑就是在这个方法内实现;

@Slf4jpublic class Dog implements InitializingBean, DisposableBean, SmartInitializingSingleton {    private String name = "wang cai";
    private Food food;
    public Dog() {        log.info("----Dog的无参构造方法被执行");    }    @Autowired    public void setFood(Food food) {        this.food = food;        log.info("----dog的food属性被注入");    }    @Override    public void afterPropertiesSet() throws Exception {        log.info("----com.fanfu.entity.Dog.afterPropertiesSet触发执行");    }
    public void myInitMethod() {        log.info("----com.fanfu.entity.Dog.myInitMethod触发执行");    }
    @Override    public void destroy() throws Exception {        log.info("----com.fanfu.entity.Dog.destroy触发执行");    }
    @Override    public void afterSingletonsInstantiated() {        log.info("----com.fanfu.entity.Dog.afterSingletonsInstantiated触发执行");    }}

2、定义Food类,作为Dog类的一个属性;

@Slf4jpublic class Food {    private String name = "大骨头";
    public Food() {        log.info("----Food的无参数构造方法被执行");    }}

3、使用Configuration配置类的方式注册Dog、Food到Spring容器里;默认是单例对象;

@Configurationpublic class SpringConfig {    @Bean(initMethod = "myInitMethod")    public Dog dog(){        Dog dog = new Dog();        return dog;    }    @Bean    public Food food(){        Food food = new Food();        return food;    }}

4、单元测试,用于验证结果;

@Test    public void test5(){        log.info("----单元测试执行开始");        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.fanfu");        log.info("----单元测试执行完毕");    }

图片

工作原理

如果我分享的关于Springboot扩展点系统的文章,从头都一点一点自己debug看过的话,一定对AbstractApplicationContext#refresh()不陌生,与Spring容器启动相关的核心逻辑都在这个方法中。

1、在AbstractApplicationContext#refresh()中,会发现调用了finishBeanFactoryInitialization(),从上面的注释可以看出,这个方法昌要实例化所有非懒加载的单例bean;

图片

2、进入到finishBeanFactoryInitialization()方法内,做了一些beanFactory的准备工作后,调用preInstantiateSingletons()开始非懒加载的单例bean实例化;

图片

3、继续往下实际上是调用 了DefaultListableBeanFactory#preInstantiateSingletons(),单独看这个方法,逻辑相对简单,分为两部分:第一部分,bean的实例化、属性注入、相关初始化操作;第二部分,找出所有实现了SmartInitializingSingleton接口的实现类,遍历并执行afterSingletonsInstantiated();

public void preInstantiateSingletons() throws BeansException {    //----------start------------------实例化bean----------------------------------------   if (logger.isTraceEnabled()) {      logger.trace("Pre-instantiating singletons in " + this);   }   List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
   // Trigger initialization of all non-lazy singleton beans...   for (String beanName : beanNames) {      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {         if (isFactoryBean(beanName)) {            Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);            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 {                  isEagerInit = (factory instanceof SmartFactoryBean &&                        ((SmartFactoryBean<?>) factory).isEagerInit());               }               if (isEagerInit) {                  getBean(beanName);               }            }         }         else {            getBean(beanName);         }      }   }   //----------end------------------实例化bean----------------------------------------   //----------start------------------SmartInitializingSingleton#afterSingletonsInstantiated----------------------------------------   for (String beanName : beanNames) {      Object singletonInstance = getSingleton(beanName);      //判断单例bean是否实现了SmartInitializingSingleton接口      if (singletonInstance instanceof SmartInitializingSingleton) {         SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;         if (System.getSecurityManager() != null) {            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {               smartSingleton.afterSingletonsInstantiated();               return null;            }, getAccessControlContext());         }         else {             //执行afterSingletonsInstantiated()            smartSingleton.afterSingletonsInstantiated();         }      }   }   //----------start------------------SmartInitializingSingleton#afterSingletonsInstantiated----------------------------------------}

总结

如果要在业务开发中使用SmartInitializingSingleton扩展点,需要特别注意实现这个接口的bean应该是非懒加载的单例bean,执行时机是在bean完成实例化、属性注入、相关初始化操作后,否则无法触发执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值