在现代 Java 开发中,Spring 框架几乎无处不在,特别是其核心的 IOC(Inversion of Control) 容器,几乎所有Spring的功能都与它紧密相关。
一、什么是IOC
IOC,全称为 Inversion of Control(控制反转),顾名思义,它是将原本由程序员控制的对象创建、对象依赖关系的管理等责任反转给框架来做,程序员只需要编写业务逻辑,框架会处理对象的创建和管理。
在java当中一个类想要使用另一个类的方法,就必须在这个类当中创建这个类的对象,那么可能会出现如下情况:比如A类当中创建着B对象,B类当中有C对象,C类当中有A对象,这个如果一个类出了问题,那么可能会导致这个框架出现问题。
Spring 将创建对象的权利给了IOC,在IOC当中创建了ABC三个对象吗,那么我们我们其他的类只需要调用集合,大大的解决了程序耦合性的问题。
通过 IOC,Spring 容器负责创建和管理对象的生命周期,同时还负责将需要的依赖注入到这些对象中。这就避免了传统编程方式中,程序员需要手动管理对象实例化和依赖关系的问题。
举个通俗的例子:
- 传统方式:你去餐厅吃饭,自己点菜、自己烹饪、自己上菜、自己清理桌面。
- IOC方式:你去餐厅吃饭,餐厅的服务员负责点菜、烹饪、上菜,甚至清理桌面,你只需要享受美食。
这样看,IOC 就像一个得力的服务员,帮你解决了很多繁琐的事情,而你只需要专注于业务逻辑即可。
二、IOC的底层原理
在 Spring 中,IOC 主要由 BeanFactory 和 ApplicationContext 两个核心容器实现。BeanFactory 是 Spring 的基本容器,而 ApplicationContext 是 BeanFactory 的扩展,具备更多的功能。
IOC的实现主要依赖于:
- dom4j 解析 xml 文档
- 工厂模式
- 采用反射设计模式创建对象
1、工厂模式

