Spring 面试题
Spring 家族
- Spring Core:
- Spring MVC:工厂模式和代理模式,用于解耦应用组件
- Spring Boot:模板代码
- Spring Clound:微服务
Spring IOC
IOC 就是 Inversion of Control,控制反转。先了解依赖注入(Dependency Injection)。
在以前的开发中,上层建筑依赖于下层建筑。每个类都调用成员的构造函数,都是直接 new 出来的。
这时,如果我们想修改 Tire 类,支持动态设置 size,那么就要修改所有的类。
如果我们每次修改一个类,都要修改所有依赖它的类,可维护性就非常差。
依赖注入就可以解耦,把底层类作为参数传递给上层类,实现上层对下层的控制。
这种做法还有利于单元测试和协同开发。
而 IOC 就是通过 DI 实现的(DL 已经被抛弃,因为具有侵入性)。
依赖注入的方式:
- Setter
- Interface:实现特定的接口,供 IOC 容器注入所依赖类型的对象(废弃)
- Constructor:实现特定参数的构造函数
- Annotation:如 AutoWierd
- 依赖倒置原则是思想 - 控制反转是思路 - 依赖注入是具体的实现方法 - IOC 容器也是一种实现方法,统一管理 Bean 的生命周期
IOC 容器的优势
- 避免在各处使用 new 来创建类,并且可以并做到统一维护
- 在创建实例时,不需要了解其中的细节,就像是一个工厂,会根据依赖关系去创建对象,隐藏了细节。增加了可维护性,降低开发难度
Spring IOC 流程
- 从配置文件或者注解,读取配置信息,配置 Bean 注册表
- 根据 Bean 注册表,利用反射实例化 Bean,建立 Bean 之间的依赖关系
- 将 Bean 实例放到 Spring 容器中
Spring IOC 支持的功能
- 依赖注入
- 依赖检查
- 自动装配
- 支持集合
- 指定初始化和销毁方法
- 支持回调方法(需要实现某些接口)
BeanFactory 与 ApplicationContext 的比较
- BeanFactory 是 Spring 框架的基础设施,面向 Spring
- ApplicationContext 面向使用 Spring 框架的开发者
getBean 方法的代码逻辑
- 转换 beanName
- 从缓存中加载实例
- 实例化 Bean
- 检测 parentBeanFactory
- 初始化依赖的 Bean
- 创建 Bean
Spring Bean 的作用域
- singleton:Spring 的默认作用域,容器里拥有唯一的 Bean 实例,适合无状态的 Bean
- prototype:针对每个 getBean 请求,容器都会创建一个 Bean 实例,适合有状态的 Bean,不过频繁创建和销毁 Bean 是有开销的
- Web 容器里面,还有 3 种作用域
- request:会为每个 Http 请求创建一个 Bean 实例
- session:会为每个 session 创建一个 Bean 实例
- globalSession:会为每个全局 Http Session 创建一个 Bean 实例,该作用域仅对 PorletContext 有效
Spring AOP
关注点分离:不同的问题交给不同的部分去解决
- 通用化代码的实现(事务、缓存、日志)等,对应的就是切面
- 业务代码和切面代码分开后,架构将变得高内聚、低耦合
- 分开后,又需要合并到一起,切面最终需要被合并到业务中
AOP 的三种织入方式
- 编译时织入:在编译时,融合进来,生成完整功能的字节码,需要特定编译器,如 AspectJ
- 类加载时织入:在类加载时融合进来,需要特定编译器,如 AspectJ
- 运行时织入:Spring 采用的方式,通过动态代理的方式,实现简单。会有性能上的开销,好处是不需要特定的编译器和类加载器。
AOP 的实现有 JDKProxy(动态代理) 和 Cglib。
由 AopProxyFactory 根据 AdvicedSupport 对象的配置来决定
默认策略:如果目标类是接口,则使用 JDKProxy 来实现,否则使用后者
JDKProxy 要求被代理的类必须实现一个接口,核心是 InvocationHandler 接口和 Proxy 类,通过 Java 内部的反射机制实现。反射机制在生成类的过程中比较高效
Cglib:是一个代码生成库,可以在运行时动态地生成某个类的子类,以继承的方式动态生成目标类的代理(final 类无法使用 Cglib),借助 ASM 实现。ASM 在生成类之后的执行过程中比较高效
Spring 里代理模式的实现
- 真实实现类的逻辑包含在 getBean 方法里
- getBean 方法返回的实际上是代理类的实例
- 代理类的实例是 Spring 采用 JDK Proxy 或者 Cglib 动态生成的