Spring IOC 入门

一、IOC

IOC的全称是 Inversion of Control,中文翻译为 "控制反转",它是一种设计思想,核心是颠覆了传统的对象创建和依赖管理方式。

1.控制反转

"控制"在这里指的是对象的创建权、依赖的管理控制权。

传统方式(无IOC):

开发者在程序中直接 new 对象,手动管理所有依赖。比如你需要一个UserService,它依赖UserDao,你要自己创建:

java 复制代码
// 传统方式:开发者手动控制对象创建和依赖
public class UserService {
    // 手动创建依赖对象
    private UserDao userDao = new UserDaoImpl();
    
    public void addUser() {
        userDao.add();
    }
}

// 使用时还要手动创建UserService
public class Main {
    public static void main(String[] args) {
        UserService userService = new UserService();
        userService.addUser();
    }
}

IOC方式(控制反转):

把对象的创建、依赖注入、生命周期管理等"控制权"反转给容器(比如Spring容器),开发者只需要定义"需要什么",而不用管"怎么创建"。

java 复制代码
// IOC方式:开发者只声明依赖,容器负责创建和注入
public class UserService {
    // 声明依赖,不手动创建
    private UserDao userDao;
    
    // 提供构造器/setter,让容器注入依赖
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
    
    public void addUser() {
        userDao.add();
    }
}

2.DI

DI(Dependency Injection,依赖注入):IOC的实现方式,容器将依赖关系注入到对象中。

核心含义:**"注入"**是指由外部实体(IOC容器)在创建对象时,将其所依赖的对象通过构造函数、Setter方法或接口等方式"传递"(注入)给它。

DI是实现IOC思想的具体技术手段。它明确了"反转"的具体方式:通过"注入"来提供依赖。

3.IOC容器(实现IOC/DI的框架组件)

IOC容器(在 Spring 中就是 ApplicationContext)是具体干活的角色。它的核心工作是:

  1. 创建和管理对象
  2. 管理对象的生命周期
  3. 自动注入依赖

4.IOC的优点

  1. 解耦:类与类之剑不再直接依赖
  2. 简化开发:无需手动管理对象的创建和依赖
  3. 便于测试:可以轻松替换依赖为模拟对象
  4. 统一管理:容器统一管理对象的生命周期

二、Spring IOC

Spring IOC 是 "IOC 思想" 的Java落地实现,Spring 框架把 IOC 思想变成了可以直接使用的工具(Spring IOC 容器)。

1.配置方式

1.1.XML配置(传统方式)

xml 复制代码
<!-- applicationContext.xml -->
<beans>
    <!-- 定义Bean -->
    <bean id="userService" class="com.example.UserServiceImpl">
        <!-- 属性注入 -->
        <property name="userDao" ref="userDao"/>
    </bean>
    
    <bean id="userDao" class="com.example.UserDaoImpl"/>
</beans>

1.2.注解配置(推荐)

java 复制代码
@Component  // 标识为Spring组件
public class UserServiceImpl implements UserService {
    
    @Autowired  // 自动注入依赖
    private UserDao userDao;

    public void addUser() {
        userDao.add();
    }
}

@Repository  // 数据访问层组件
public class UserDaoImpl implements UserDao {
    // ...
}

1.3.Java配置类

java 复制代码
@Configuration  // 声明为配置类
@ComponentScan("com.example")  // 扫描组件
@PropertySource("classpath:app.properties")  // 加载配置文件
public class AppConfig {
    
    @Bean  // 声明Bean
    public UserService userService() {
        return new UserServiceImpl(userDao());
    }
    
    @Bean
    public UserDao userDao() {
        return new UserDaoImpl();
    }
}

2.Bean的声明

控制反转是将对象的控制权交给Spring的IOC容器,由IOC容器创建和管理对象,IOC容器创建的对象称为Bean对象。

要把某个对象交给IOC容器管理,需要在类上添加 @Component 注解。

