也来聊聊Spring中的IOC和AOP
作为一名Java开发的程序猿,Spring可以说是每天都要打交道的东西,从最一开始基于xml配置文件的Spring再到Spring Boot或是Spring Cloud,可以明显感觉到的就是业务的搭建变得日益简单,配置由复杂变得越来越轻量,甚至只需要在网页上点一点就能初始化一个可以运行的Spring项目。网上讨论Spring核心概念IOC和AOP的文章也有很多,今天我也想来聊聊在我看来Spring IOC和AOP的一些特点和使用时需要注意的地方。
IOC
IOC是构成Spring常用模块中最核心的模块BeanFactory使用到的最重要的技术,通过控制反转,应用程序的配置和依赖规范可以很好的与应用程序代码分离开。我们不再需要从一个类引用另一个类时显式地初始化对方,而是可以单纯的通过某个配置文件就能描述组成Spring应用的各个类之间的依赖关系。通过Java语言的反射功能就可以实例化各个Bean并建立他们之间的依赖关系。IOC除了这些功能以外,还提供了Bean实例缓存、生命周期管理、Bean实例代理发布、事件发布、资源装载等等高级功能。
说起IOC,最重要的就是BeanFactory,这是面向Spring框架基础本身的一个类,而我们的开发者更多的使用ApplicationContext。
Bean的类型有两种,一种是单例Singleton,一种是prototype,分别会在使用的过程中共享同一个Bean或者每次分别创建不同的Bean。在Web场景下Bean还有request、session和global session三种额外的作用域。
Spring Bean的生命周期大概是:
- 实例化
- IOC依赖注入
- setBeanName
- BeanFactoryAware
- ApplicationContextAware
- postProcessBeforeInitialization
- init-method
- postProcessAfterInitialization
- Destory过期自动清理
- Destory-method
依赖注入的四种方式:
- 构造器注入
- setter注入
- 静态工厂注入
- 实例工厂注入
AOP
核心概念:
切面:对横切关注点的抽象
横切关注点:对哪些方法进行拦截,拦截后进行什么样的处理
连接点:被拦截到的点
切入点:对拦截点进行拦截的定义
增强:进行拦截之后要进行的处理,分别有-前置,后置,异常,返回值,环绕
目标对象:要进行代理的目标
织入:将切面应用于目标对象并创建代理对象的过程
引入:不改变代码的前提下运行期修改类,动态添加方法或者字段
两种代理方式:
JDK动态接口代理:
要求横切逻辑必须是接口定义的,通过反射调用目标类的代码,动态的将横切逻辑和业务逻辑进行编织
CGLib动态代理:
高性能的代码生成类库,不受被代理对象是否实现了接口的限制,可以默认关闭JDK动态代理而完全使用CBLib动态代理
循环依赖问题
- Spring 为了解决单例的循环依赖问题,使用了 三级缓存 ,递归调用时发现 Bean 还在创建中即为循环依赖
- 单例模式的 Bean 保存在如下的数据结构中:
/** 一级缓存:用于存放完全初始化好的 bean **/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** 二级缓存:存放原始的 bean 对象(尚未填充属性),用于解决循环依赖 */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
/** 三级级缓存:存放 bean 工厂对象,用于解决循环依赖 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/**
bean 的获取过程:先从一级获取,失败再从二级、三级里面获取
创建中状态:是指对象已经 new 出来了但是所有的属性均为 null 等待被 init
*/
检测循环依赖的过程如下:
-
A 创建过程中需要 B,于是 A 将自己放到三级缓里面 ,去实例化 B
-
B 实例化的时候发现需要 A,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了!
-
- 然后把三级缓存里面的这个 A 放到二级缓存里面,并删除三级缓存里面的 A
- B 顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态)
-
然后回来接着创建 A,此时 B 已经创建结束,直接从一级缓存里面拿到 B ,然后完成创建,并将自己放到一级缓存里面
-
如此一来便解决了循环依赖的问题
- 点赞
- 收藏
- 关注作者
评论(0)