侧边栏壁纸
博主头像
Xee博主等级

为了早日退休而学

  • 累计撰写 44 篇文章
  • 累计创建 8 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

Spring 常见面试题

Xee
Xee
2022-03-23 / 0 评论 / 0 点赞 / 1,029 阅读 / 3,590 字

说下你理解的 Spring

可以从两个层面来理解 Spring。

第一个层面指的是 Spring Framework,是一个开源的应用框架,提供 IOCAOP 降低了应用开发的复杂度。

第二个层面指的是 Spring 全家桶,Spring 发展到今天可以说几乎是统领了 Java,有关 Java 应用开发所需的全部功能, Spring 都提供了解决方案。

包括对批处理的支持、对 web 的支持、对微服务的支持等。
这种题大致说一下就行了,不需要太多细节,面试官会接着追问的。

看过源码吗?说下 Spring 由哪些重要的模块组成?

Spring 最主要的核心就是一个容器,这个容器根据我们的配置文件创建了各种 Bean 且编织它们之间的依赖关系,管理这些 Bean 的生命周期。
所以最核心的代码模块是:

  • spring-core:核心类库,包括资源访问等一些工具类,别的模块都会依赖此模块
  • spring-beans:对 Bean 的支持,包括控制反转和依赖注入,核心工厂 BeanFacotry 就在这个模块。
  • spring-context:Spring 的上下文,支持数据验证、国际化、事件传播等支持,ApplicationContext 就在这里,可以理解为运行时的 Spring 容器。
    基于上面的这些核心模块,Spring 也提供了很多重要的支持:
  • spring-aop:AOP 的支持
  • spring-jdbc:jdbc 的支持
  • spring-orm: orm 的支持
  • spring-webmvc: mvc 的支持

什么是IOC

IOC,即Inversion of Control,控制反转。
首先要明确 IOC 是一种思想,而不是一个具体的技术,其次 IOC 这个思想也不是 Spring 创造的。

然后我们要理解到底控制的是什么,其实就是控制对象的创建,IOC 容器根据配置文件来创建对象,在对象的生命周期内,在不同时期根据不同配置进行对象的创建改造。

那什么被反转了?

其实就是关于创建对象且注入依赖对象的这个动作,本来这个动作是由我们程序员在代码里面指定的。

例如对象 A 依赖对象 B,在创建对象 A 代码里,我们需要写好如何创建对象 B,这样才能构造出一个完整的 A。

而反转之后,这个动作就由 IOC 容器触发,IOC 容器在创建对象 A 的时候,发现依赖对象 B ,根据配置文件,它会创建 B,并将对象 B 注入 A 中。

这里要注意,注入的不一定非得是一个对象,也可以注入配置文件里面的一个值给对象 A 等等。

至此,你应该明确了,控制和反转。

IOC有什么好处

对象的创建都由 IOC 容器来控制之后,对象之间就不会有很明确的依赖关系,使得非常容易设计出松耦合的程序。

例如,对象 A 需要依赖一个实现,但是对象都由 IOC 控制之后,我们不需要明确地在对象 A 的代码里写死依赖的实现。

例如只需要写明依赖一个接口,这样我们的代码就能顺序的编写下去。
然后,我们可以在配置文件里定义 A 依赖的具体的实现(比如 B),根据配置文件,在创建 A 的时候,IOC 容器就知晓 A 依赖的 B,这个注入这个依赖即可。

如果之后你有新的实现需要替换,那 A 的代码不需要任何改动,你只需要将配置文件 A 依赖 B 改成 B1,这样重启之后,IOC 容器会为 A 注入 B1。
这样就使得类 A 和类 B 解耦了, very nice!

并且也因为创建对象由 IOC 全权把控,那么我们就能很方便的让 IOC 基于扩展点来“加工”对象。

例如我们要代理一个对象,IOC 在对象创建完毕,直接判断下这个对象是否需要代理,如果要代理,则直接包装返回代理对象。
这等于我们只要告诉 IOC 我们要什么,IOC 就能基于我们提供的配置文件,创建符合我们需求的对象。

正是这个控制反转的思想,解放了我们的双手。

什么是DI

DI,Dependency Injection,依赖注入。

普遍的答案是 DIIOC 的一种实现。

其实它跟 IOC 的概念一致,只是从不同角度来描述罢了。

Martin Fowler 提出了 DI 的概念,它觉得用 DI 来形容更加具体。

大致理解为容器在运行的时候,可以找到被依赖的对象,然后将其注入,通过这样的方式,使得各对象之间的关系可由运行期决定,而不用在编码时候明确。