@Component 及其衍生注解:

注解 说明 位置
@Component 声明bean的基础注解 不属于以下三类时,用此注解
@Controller @Component的衍生注解 标注在控制层类上
@Service @Component的衍生注解 标注在业务层类上
@Repository @Component的衍生注解 标注在数据访问层类上

声明Bean的时候,可以通过注解的value属性指定Bean的名字,如果没有指定,默认为 类名 首字母小写。

3.组件扫描

通过注解声明的Bean想要生效,还需要被组件扫描注解 @ComponentScan 扫描。@ComponentScan 注解主要用于启动类(@SpringBootApplication)、配置类(@Configuration)。

隐式使用

当使用 @SpringBootApplication 注解时,它已经包含了 @ComponentScan 注解,默认会扫描主类所在包及其子包:

java 复制代码
// 等价于:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class Application {
    // ...
}

显示指定扫描路径

如果组件不在默认包下,需要显示指定:

java 复制代码
@ComponentScan(basePackages = {
    "com.example.service",
    "com.example.repository",
    "com.example.controller"
})

4.依赖注入

依赖注入的常见方式,有以下三种:

4.1.构造器注入(推荐)

java 复制代码
@Service
public class UserService {
    private final UserDao userDao;
    
    // @Autowired 可省略(Spring 4.3+)
    public UserService(UserDao userDao) {
        this.userDao= userDao;
    }
}
  • 优点:能清晰地看到类的依赖关系、提高了代码的安全性。
  • 缺点:代码繁琐、如果构造参数过多,可能会导致构造函数臃肿。

4.2.Setter注入

java 复制代码
@Service
public class UserService {
    private UserDao userDao;
    
    @Autowired
    public void setUserService(UserDao userDao) {
        this.userDao= userDao;
    }
}
  • 优点:保持了类的封装性,依赖关系更清晰。
  • 缺点:需要额外编写setter方法,增加了代码量。

4.3.属性注入(不推荐,测试时使用)

java 复制代码
@Service
public class UserService {
    @Autowired
    private UserDao userDao;
}
  • 优点:代码简洁、方便快速开发。
  • 缺点:隐藏了类之间的依赖关系、可能会破坏类的封装性。

5.Bean的作用域

java 复制代码
@Component
@Scope("singleton")  // 默认,单例模式,整个容器中只有一个实例
public class SingletonBean { }

@Component
@Scope("prototype")  // 原型模式,每次获取都创建新实例
public class PrototypeBean { }

@Component
@Scope("request")    // 每个HTTP请求一个实例
public class RequestBean { }

@Component
@Scope("session")    // 每个HTTP Session一个实例
public class SessionBean { }
相关推荐
白露与泡影10 小时前
2026版Java架构师面试题及答案整理汇总
java·开发语言
历程里程碑11 小时前
滑动窗口---- 无重复字符的最长子串
java·数据结构·c++·python·算法·leetcode·django
qq_2290580111 小时前
docker中检测进程的内存使用量
java·docker·容器
我真的是大笨蛋11 小时前
InnoDB行级锁解析
java·数据库·sql·mysql·性能优化·数据库开发
钦拆大仁11 小时前
Java设计模式-单例模式
java·单例模式·设计模式
小手cool11 小时前
在保持数组中对应元素(包括负数和正数)各自组内顺序不变的情况下,交换数组中对应的负数和正数元素
java
笨手笨脚の12 小时前
深入理解 Java 虚拟机-04 垃圾收集器
java·jvm·垃圾收集器·垃圾回收
skywalker_1112 小时前
Java中异常
java·开发语言·异常
没有天赋那就反复12 小时前
JAVA 静态方法
java·开发语言
代码丰12 小时前
SpringAI+RAG向量库+知识图谱+多模型路由+Docker打造SmartHR智能招聘助手
人工智能·spring·知识图谱