1. 简述 IoC 思想
Spring 框架倡导基于 POJO(Plain Old Java Object,简单 Java 对象) 的轻量级开发理念,立足于 POJO,为 POJO 提供服务。

Spring Core 是整个 Spring 框架的基础,在该模块中,Spring 为我们提供了一个 IoC 容器的实现。IoC 是 Spring 最核心的思想,即 Inversion of Control(控制反转)。控制反转的意思就是控制权发生反转,进一步说,获得依赖对象的过程被反转了。
IoC 是一种思想,Spring 只是实现了这种思想,抛开 Spring 不谈,这种思想其实也是非常常见的:
java
// 造车:
// 车依赖车架
public class Car {
private Framework framework;
public void run() {
System.out.println("car run...");
}
}
============================================
// 车架依赖底盘
public class Framework {
private Bottom bottom;
}
============================================
// 底盘依赖轮子
public class Bottom {
private Tire tire;
}
============================================
// 轮子需要客户提供尺寸
public class Tire {
private int size;
}
如果我们使用非 IoC 的思想去写构造方法,那会是这样:
java
public class Car {
private Framework framework;
public Car(int size) {
this.framework = new Framework(size);
}
public void run() {
System.out.println("car run...");
}
}
============================================
public class Framework {
private Bottom bottom;
public Framework(int size) {
this.bottom = new Bottom(size);
}
}
============================================
public class Bottom {
private Tire tire;
public Bottom(int size) {
this.tire = new Tire(size);
}
}
============================================
public class Tire {
private int size;
public Tire(int size) {
this.size = size;
}
}
对于上面的代码,对象之间的依赖关系是:车 --> 车架 --> 底盘 --> 轮子,最下层的对象反而是受依赖程度最高的,因此实际上没人会这样写构造函数,此时相当于上级类需要在自己的代码中创建下级类,不同功能代码之间的耦合度是很高的,维护起来极其困难。如果最底层代码需要做出改变,比如增加某个属性,那么上层代码都需要随之改动。
而使用 IoC 的思想来写构造函数,就会是这样:
java
public class Car {
private Framework framework;
public Car(Framework framework) {
this.framework = framework;
}
public void run() {
System.out.println("car run...");
}
}
============================================
public class Framework {
private Bottom bottom;
public Framework(Bottom bottom) {
this.bottom = bottom;
}
}
============================================
public class Bottom {
private Tire tire;
public Bottom(Tire tire) {
this.tire = tire;
}
}
============================================
public class Tire {
private int size;
public Tire(int size) {
this.size = size;
}
}
现在和之前有了很大的不同,上级类不再自己创建下级类的实例,而是变成下级类将自己的实例传递给上级类,此时无论下级类内部发生任何变化,上级类都不需要关心了,从而实现了程序的解耦。
此时当我们想要调用 run 方法,如下代码所示,我们会发现,创建实例的顺序反转了。原先是,我们创建 Car 的实例,由 Car 创建 Framework 的实例,由 Framework 创建 Bottom 的实例,由 Bottom 创建 Tire 的实例。现在是,我们先创建 Tire 的实例,将 Tire 的实例注入 Bottom 才能接着创建 Bottom 的实例,有了 Bottom 的实例,才能将 Bottom 的实例注入 Framework 以创建 Framework 的实例...... 这就是 IoC 控制反转思想的体现。
java
public class Main {
public static void main(String[] args) {
Tire tire = new Tire(1);
Bottom bottom = new Bottom(tire);
Framework framework = new Framework(bottom);
Car car = new Car(framework);
car.run();
}
}
再来看上面的代码,其实我们的目的就是调用 Car 的 run 方法,但是为了实现 IoC,我们要手动 new 出很多对象。如果一个大型项目中有几百个对象,而我们都需要手动绑定它们之间的关系,那将是非常难的。所以,SpringIoC 容器就为我们做了这部分的工作,它为我们管理着所有的业务对象,通过依赖注入(DI)的方式,实现对象之间的解耦,因此它也被称为 IoC Service Provider。
IoC Service Provider 最重要的职责就是两个:一个是创建对象,另一个是管理好对象之间的依赖关系。

2. Bean 的储存
SpringIoC 是一个容器,受它管理的 Java 对象就称之为 Bean。容器负责管理 Bean 的生命周期,我们需要做的就是告诉容器要存些什么。
在前面的文章中已经说明过什么是软件的三层架构,SpringIoC 容器对三层架构提供了支持:下面的注解都可以用于将对象交给 Spring 管理,但也有区别。
@Controller/@RestController:只有它们可以用于前后端交互,必须用于控制层,不可替换。现在更推荐使用 @RestController 注解,因为 @Controller 默认返回的是视图,而如今前后端分离,后端不需要返回视图,而是返回视图所需的数据。@RestController 用于返回纯数据。
@Service:建议用于业务逻辑层。
@Repository:建议用于数据访问层。
@Configuration:建议用于项目的配置信息。
@Component:上面几个注解的元注解,上面的注解是针对不同场景的细分。Component 相当于杯子,水杯和牙刷杯都可以称为杯子,但是它们有各自的应用场景,或者说更加规范的使用方式。根据规范添加注解可以提高我们的代码可读性,让别人快速了解该 Bean 的用处。
3. 访问容器
BeanFactory:SpringIoC 最顶层的接口,提供基础的访问容器的能力。默认采用延迟初始化的策略,即当客户端需要访问容器中受管对象的时候,才对该对象进行初始化及依赖注入。因此,容器初期启动速度较快,对于资源有限并且功能要求不严格的场景较为适用。
ApplicationContext:BeanFactory 的子类,是更加高级的容器实现,提供企业级功能。除了拥有 BeanFactory 的全部支持,还提供了事件发布、国际化信息支持等高级特性。ApplicationContext 所管理的对象,在该类型容器启动之后,默认全部初始化并绑定完成。因此它比 BeanFactory 要求更多的系统资源,在系统资源充足并且要求更多功能的场景更为合适。
启动 SpringIoC:使用 SpringBoot 提供的 @SpringBootApplication 注解标记 Spring 程序启动类。SpringBoot 提供 SpringApplication 类为我们简化传统 Spring 项目的繁多配置,一行代码启动,自动处理上下文和环境。
java
@SpringBootApplication
public class SpringIoCDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringIoCDemoApplication.class, args);
}
}
使用 ApplicationContext 类接收。
java
ApplicationContext context = SpringApplication.run(SpringIoCDemoApplication.class, args);
通过类名获取对象:
java
UserController userController = context.getBean(UserController.class);
通过 Bean 名获取对象,Spring 自动使用小驼峰为每个 Bean 生成唯一的名称:
java
UserController userController = (UserController)context.getBean("userController");