最近很多小伙伴出去面试,都觉得自己面试的不是很理想。回来后很多朋友把面试题录下来发给我。我给大家分析一下,然后发出去。当我看到这些面试题的时候,我脑子里快速构建了整个知识体系,从基础到框架,从分布式到微服务,从数据结构到算法,从虚拟化到云原生,从大数据到云计算,从实战项目到性能调优。其实这些面试本质上并不难,很多都是基于基础知识的考察。从今天开始,我们就一一攻破这几大厂商的面试题。好了,开始今天的正文。问题:正如标题所说:SpringIOC容器中是否只有一个单例bean?先给个结论吧。这里想来想去还是直接说吧:对,SpringIOC容器中只存放单例bean。接下来详细说明一下哈萨克斯坦只存单例bean。问题分析既然我们已经知道SpringIOC容器中只存放了singletonbean,但是面试的时候不能只说这句话,否则面试官会直接pass你。为什么?如果你只说这句话,面试官可能会认为你不懂事,不懂事的概率是50%。如果你无知和错误,面试官认为你不会。如果你无知而对,面试官也可能认为你不会。因此,除了要正确回答结论外,还需要说清楚为什么SpringIOC容器中只存放单例bean。好了,我们正式开始分析这个问题。IOC容器在初始化的时候,会在singletonObjects的ConcurrentHashMap中初始化所有的bean,bean都是单例的。获取bean时,会先从singletonObjects中获取。通过调试,发现如果scope是单实例,是可以获取到bean的。如果范围是多个实例,则无法获取bean。需要从一个名为mergedBeanDefinitions的ConcurrentHashMap中获取bean的定义,然后根据bean的作用域决定如何创建bean。如果scope=prototype,每次都会创建一个新的实例。到这里,我们可以大致得出一个结论,IOC在初始化的时候,只会实例化scope=singleton的对象(单例),不会实例化scope=prototype的对象(多实例)。接下来我们调试一下Spring的源码。首先,我们创建一个Person类,用于将范围作为多个实例进行测试,并获取不同的实例,如下所示。publicclassPerson{@Value("张三")privateStringname;@Value("#{20-2}")privateIntegerage;@Value("${person.nickName}")privateStringnickName;publicPerson(){}publicPerson(Stringname,Integerage){this.name=name;this.age=age;}//省略get/set}接下来,创建一个MainConfig类,如下所示。@ConfigurationpublicclassMainConfig{@Bean("person")@Scope("prototype")publicPersonperson(){System.out.println("添加Person到容器中...");returnnewPerson("张三",25);}}可以看出,此时MainConfig测试的是scope为多个实例,获取不同实例的场景。而如果要测试scope为单例的场景,获取同一个实例,只需要去掉MainConfig类中person()方法上的@Scope("prototype")注解即可,如下图。@ConfigurationpublicclassMainConfig{@Bean("person")publicPersonperson(){System.out.println("将Person添加到容器中...");returnnewPerson("张三",25);}}接下来写另一个main方法用于启动测试程序。publicstaticvoidmain(String[]args){ApplicationContextapplicationContext=newAnnotationConfigApplicationContext(MainConfig.class);Personperson=applicationContext.getBean(Person.class);Personperson2=applicationContext.getBean(Person.class);if(person.equals(person2)){System.out.println("Sameinstance");}else{System.out.println("Differentinstance");}}启动程序,开始调试测试单例。Debugsingletonscope经过debug调试,在单例的情况下,第一次从singletonObjects的map中获取的bean是空的,后面每次获取的时候,从singletonObjects的map中获取的bean都不为空,会直接返回要从这个Map中得到的值。第一次从singletonObjects取值如下。第二次从singletonObjectsMap中获取到的bean不再为空。此时,命令行打印相同的实例。说明在单例作用域下,每次共享一个bean实例,这个bean实例保存在容器中。调试多实例如果作用域是多实例,无论外界获取多少个bean,都无法从singletonObjectsMap中获取到对应的bean实例,需要每次创建并返回一个新的bean。通过调试源码可以发现,当bean有多个实例时,每次都会从一个名为mergedBeanDefinitions的HashMap中获取一个RootBeanDefinition对象,其中包含了bean的一些基本信息,如下图。接下来根据bean的scope属性进行处理。如果作用域是单个实例,则直接从容器中获取。如果范围是多个实例,则每次都会创建一个实例。此时,命令行会打印出不同的实例。说明在多实例作用域下,每次都会创建并返回一个bean实例。总结一下:SpringIOC容器中只存放了单例bean。本文转载自微信公众号“冰河科技”,可通过以下二维码关注。转载本文请联系冰川科技公众号。
