Spring
Spring
什么是 spring?
- Spring 在创立之初是一个轻量级的 java 开源开发框架(轻量相对于J2EE EJB而言,其实 spring 也是比较大了),其提出是为了解决企业应用开发的复杂性而创建的,spring 可以更加快速、简单的构建应用,在目前是最受欢迎的 java 框架。
- 其设计理念在于分层架构思想,分层架构使得开发者可以自由选择要使用的组件。
- Spring 的核心优势在于其可以无缝集成主流开发框架,只需通过配置和简单的对象注入。
Spring 的两个核心特性:IoC 和 AOP(控制反转和面向切面编程)
IoC
什么是 IoC?
IoC 指的是控制反转,有些时候可以理解和依赖注入一个关系, IOC 是一种设计思想,将原本程序手中控制对象创建的权力,交给框架来管理;IOC 容器是实现 IOC 的载体,实际上底层就是一个 Map, 其中存放着各种对象。
- 容器概念:IOC 容器中存放着各种对象。在项目启动的时候,根据配置文件读取 beans, 然后通过反射放到容器里面,此时容器中就有各种对象了,后面会在需要的时候进行依赖注入。
- 控制反转:没有引入 IOC 容器的时候,对象的创建存在依赖;引入以后,对象之间失去了联系,而是在需要使用某对象的时候,由 IOC 主动创建并注入。
bean 的注册
// 返回 IOC 容器,基于 XML配置,传入配置文件的位置
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("xxx.xml");
User user = (User) applicationContext.getBean("user");
ApplicationContext 和 BeanFactory
通过上面的代码,问问题:ApplicationContext 和 BeanFactory 的区别是什么?
回答:
- ApplicationContext 是 BeanFactory 的子接口;
- BeanFactory 通过延迟加载的方式来注入 bean, ApplicationContext 是在容器启动时就一次性创建了所有的 bean;
- ApplicationContext 提供了更完整的功能,如统一的资源文件访问方式、支持国际化、同时加载多个配置文件等。
bean 的生命周期
@todo
Spring bean 的作用域
- singleton 单例
- prototype
- request
- session
- global-session(Spring 5 以后废弃)
Spring singleton 是线程安全吗?
不是。bean 可以分为有状态对象 stateful bean 和无状态对象 stateless bean, 有状态对象保存有实例变量,有数据,不是线程安全的;
对于单例 bean, 存在多个线程共享这个 bean 的实例,如果是一个无状态 bean, 那么各个线程不会对这个 bean 进行查询以外的操作,这时候是线程安全的。如果是有状态 bean, 这时候存在资源的竞争。
如何避免?
- 可以通过
ThreadLocal
来解决线程安全的问题,因为ThreadLocal
为每个线程保存线程私有的数据。 - 定义无状态 bean(实际生产中较难)。
AOP
什么是 AOP?
AOP 是面向切面编程的意思。其思想在于把与业务无关的,各个业务模块可以公用的模块(如日志、事务、异步调用等)封装起来,减少重复代码、降低耦合,其相关的设计模式为代理模式。
Spring AOP 基于动态代理实现。对于已经实现的接口,JDK 可以动态代理去创建代理对象;如果该对象没有实现接口,那么会使用 CGlib 动态代理生成一个代理的子类来作为代理。
@EnableAspectJAutoProxy
@Enable
模块:@Enable
模块驱动注解 EnableAspectJAutoProxy
用于开启 AspectJ 自动代理。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
AspectJAutoProxyRegistrar
为一个 AspectJ
自动代理注册器,通过 @import
注解导入。
如何解决循环依赖?
循环依赖与 IoC
类与类之间的依赖关系形成了闭环,就会导致循环依赖问题的产生。
通过 Spring IOC 流程的源码分析循环依赖问题:
- 先从缓存中获取,获取不到则继续往下走
- 实例化 Class A
- 依赖注入 Class A 对象的成员变量(setter) -- 注意到此时辉产生循环依赖
- 初始化 Class A(初始化方法)
- 将 Class A 的引用放入一级缓存
循环依赖的三种情况:
- 通过构造方法进行依赖注入的时候产生的循环依赖
- 通过 setter 方法进行依赖注入的时候产生的循环依赖(多例模式下)
- 通过 setter 方法进行依赖注入的时候产生的循环依赖(单例模式下)-- Spring 解决了这种场景下循环依赖的问题
构造方法进行依赖主语的时候,new 对象的时候就阻塞住了。而多例的循环依赖每次 getBean()
时,都会产生一个新的 bean, 最终导致 OOM 发生。
Spring 三大缓存
提示
Spring 解决循环依赖主要是通过两个缓存。总的来说,Spring 有三大缓存:一级缓存 singletonObjects, 二级缓存 earlySingletonObjects 和三级缓存 singletonFactories.
一级缓存 singletonObjects
- 对容器外提供单例 bean 的存储功能
- 非创建中的 bean 才会被存储在该缓存
二级缓存 earlySingletonObjects
主要是存储原生的早期 bean.
- 用于存储单例模式下创建的 bean 实例(该 bean 还在创建中)
- 该缓存对内使用,Spring 内部的框架裸机使用该缓存
三级缓存 singletonFactories
主要是存储代理的 bean.