面试官问为什么用Spring,程序员:因为方便?面试官:什么?

过完年了,应该有很多程序员开始准备面试了吧?这里帮大家整理了一些关于Spring的知识。相信绝大多数Java都曾经被面试官问到过"什么是IoC"、什么是"依赖注入",为什么要使用Spring,Spring是怎么初始化、Spring怎么解决循环依赖、bean的类型有哪些等问题。面试被问到这些问题,不会,然后上网逐个搜索答案总不是个好习惯,应该彻底把Spring弄懂才对。看完这篇文章之后,面对什么是Spring,为什么要使用Spring这类问题的时候,再也不会说出"因为用起来方便"这样让人尴尬的回答了,并且还能对Spring框架有自己的看法,让面试官给对你产生好感。



面试官问为什么用Spring,程序员:因为方便?面试官:什么?

讲解Spring容器初始化过程前,顺便说一下,没必要畏惧阅读Spring的源码。Spring源码相对于JDK而言,简直太容易看懂,代码清晰明了,设计模式应用得恰到好处,也没有java.util.concurrent包中那些令人头疼的循环和状态变量,类名、变量名、函数名几乎不用解释,代码看一遍就能懂,学会非常容易。网上还有人宣称"Spring源码至少要看3遍"的,我觉得看一遍差不多了吧,看3遍太浪费时间。

Spring是容器还是工厂

使用过Spring的同学都知道,"Spring是一个容器"。其实,这种说法不太准确,Spring提供的容器不止一种,例如有ClasspathXmlApplicationContext、XmlWebApplicationContext等多种容器,所有这些容器的父类都是BeanFactory。

但是为什么起名叫"工厂"呢?单纯的"容器"只起到一个存放对象的作用,如果只是想把对象存放起来,那么ArrayList,HashSet等Java集合类完全可以胜任,根本用不着Spring。Spring容器并不是单纯用于存放对象的,它存放对象定义(bean definition),而不止是存放对象(bean)。当需要一个对象时,我们从bean容器获取,而bean容器(BeanFactory)负责根据定义创建对象,所以说它是一个"工厂"。



面试官问为什么用Spring,程序员:因为方便?面试官:什么?

反过来,它又为什么叫做"容器"?这是因为在大多数时候,bean都被定义为单例(singleton,与GoF中的singleton略有不同),且这些bean的类是Spring提供的ClassLoader加载的,单例的bean被实例化一次之后就一直保存在这个工厂中,以后每次请求获得这个bean都是同一个对象,相当于这个对象被放在BeanFactory中了,所以它又可以视为一个容器。



面试官问为什么用Spring,程序员:因为方便?面试官:什么?

而对于非singleton的bean来说,例如scope等于prototype的bean,BeanFactory就主要表现为一个工厂了,每次请求都返回一个不同的实例。

既然说到scope,顺便分享一道常见的Spring面试题,"Spring中bean/对象的类型(scope)有哪些"?回答:取决于你所使用的具体容器。如果使用的是基本容器,例如ClasspathXmlApplicationContext,那么只有singleton和prototype可用;如果使用的是Web容器(例如WebXmlApplicationContext),那么就有singleton, prototype,request,session和global等可用;另外,scope还可以自定义。

Spring通过把自己设计成一个管理应用程序模块定义的容器以及工厂,而不是管理模块自身的容器,让模块可以分别独立开发,实现了模块之间的解耦。

深入理解Dependency Injection和Inversion of Control

依赖注入(Depdendency Injection)和控制反转(IoC,Inversion of Control)是同一个概念,Spring实践了这个思想,为Java Web应用程序建立起一个基本框架。换句话说,Spring是Java Web应用程序的基本框架。下次面试官如果问你,"为什么要使用Spring",你可以回答"它提供了一个Web应用程序的基本框架",然后说它是工厂和容器,提供了IoC,不要回答"用起来方便"

因为任何一个大型应用程序都包含很多组件(components),组件之间互相调用,并且在运行时并非所有组件都必须创建。如果没有一个整体的结构(框架),程序将会变得混乱、难以维护。假如不使用Spring,那么每次需要调用另一个组件的时候就new一个对象,或者getInstance得到一个实例。当组件越来越多,程序规模越来越大,这种方式会让组件之间的耦合度很高,难以维护。

Spring框架基于BeanFactory,把模块的配置信息(bean definition)统一管理起来(有的模块可以根据需要创建,若不调用则不创建),同时,还把模块的配置参数也统一管理(每个模块不必自己读配置参数,由Spring加载)。程序结构变清晰很多。在此基础上,加上依赖注入的功能,即Spring负责把模块依赖的其他模块推送(push)进来,而不是模块自己去拉取(pull),所以是一个"反转(IoC)",进一步简化了大型应用程序开发。

根据上面讨论,我们总结一下,为什么要使用Spring:

  1. Spring提供一个容器/工厂,统一管理模块的定义,根据需要创建。
  2. 把模块的配置参数统一管理,模块不需要自行读取配置。
  3. Spring提供依赖注入,把依赖的模块自动推送进来,不需要模块自己拉取。
  4. 此外,Spring提供了对很多其他第三方框架的集成功能,减少了样板代码(boilerplate)。

Spring中bean的生命周期

在Web应用程序启动过程中,Spring容器中的每个bean也有各自的初始化顺序。一个bean,从bean definition被加载到初始化完成,按照以下顺序执行:

1. BeanFactory加载完bean definition和class,实例化除了bean对象。

2. 检查有没有实现BeanNameAware,有则调用setBeanName(得到bean id)

3. 检查有没有实现BeanClassLoaderAware,有则调用setBeanClassLoader。

4. 检查有没有实现EnvironmentAware,有则调用setEnvironment。

5. 检查有没有实现EmbeddedValueResolverAware,有则调用setEmbeddedValueResolver。

6. 检查有没有实现ResourceLoaderAware,有则调用setResourceLoader。

7. 检查有没有实现ApplicationEventPublisherAware,有则调用setApplicationEventPublisher。

8. 检查有没有实现MessageSourceAware,有则调用setMessageSource。

9. 检查有没有实现ApplicationContextAware,有则调用setApplicationContext。

10. 检查有没有实现ServletContextAware,有则调用setServletContext。

11. 调用BeanPostProcessors中的所有postProcessBeforeInitialization,对bean进行一些更进一步的配置。

12. 调用InitializingBean接口中的afterPropertiesSet执行bean自身提供的初始化代码。

13. 调用通过其他方式指定的init-method方法,执行bean自身的初始化。

14. 调用BeanPostProcessors中的所有postProcessAfterInitialization方法。

经历了上述的14个步骤,spring bean才完整地创建出来,很不容易。从以上步骤可以了解到,在初始化方法中哪些资源是已经准备好可以访问的,哪些是还不能访问的。

发布了8 篇原创文章 · 获赞 18 · 访问量 8868

猜你喜欢

转载自blog.csdn.net/weixin_38054045/article/details/104147323