一直以来,SpringFramework 作为 Java 企业级开发的老大哥,面试中也常被问到。虽说有些基础性的问题可能不那么特别被面试官和求职者重视,但如果真的问起来,能不能回答的准确、全面、有深度,还是很容易体现出水平的。
在接下来的一个系列中,我会慢慢盘点一些 Spring 中常见但不好回答全面的问题,跟小伙伴们分享。
本文主题:Spring 的 IOC 如何回答的尽可能全面、准确、有深度。
这是一个看上去特别简单、感觉很无脑的问题,很多求职者在回答这个问题时会不加思考、快速回答出一个很简单但同时也没有什么含量的答案。
试问一句,亲,你在做名词翻译吗?
就算真的是在做名词翻译,也不应该只是把这个简称的全称解释出来就完事吧,好歹的展开解释点东西也好吧,作为 面试官不应该只想听到这么一点点 吧。
这里面大体上把 IOC 的核心思想解释出来了: 对象间的依赖关系的维护权利发生了转移 ,但是请小伙伴们注意,我们在问 IOC , 这个问题仅仅是问 IOC 本身,与具体的技术无关 。 IOC 不止有 Spring ,只是当下最强大的、使用最广的是 Spring 而已。
所以小伙伴们在 回答理论、概念等问题时,不要直接在概念解释中提到具体的技术,技术都是概念和理论的落地实现,不止一种 。单把一种拉出来,面试官可能会觉得:你是不是只知道这个?
该答案仅供参考,可根据自身的知识储备动态调整。
IOC 全名 控制反转 Inverse of Control,它是一种 编程原则 ,它的设计和架构可以实现 组件间的解耦 ,核心思想是 将控制权转移出去 。
这里面提到了几个点:
如果对 IOC 的实现不是特别了解,或者只是用 SpringFramework 用的太习惯了,亦或是刻板的学习 SpringFramework ,那这个答案通常会是这样的:
如果回答出这个答案,而面试官碰巧也跟你一样,那恭喜你“瞎猫碰到死耗子”了!因为这个回答真的 大错特错 啊,IOC 不止有 DI 的!
正确的回答应该是:
上面已经介绍过了,IOC 仅仅是一种思想,它的意图是想让对象间的依赖控制发生转换。用过 SpringFramework 的小伙伴都知道,你没有在哪个地方见过直接写 IOC 的代码,都是由一些实现方式来体现 IOC 的。
如果按照上面这样回答,可能会引来下面一个问题:
针对这个问题,最好不要一上来就搬出代码解释,最好是先理论后代码。
一般情况下,对比依赖查找和依赖注入,通常可以从以下几个维度对比:
依赖查找 | 依赖注入 | |
---|---|---|
实现方式 | 使用上下文(容器)主动获取 | 依赖上下文被动接收 |
作用目标 | 通常是方法体内的局部变量,也可以是对象成员 | 通常是对象成员 |
API依赖 | 依赖 IOC 框架的 API(必须操纵容器的 API ) | 可以不依赖(暴露 setter 方法即可) |
applicationContext.getBean(beanName) | public void setXXX() { ... } |
真的不会有小伙伴只能答出 ApplicationContext
吧,一开始学的时候应该知道还有个 BeanFactory
吧。这是 SpringFramework 中两个核心的 IOC 容器的抽象。 BeanFactory
仅仅是提供了一个容器管理的基本能力, ApplicationContext
在此基础上做了更加完善、强大的扩展。具体的对比可以参照下表:
Feature | BeanFactory |
ApplicationContext |
---|---|---|
Bean instantiation/wiring —— Bean的实例化和属性注入 | Yes | Yes |
Integrated lifecycle management —— 生命周期管理 | No | Yes |
Automatic BeanPostProcessor registration —— Bean后置处理器的支持 |
No | Yes |
Automatic BeanFactoryPostProcessor registration —— BeanFactory后置处理器的支持 |
No | Yes |
Convenient MessageSource access (for internalization) —— 消息转换服务(国际化) |
No | Yes |
Built-in ApplicationEvent publication mechanism —— 事件发布机制(事件驱动) |
No | Yes |
下面提供一个比较完整的示例答案,小伙伴们可以根据自己的知识储备和理解,调整这里面的一些描述细节:
BeanFactory
接口提供了一个 抽象的配置和对象的管理机制 , ApplicationContext
是 BeanFactory
的子接口,它 简化了与 AOP 的整合、消息机制、事件机制 ,以及 对 Web 环境的扩展 ( WebApplicationContext
等), BeanFactory
是没有这些扩展的。
ApplicationContext
主要扩展了以下功能:(括号内的部分是解释扩展功能的一些简单描述或者原理底层实现,能回答出来更好)
AnnotationAwareAspectJAutoProxyCreator
作用于 Bean 的初始化之后 ) BeanDefinition
、 Environment
、注解等 ) Resource
抽象 ) ApplicationEvent
、 ApplicationListener
) LocaleResolver
) Environment
抽象 (SpringFramework 3.1以后) 注意这个问题也是与 SpringFramework 无关的,注入的方式本身就应该是依赖注入的实现,至于框架的代码,那是人家对于这个方式的落地。
可从以下几个维度对比:
注入方式 | 被注入成员是否可变 | 是否依赖IOC框架的API | 使用场景 |
---|---|---|---|
构造器注入 | 不可变 | 否(xml、编程式注入不依赖) | 不可变的固定注入 |
参数注入 | 不可变 | 是(只能通过标注注解来侵入式注入) | 通常用于不可变的固定注入 |
setter注入 | 可变 | 否(xml、编程式注入不依赖) | 可选属性的注入 |
基本上问这个问题的话,还可能会继续问另一个问题:
“莽夫”应聘者一看终于来开放式问题了,赶紧开始自由发挥了:
我觉得参数注入好,因为我写习惯了,给参数打注解多舒服啊!
你是这么说了,面试官咋想:就这?就这???进而对你的印象可能就会有所减分了。
这种问题,除了要表述主观看法之外,更多的是要 根据一些既有的论述来辅助你的观点 ,最好的论述那一定是官方文档了。
SpringFramework 的官方文档在不同的版本推荐的注入方式是不同的:
相信大多数小伙伴都能答出 @Autowired
和 @Resource
吧,如果答出这两个,那证明你应该用过,也会用。但你能回答出 @Inject
,证明你对这些注入的注解确实有了解。作为应聘者,在回答问题时一定是回答的 尽可能全面 为好,下面对这几种注解作一个对比:
注解 | 注入方式 | 是否支持@Primary | 来源 | Bean不存在时处理 |
---|---|---|---|---|
@Autowired | 根据类型注入 | 是 | SpringFramework原生注解 | 可指定 required=false 来避免注入失败 |
@Resource | 根据名称注入 | 否 | JSR250规范 | 容器中不存在指定Bean会抛出异常 |
@Inject | 根据类型注入 | 是 | JSR330规范 ( 需要导jar包 ) | 容器中不存在指定Bean会抛出异常 |
跟上面差不多,如果问到了这个问题,那就有可能继续被问到下面一个问题:
可能大多数小伙伴都能答出以下几种解决方案:
@Resource
:根据名称指定注入的 Bean @Qualifier
:配合 @Autowired
注解使用,如果被标注的成员 / 方法在根据类型注入时发现有多个相同类型的 Bean ,则会根据该注解声明的 name 寻找特定的 bean @Primary
:配合 @Bean
注解使用,如果有多个相同类型的 Bean 同时注册到 IOC 容器中,使用 @Autowired
、 @Inject
注解时会注入标注 @Primary
注解的 bean 其实你还可以提另外一种方案:把 注入的字段名与 bean 的名称保持一致 ,这样也可以解决注入时报不唯一 Bean 的问题。
以上几个问题是关于 SpringFramework 与 IOC 部分的一些常见问题,倒是问题都不太陌生,但是小伙伴们想回答的全面、有深度,还是需要下下功夫的。希望小伙伴们能有所收获,在面试中流利回答,斩获 offer !
问题整理不易,不点个赞支持一下作者吗?(可怜巴巴)