什么是Bean

可以认为,由 Spring 创建的、用于依赖注入的对象,就是 Bean。

例如调用 getBean 能返回的玩意,就是 Bean。

BeanFactory 什么用

BeanFactory 其实就是 IOC 的底层容器。

我们都说 SpringIOC容器,说的再直白点,其实就是 Bean 的工厂,它帮我们生产和管理 Bean,如果我们需要 Bean 就从工厂拿到 bean,所以再来理解下 BeanFactory 这个名字,就知晓它就是 Spring 的核心。

例如我们调用 getBean,这就是 BeanFactory 定义的方法,通过它得到 Bean

不过一般我们所述的 BeanFactory 指的是它实现类,例如 DefaultListableBeanFactory

BeanFactory 本身只是一个接口。

FactoryBean 又是啥

从命名角度来看,我们可以得知它就是一个 Bean,而不是一个工厂。

那为什么名字如此奇怪,它其实是一个接口,并且有以下几个方法
image.png

如果一个对象实现了这接口,那它就成为一种特殊的 Bean,注册到 IOC 容器之后,如果调用 getBean 获取得到的其实是 FactoryBean#getObject() 方法返回的结果。

为什么要这样做?

假设你依赖一个第三方的类 A,而我们又不能修改第三方的类,并且这个对象创建比较复杂,那么你就可以创建一个 bean 来封装它:

public class AFactoryBean implements FactoryBean<A> {
    public A getObject() throws Exception {
        A a = new A();
        a.setXXX
        ....
        ...
        return A
    }
    ....省略一些实现
}

这样,我们 getBean("A") 会得到 AFactoryBean#getObject 的结果。

如果单纯只想要 AFactoryBean, 那么加个 “&” 即可,即 getBean("&A")

ObjectFactory 又是啥

从命名角度来看,它是一个工厂。

好吧,摊牌了,没啥特殊含义,就是一个工厂。

三级缓存的 map 里面存储的就是 ObjectFactory,用于延迟代理对象的创建。

其实就是封装对象的创建过程,像三级缓存里的 ObjectFactory 逻辑就是判断这个 Bean 是否有代理,如果有则返回代理对象,没有则返回原来传入的对象。
image.png

ApplicationContext 呢

ApplicationContext 对我们来说应该很熟悉,我们基本上都是基于 ApplicationContext 操作的。

它也实现了 BeanFactory 这个接口,也属于一个 BeanFactory,但是它内部还包装了一个 BeanFactory 的实现,这属于组合

而关于 Spring Beans 的一些操作都是委托内部的 BeanFactory 来操作的。
image.png

所以它有 BeanFactory 的所有功能,并且基于此它还扩展了很多其他功能:

  • AOP
  • 国际化
  • 资源管理
  • 事件
  • 注解
  • 等等

因此,我们开发直接用的肯定是 ApplicationContext 而不是 BeanFactory

Bean 一共有几种作用域

从官网,我们很容易可以得知,最新版本一共有六种作用域:
image.png

  • singleton:默认是单例,含义不用解释了吧,一个 IOC 容器内部仅此一个
  • prototype:原型,多实例
  • request:每个请求都会新建一个属于自己的 Bean 实例,这种作用域仅存在 Spring Web 应用中
  • session:一个 http session 中有一个 bean 的实例,这种作用域仅存在 Spring Web 应用中
  • application:整个 ServletContext 生命周期里,只有一个 bean,这种作用域仅存在 Spring Web 应用中
  • websocket:一个 WebSocket 生命周期内一个 bean 实例,这种作用域仅存在 Spring Web 应用中

别背网上那些多年前的答案了,以上才是最新的~

Spring 一共有几种注入方式

  • 构造器注入,Spring 倡导构造函数注入,因为构造器注入返回给客户端使用的时候一定是完整的。
  • setter 注入,可选的注入方式,好处是在有变更的情况下,可以重新注入。
  • 字段注入,就是平日我们用 @Autowired 标记字段
  • 方法注入,就是平日我们用 @Autowired 标记方法
  • 接口回调注入,就是实现 Spring 定义的一些内建接口,例如 BeanFactoryAware,会进行 BeanFactory 的注入

其实官网上关于注入就写了构造器和setter :
image.png

像字段注入其实官方是不推荐的使用的。
因为字段注入依赖注解,然后无法注入静态字段,无法控制成员变量注入顺序。

emmmm...先到这吧,Spring的东西要展开说的话篇幅太大了,后面有时间了再聊吧

0

评论区