在当前情况之下A类想要调用B类就必须自己在自己的内部新建B类的对象,这样的耦合度太高,如何降低耦合度的呢?
java
public class A {
public void run(){
B b = AFactory.getB();
b.print();
}
}
public class B {
public void print(){
System.out.println("你好");
}
}
public class BFactory {
public static B getB(){
return new B();
}
}
创建一个工厂类BFactory,这样就能够使得A和B的耦合度降低到最小值
2、容器与 Bean 的管理
Spring 的 IOC 容器负责创建、管理和销毁应用中的所有 bean。每个 bean 都是由 Spring 容器根据配置创建的 Java 对象,并通过依赖注入(DI)来管理它们之间的依赖关系。
Bean管理指的是:
- 创建对象
- 注入操作
这两个操作
3、依赖注入(DI)
依赖注入是 IOC 的关键实现方式,Spring 提供了三种主要的 DI 方式:
- 构造器注入:通过构造函数传递依赖。
- Setter 注入:通过 setter 方法传递依赖。
- 接口注入:通过接口传递依赖(这种方式较少使用,Spring 没有默认支持)。
4、 Bean 生命周期
Spring 容器中的 Bean 在创建时,会经历以下生命周期:
- 实例化:根据配置实例化对象。
- 填充属性:根据配置文件将属性注入到 Bean 中。
- 初始化:初始化 Bean,执行自定义的初始化方法。
- 销毁:当容器销毁时,执行销毁方法。
三、基于XML配置文件实现Bean管理和依赖注入
1、Maven项目信息配置
配置Maven项目基本信息,构建一个 Spring IOC 示例项目,并集成了日志和单元测试支持。
XML
<?xml version="1.0" encoding="UTF-8"?>
<!-- 声明 XML 版本和编码格式,UTF-8 适用于多语言支持 -->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- 定义项目的根元素 <project>,指定 POM 的 XML 命名空间
xsi:schemaLocation 指定了 XML Schema 文件的位置,用于 POM 语法验证 -->
<modelVersion>4.0.0</modelVersion>
<!-- 指定 POM 的模型版本,当前 Maven 版本统一使用 4.0.0 -->
<groupId>com.goose</groupId>
<!-- 组 ID,通常采用公司或组织的反向域名,标识该项目属于哪个组织或团队 -->
<artifactId>Spring_IOCDemo</artifactId>
<!-- 工件 ID,代表当前项目的名称,最终生成的 JAR 或 WAR 文件会使用这个名称 -->
<version>1.0-SNAPSHOT</version>
<!-- 版本号,1.0-SNAPSHOT 表示开发中的快照版本,非正式发布 -->
<properties>
<maven.compiler.source>8</maven.compiler.source>
<!-- 指定 Java 源代码版本为 8 -->
<maven.compiler.target>8</maven.compiler.target>
<!-- 指定编译后的字节码版本为 Java 8 -->
</properties>
<dependencies>
<!-- 依赖管理,列出项目所需的外部库 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- Spring Context 依赖,提供 IoC 容器和 AOP 等核心功能,版本 5.0.2.RELEASE -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- commons-logging 依赖,Spring 使用它作为日志抽象层,可适配多种日志框架 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<!-- log4j 依赖,具体的日志实现,用于输出日志信息 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- JUnit 依赖,版本 4.12,scope 为 test,表示该依赖仅用于测试环境 -->
</dependencies>
</project>
<!-- 结束 <project> 标签,完整定义 Maven 项目 -->
2、创建Spring依赖
用于定义和管理不同的 bean(对象)及其依赖关系,使用 XML 配置方式 来定义应用程序的对象,Spring 容器会根据这个配置文件来创建和管理对象的生命周期,并注入它们之间的依赖。
XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
3、创建对象
Spring.xml中定义一个名为 car 的 Bean,并指定它的实现类为 com.goose.entity.Car。
XML
id :类的唯一标识符
class : 类的全路径名
<bean id="car" class="com.goose.entity.Car"/>
创建实体类 Car,方便观察,设计 run 方法来测试
java
public class Car{
public void run(){
System.out.println("汽车轱辘骨碌碌的转.........");
}
}
创建测试类
java
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CarTest {
//传统方式创建对象
@Test
public void test(){
// 创建对象
Car car = new Car();
// 调用方法
car.run();
}
// Spring 创建对象
@Test
public void test_Spring(){
ApplicationContext ac = new ClassPathXmlApplicationContext("Spring.xml");
Car car = (Car) ac.getBean("car");
// 方法调用
car.run();
}
}
4、注入属性
1.普通属性
Spring.xml中定义一个名为 car 的 Bean,并指定它的实现类为 com.goose.entity.Car,并指定属性和对应值。ref 用于表示该属性的值是引用另一个已经定义的 Bean。
ref="engine":这表示 car Bean 的 engine 属性将会通过引用另一个已定义的 Bean 来注入,那个 Bean 的 ID 为 engine。Spring 会从容器中查找一个 ID 为 engine 的 Bean,并将其注入到 car Bean 的 engine 属性中。
换句话说,ref="engine" 告诉 Spring 容器,car 这个对象的 engine 属性应该被赋值为 ID 为 engine 的 Bean,而不是一个普通的值。
XML
<bean id="car" class="com.goose.entity.Car">
<property name="brand" value="Geely"/>
<property name="IDs" value="18952"/>
<property name="engine" ref="engine"/>
</bean>
<bean id="engine" class="com.goose.entity.Engine">
<constructor-arg name="cylinder" value="V8"/>
<constructor-arg name="displayment" value="6.0T"/>
<constructor-arg name="ratio" value="49%"/>
</bean>
创建实体类 Car 和 Engine ,其中 Engine 中实现了使用带参构造方法进行值的注入,在 xml 中需要使用 constructor-arg 关键字。
java
public class Car implements CarInterface {
private Integer IDs;
private String Brand;
private Engine engine;
@Override
public String toString() {
return "Car{" +
"IDs=" + IDs +
", Brand='" + Brand + '\'' +
", engine=" + engine +
'}';
}
public Integer getIDs() {
return IDs;
}
public void setIDs(Integer IDs) {
this.IDs = IDs;
}
public String getBrand() {
return Brand;
}
public void setBrand(String brand) {
Brand = brand;
}
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public void run(){
System.out.println("汽车轱辘骨碌碌的转.........");
}
}
java
public class Engine {
private String cylinder;
private String displayment;
private String ratio;
public Engine(String cylinder, String displayment, String ratio) {
this.cylinder = cylinder;
this.displayment = displayment;
this.ratio = ratio;
}
@Override
public String toString() {
return "Engine{" +
"cylinder='" + cylinder + '\'' +
", displayment='" + displayment + '\'' +
", ratio='" + ratio + '\'' +
'}';
}
public String getCylinder() {
return cylinder;
}
public void setCylinder(String cylinder) {
this.cylinder = cylinder;
}
public String getDisplayment() {
return displayment;
}
public void setDisplayment(String displayment) {
this.displayment = displayment;
}
public String getRatio() {
return ratio;
}
public void setRatio(String ratio) {
this.ratio = ratio;
}
}
测试类
java
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CarTest {
@Test
public void test_Spring(){
ApplicationContext ac = new ClassPathXmlApplicationContext("Spring.xml");
Car car = (Car) ac.getBean("car");
// 方法调用
car.run();
System.out.println(car.toString());
}
}

