Spring IOC 和 AOP

来源:2-1 如何从根源避免空指针

嘘哈

2020-09-22

一哥,你好,我在网上看了很多关于Spring IOC 和 AOP的博客但是始终感觉不是很明白Spring中IOC是怎样反转控制权的,还有循环依赖是如何解决的,面相切面的作用和实现,还有为什么要这么实现,希望一哥能解答

写回答

1回答

张勤一

2020-09-23

嘘哈你好:

    你这里一共是提出了三个问题:IOC、Spring 如何解决循环依赖,以及 AOP,我这里依次对你这里提出的三个问题进行解答。

-----------------------------------------------------------------------------------------------------------------

到底什么是 IOC 呢?

    IOC 英文全称为“Inverse of Control”即控制反转的意思,它并不是一种技术,而是一种设计思想与理念。便是最初由我们在程序中自己手动 new 一个对象改变成交给 Spring 容器来控制并管理。简单的说,IOC 你可以理解为一个 Map 集合,而 Map 存放的是各种对象,当我们需要用的时候就直接去获取这个 key。

  IOC 容器里面管理着各个对象之间的依赖关系并且完成对象的注入。这样做就会简化了我们在应用程序中的开发,从而将应用程序从复杂的依赖中解放出来,最终达到了程序解耦的目的。

  从上面的描述中我们知道 IOC 带来的改变不仅仅是在程序上的体现,它给我们带来的最大的改变是从思想上的改变,使得原本是主动的应用程序变成被动的了,被动的去等待 IOC 容器中生成对象并推给它想要的资源。通俗点说就是“你别过来找我,你等着就行,我会给你的”。

    那么,从之前的自己去控制对象的创建、生命周期管理,到由 Spring 去完全掌控这一切,当然也就叫做 “控制反转”了。

-----------------------------------------------------------------------------------------------------------------

关于 “Spring 如何解决循环依赖”的这个问题,我在之前已经回答过一个同学了,我这里直接粘贴过来我之前的回复吧。(可以参考:https://coding.imooc.com/learn/questiondetail/204911.html)

    我们先来解读下什么是循环依赖:简单地说,就是循环引用,两个或者多个 bean 相互之间的持有对方,形成一个闭环。如,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖,又或者是 A 依赖 B,B 依赖 C,C 又依赖 A。可以用一张经典的图(图片来自于网络)描述这种依赖关系。


    Spring 能够轻松的解决属性的循环依赖是用到了三级缓存,下面,我们来看看这三级缓存的定义:

/**一级缓存,用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
 
/**三级缓存 存放 bean 工厂对象,用于解决循环依赖*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
 
/**二级缓存 存放原始的 bean 对象(尚未填充属性),用于解决循环依赖*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    (1)一级缓存:singletonObjects,存放完全实例化属性赋值完成的Bean,直接可以使用

    (2)二级缓存:earlySingletonObjects,存放早期Bean的引用,尚未属性装配的Bean

    (3)三级缓存:singletonFactories,三级缓存,存放实例化完成的Bean工厂

    所以,其实你也可以看出来了,三级缓存是解决循环依赖的重中之重:因为 Spring 在 Bean 的创建过程中,首先就会从尝试从缓存中获取,如果没有获取到,就会走到 Bean 的创建过程。如果存在循环依赖,则会利用三级缓存的策略 逐步递进式的初始化各个 Bean,最终完成整体的初始化。但是,需要注意,对于构造函数注入方式造成的循环依赖,Spring 是解决不了的。

    那么,到这里,基本上也就说明了为什么需要三级缓存,以及三级缓存是怎么解决循环依赖的了。

-----------------------------------------------------------------------------------------------------------------

什么是 AOP 呢?

    要完全理解 AOP,你需要知道一个设计模式:代理模式。代理模式呢,它又分为静态代理和动态代理,其实思想都是一致的。其目的无非就是想要在“处理真实对象”的前后最一些前置、后置操作。(这有点 AOP 定义的意思了)下面,我们从理论从层面理解下代理模式:

    代理模式的定义:给目标对象提供一个代理对象,代理对象包含该目标对象,并控制对该目标对象的访问。

    代理模式的目的:

    (1)通过代理对象的隔离,可以在对目标对象访问前后增加额外的业务逻辑,实现功能增强;

    (2)通过代理对象访问目标对象,可以防止系统大量地直接对目标对象进行不正确地访问,出现不可预测的后果。

    下面,通过一张图来看看代理模式的思想:

    Spring 的 AOP 提供了很多功能,例如缓存、日志、事务等等。我这里以事务为例来说一说 AOP 的思想:

    Spring 的事务涉及到一个核心注解 @Transactional,相信很多人在项目中都用到过,加上这个注解之后,在执行方法时如果发生异常,该方法内所有的事务都回滚,否则全部提交生效,它内部是如何实现的呢?

    每个有关数据库的操作都要保证一个事务内的所有操作,要么全部执行成功,要么全部执行失败,传统的事务失败回滚和成功提交是使用 try...catch 代码块完成的:

SqlSession session = null;
try{
    session = getSqlSessionFactory().openSession(false);
    session.update("...", new Object());
    // 事务提交
    session.commit();
}catch(Exception e){
    // 事务回滚
    session.rollback();
    throw e;
}finally{
    // 关闭事务
    session.close();
}

    如果多个方法都需要写这一段逻辑非常冗余,所以 Spring 给我们封装了一个注解 @Transactional,使用它后,调用方法时会监视方法,如果方法上含有该注解,就会自动帮我们把数据库相关操作的代码包裹起来,最终形成类似于上面的一段代码原理。--> 这其实就是 AOP 的思想了。


    我是勤一,致力于将这门课程的问答区打造为 Java 知识体系知识库,Java 知识体系 BBS!共同建造、维护这门课程,我需要每一个你!


6
0

Java实操避坑指南 SpringBoot/MySQL/Redis错误详解

掌握业务开发中各种类型的坑,,Java web开发领域通用

466 学习 · 204 问题

查看课程