吊打面试官系列:深入理解Spring的IOC容器

关注我的公众号:【编程朝花夕拾】,可获取首发内容。

01 引言

Spring提供Java一站式服务的生态圈,其中最重要是控制反转(IOC)和面向切面编程(AOP)这两个特性,更是在面试八股文中屡次被问到。AOP已经在上一期文章中,通过自定义注解做了介绍。

本次,我们将一起了解经典的面试题:什么是IOC容器。

02 基本概念

IOC(inversion of control)就是控制反转的意思。控制反转依然让人一头雾水,到底什么反转了?

在我们没有用到Spring生态的时候,我们自己需要控制对象的创建和管理。例如当我们需要使用一个对象的时候,我们自己通过new 关键字创建,然后调用其内部的方法。

这不有句调侃程序员的话:程序员不怕没有对象,没事就是new一个,new的对象太多了,导致现实的反噬,没有女朋友。哈哈......

IOC时代的到来,代替了我们对对象的管理。所有对象的创建和管理交给了Spring容器统一管理,使用的时候通过注入的方式直接获取就好了。这种对对象控制权由开发者转移到Spring,发生了反转。这就是控制反转。

03 控制反转案例对比

我们通过代码,对比IOC前后的变化。

3.1 非IOC管理

java 复制代码
public class UserService {
    public void test() {
        System.out.println("test 方法执行了...");
    }
}

// 方法调用
@Test
void contextLoads() {
    // 通过new关键字创建对象
    UserService userService = new UserService();
    userService.test();
}

对象哪里需要哪里new,我的对象我做主。

3.2 IOC容器管理

java 复制代码
@Component
public class UserService {

    public void test() {
        System.out.println("test 方法执行了...");
    }
}

// 方法调用
@Autowired
UserService userService;

@Test
void contextLoads() {
    userService.test();
}

其中@Component注解将UserService对象的创建和管理交给了Spring,调用的时候,通过@Autowired注解将对象注入进来,然后正常调用即可。

除了@Component注解可以控制反转,还有@Bean注解。还有一些复合注解@Controller@Service@Repository@RestController,里面都集成了@Component。但是使用的位置都不同。

3.3 对比

IOC管理的对象,使用更加灵活,不受框架的限制,可以用在任何地方。而IOC管理就要依赖Spring框架。

IOC管理的对象,使用的越多,创建的对象就越多,因此占用的资源也就会越多,如果循环创建对象,可能造成内存溢出。虽然可以通过单例的方式解决,但是需要为每一个对象,设置单例。这时候IOC容器的好处就来了。默认就是单例模式,容器只会创建一次对象。

对比非空参构造的对象,如果发生参数的修改,对于非IOC管理的对象来说似灾难,每一处用到的地方都要手动修改。而IOC管理的对象,只需要修改实例化的地方即可。

04 IOC实现原理

4.1 容器测试

java 复制代码
@Test
    void test() {
        ApplicationContext context = new AnnotationConfigApplicationContext("com.simonking.boot.aop");
        UserService contextBean = context.getBean(UserService.class);
        contextBean.test();
    }

ApplicationContext就是我们所说的Spring容器,他的主要实现有两个:

  • AnnotationConfigApplicationContext:解析注解
  • XmlWebApplicationContext:解析Xml

案例中采用注解的方式,扫描com.simonking.boot.aop,所有相关的注解@Component@Bean,并加载到容器中。

从容器中通过getBean()的方式获取容器中的Bean对象。然后进行业务操作。

4.2 源码脉络

我们以AnnotationConfigApplicationContext为例追踪。

上面的脉络图是执行的关键代码,简单总结一下总共有三步:

  • 通过AnnotationConfigApplicationContext的构造函数,读取扫描路径的所有.class文件。
  • .class通过ScannedGenericBeanDefinition读取BeanDefinition信息,并交给Spring容器。
  • Spring容器通过getBean的方式或者注入的方式,获取容器中的Bean对象。

4.3 源码追踪

AnnotationConfigApplicationContext构造函数:

java 复制代码
public AnnotationConfigApplicationContext(String... basePackages) {
    this();
    // 扫描包路径
    scan(basePackages);
    // 启动Spring容器
    refresh();
}

scan的主要代码块如图:

其中①获取扫描包下的所有.classBeanDefinition

并通过ConfigurationClassUtils.isConfigurationCandidate方法筛选目标候选的BeanDefinition.

其中candidateIndicators包括:

再通过②③步骤获取BeanName并注册到org.springframework.beans.factory.support.BeanDefinitionRegistry中,从此完成scan操作。

refresh()Spring的核心,也是Bean初始化流程,而Bean的所有信息,都保留在BeanDefinitionRegistry里面。

05 小结

IOC的思想很简单,但是里面的处理逻辑非常复杂,以兼容各种情况,这就是框架的魅力。里面的refresh()涉及的知识点太多,无法详细说明。如果想了解其详细流程,可以在公众号输入关键字【Spring源码】,获取PDF流程图。下面是一张鸟瞰图:

相关推荐
硅的褶皱1 小时前
对比分析LinkedBlockingQueue和SynchronousQueue
java·并发编程
MoFe11 小时前
【.net core】天地图坐标转换为高德地图坐标(WGS84 坐标转 GCJ02 坐标)
java·前端·.netcore
季鸢1 小时前
Java设计模式之观察者模式详解
java·观察者模式·设计模式
互联网搬砖老肖1 小时前
Web 架构相关文章目录(持续更新中)
架构
计算机毕设定制辅导-无忧学长1 小时前
Kafka 核心架构与消息模型深度解析(二)
架构·kafka·linq
计算机毕设定制辅导-无忧学长2 小时前
Kafka 核心架构与消息模型深度解析(一)
分布式·架构·kafka
Fanxt_Ja2 小时前
【JVM】三色标记法原理
java·开发语言·jvm·算法
Mr Aokey2 小时前
Spring MVC参数绑定终极手册:单&多参/对象/集合/JSON/文件上传精讲
java·后端·spring
小马爱记录3 小时前
sentinel规则持久化
java·spring cloud·sentinel
地藏Kelvin3 小时前
Spring Ai 从Demo到搭建套壳项目(二)实现deepseek+MCP client让高德生成昆明游玩4天攻略
人工智能·spring boot·后端