2.集合属性
Spring.xml中定义一个名为 bot 的 Bean,并指定它的实现类为 com.goose.entity.Bot。
XML
<bean id="bot" class="com.goose.entity.Bot">
<property name="list">
<list>
<value>小新</value>
<value>小爱</value>
</list>
</property>
<property name="map">
<map>
<entry key="1" value="广志"/>
<entry key="2" value="美伢"/>
</map>
</property>
<property name="strs">
<array>
<value>正男</value>
<value>妮妮</value>
</array>
</property>
</bean>
创建实体类
java
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class Bot {
private String [] strs;
private List<String> list;
private Map<String,String> map;
@Override
public String toString() {
return "Bot{" +
"strs=" + Arrays.toString(strs) +
", list=" + list +
", map=" + map +
'}';
}
public String[] getStrs() {
return strs;
}
public void setStrs(String[] strs) {
this.strs = strs;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
}
测试类
java
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CarTest {
@Test
public void test_Spring(){
ApplicationContext ac = new ClassPathXmlApplicationContext("Spring.xml");
Bot bot = (Bot) ac.getBean("bot");
// 集合等普通变量设值
System.out.println(bot.toString());
}
}
在构造器中注入
XML
<bean name="bot2" class="com.goose.entity.Bot_2">
<constructor-arg name="strs">
<array>
<value>正男</value>
<value>妮妮</value>
</array>
</constructor-arg>
<constructor-arg name="list">
<list>
<value>正男</value>
<value>妮妮</value>
</list>
</constructor-arg>
<constructor-arg name="map">
<map>
<entry key="1" value="广志"/>
<entry key="2" value="美伢"/>
</map>
</constructor-arg>
</bean>
java
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class Bot_2 {
private String [] strs;
private List<String> list;
private Map<String,String> map;
public Bot_2(String[] strs, List<String> list, Map<String, String> map) {
this.strs = strs;
this.list = list;
this.map = map;
}
@Override
public String toString() {
return "Bot{" +
"strs=" + Arrays.toString(strs) +
", list=" + list +
", map=" + map +
'}';
}
public String[] getStrs() {
return strs;
}
public void setStrs(String[] strs) {
this.strs = strs;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
}
java
Bot_2 bot_2 = (Bot_2) ac.getBean("bot2");
// 集合等构造器实现注入
System.out.println(bot_2.toString());
四、基于注解实现Bean管理和注入
1、什么是注解
①:注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值...)
②:使用注解,注解作用在类上面,方法上面,属性上边
③:使用注解的目的:简化XML配置
2、Spring针对Bean管理创建对象提供的注解
@Component 普通的类
@Controller 表现层
@Service 业务层
@Repository 持久层
*上边四个功能一样,都可以用来创建bean实例
3、用注解创建对象
1.编写接口和实现类
java
public interface CarInterface {
public void hello();
}
java
package com.goose.entity;
import com.goose.config.CarInterface;
import org.springframework.stereotype.Component;
/* <bean id="us" class="UserServiceImpl"/> */
/**
* 组件,作用:把当前类使用IOC容器进行管理,如果没有指定名称,默认使用类名,首字母是小写。
* userServiceImpl。或者自己指定名称
**/
@Component(value = "cars")
public class Car implements CarInterface {
@Override
public void hello() {
System.out.println("俺是注解创建对象测试......");
}
}
2.编写配置文件,开启注解扫描
XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.goose"/>
</beans>
3.测试类
java
@Test
public void test_Zhujie(){
ApplicationContext ac = new ClassPathXmlApplicationContext("Spring.xml");
Car car = (Car) ac.getBean("cars");
car.hello();
}
4、用注解注入属性
@Value 用于注入普通类型(String,int,double等类型)
@Autowired 默认按类型进行自动装配(引用类型)
@Qualifier 不能单独使用必须和@Autowired一起使用,强制使用名称注入
@Resource Java提供的注解,也被支持。使用name属性,按名称注入
1.编写实现类
java
package com.goose.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value = "poss")
public class Possession {
@Value("刘海柱")
private String name;
@Autowired
private Car car;
@Override
public String toString() {
return "Possession{" +
"name='" + name + '\'' +
", car=" + car +
'}';
}
}
java
package com.goose.entity;
import com.goose.config.CarInterface;
import org.springframework.stereotype.Component;
@Component(value = "cars")
public class Car implements CarInterface {
private Integer IDs;
private String Brand;
private Engine engine;
@Override
public String toString() {
return "Car{" +
"IDs=" + IDs +
", Brand='" + Brand + '\'' +
", engine=" + engine +
'}';
}
public Integer getIDs() {
return IDs;
}
public void setIDs(Integer IDs) {
this.IDs = IDs;
}
public String getBrand() {
return Brand;
}
public void setBrand(String brand) {
Brand = brand;
}
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public void run(){
System.out.println("汽车轱辘骨碌碌的转.........");
}
@Override
public void hello() {
System.out.println("俺是注解创建对象测试......");
}
}
2.测试类
java
@Test
public void test_Zhuru(){
ApplicationContext ac = new ClassPathXmlApplicationContext("Spring.xml");
Possession poss = (Possession) ac.getBean("poss");
System.out.println(poss.toString());
}
五、不同方式的对比
|------|--------------------|---------------------|
| 特性 | 基于 XML 配置 | 基于注解配置 |
| 配置方式 | 使用 XML 文件进行配置 | 使用注解来标记类和依赖关系 |
| 易用性 | 配置较繁琐,尤其是 Bean 多时 | 配置简单,类和依赖关系直接通过注解指定 |
| 灵活性 | 灵活,适用于复杂配置 | 配置简洁,适用于大多数应用 |
| 可维护性 | XML 配置文件容易维护,但文件较大 | 代码更加简洁,方便修改 |