Spring IoC&DI

目录

1.Spring是什么?

2.详解Ioc

2.1Bean的存储

[2.2 Bean Name默认命名规则](#2.2 Bean Name默认命名规则)

[2.3 扫描路径](#2.3 扫描路径)

2.4@Autowired和@Resource区别

[2.5 @Autowired查找Bean顺序](#2.5 @Autowired查找Bean顺序)

3.详解DI

Spring三种依赖注入方式优缺点对比


1.Spring是什么?

Spring 是一个开源的 Java 开发框架 ,核心目标是简化企业级应用开发。它提供了模块化工具(如 Spring MVC、Spring Security、Spring Data 等),让开发者更专注于业务逻辑,而非重复的底层代码。也就是包含了众多方法的IoC容器,

容器是什么?

在 Spring 中,容器是一个 装对象的"盒子", 它负责创建、配置、组装你编写的 Java 对象(称为 Bean),并管理它们的生命周期。

Ioc(控制反转)是什么?

谁使用谁控制=>Spring来控制

是 Spring 的核心思想。传统代码中,对象自己控制依赖的创建(如 new Service());而 IoC 将控制权"反转"给容器------你只需声明依赖关系,容器自动注入所需对象。

例如:

复制代码
// 传统方式:程序员自己创建依赖
UserService service = new UserService();

// IoC 方式:容器自动注入依赖
@Autowired
private UserService service;

简述Ioc和di

控制反转(IoC)的核心思想是将对象的创建和管理权从传统的"谁使用谁控制"模式转变为由框架(如Spring)统一管理。这种方式有效降低了组件间的耦合度,使系统更易于维护和扩展。

在IoC模式下,对象的生命周期完全由Spring容器管理,开发者无需手动实例化对象。依赖注入(DI)是IoC的一种实现方式,通过DI,Spring容器在运行时自动将依赖的对象注入到目标组件中,开发者只需声明依赖关系即可获得所需实例。

通过在类上面注释**@Component**:告诉Spring将该对象的控制权转给Spring

在类中注明依赖之后注释**@Autowired**:表明从Spring容器中拿出该对象并赋值给注明的依赖

2.详解Ioc

2.1Bean的存储

Spring为了更好地完善web服务提供许多注解能够完成对象的存储,代替**@Component**

类注解:@Controller,@Service,@Repository,@Component,@Configuration

方法注解:@Bean

类注解:

@Controller(控制器存储)

复制代码
@Controller
public class UserController {
    public String sayHello(){
        return "Hello World!";
    }
}
复制代码
public static void main(String[] args) {
    //应用上下文
    ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
    //通过getBean方法观察是否能拿到UserController对象
//拿到说明通过@Controller,对象成功创建并且存储到Spring容器当中
    UserController bean = context.getBean(UserController.class);
    System.out.println(bean.sayHello());
}

其他注解均可用同样的方法验证存储功能,如@Service:

复制代码
@Service
public class UserService {
    public String doService(){
        return "doService";
    }
}
复制代码
@SpringBootApplication
public class SpringIocDemoApplication {

    public static void main(String[] args) {
       //应用上下文
       ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);

       UserService bean = context.getBean(UserService.class);
       System.out.println(bean);

    }

}

因此后续类注解同上验证不做过多重复解释,既然起到效果相似,为什么还要有这么多?

原因是为了语义更加清晰,提高可读性,在对应的三层架构中使用对应的注解能更好的理解语义

//不好的实践 :因为其余注解都是@Component的衍生注解 ,因此都使用@Component
@Component

public class UserController { } // 这是控制器吗?服务吗?看不出来

@Component

public class UserService { } // 这是服务层吗?工具类吗?无法分辨

@Component

public class UserDao { } // 这是数据访问层吗?不清楚

// 好的实践:语义明确
@Controller

public class UserController { } // 明确是控制器,处理HTTP请求

@Service

public class UserService { } // 明确是服务层,处理业务逻辑

@Repository

public class UserDao { } // 明确是数据访问层

方法注解:开发人员自己创建对象交给Spring去管理,用于解决同一个类需要多个对象的问题/创建第三方对象

注意**:** 需配合类注解使用

复制代码
UserInfo类

@AllArgsConstructor//有参构造方法
@NoArgsConstructor//无参构造方法
@Data
public class UserInfo {
    private String name;
    private int age;
}
复制代码
BeanConfig类

@Configuration
public class BeanConfig {
    @Bean
    public UserInfo userInfo(){
        return new UserInfo("zhangsan",12);//自主创建对象
    }
}
复制代码
@SpringBootApplication
public class SpringIocDemoApplication {

    public static void main(String[] args) {
       ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);

       UserInfo bean1 = context.getBean(UserInfo.class);
       System.out.println(bean1);

    }

}

可以看到能够通过**@Bean** 使Spring 得到对UserInfo对象的控制权

接下来解决多个对象创建的问题

复制代码
@Configuration
public class BeanConfig {
//创建两个UserInfo对象,方法名区分开
    @Bean
    public UserInfo userInfo(){
        return new UserInfo("zhangsan",12);
    }
    @Bean
    public UserInfo userInfo2(){
        return new UserInfo("lisi",16);
    }
}
复制代码
@SpringBootApplication
public class SpringIocDemoApplication {

    public static void main(String[] args) {
       ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
       //只靠类型无法区分对象,因此传入对象名作为参数
       UserInfo bean1 = context.getBean("userInfo",UserInfo.class);
       System.out.println(bean1);
       UserInfo bean2 = context.getBean("userInfo2",UserInfo.class);
       System.out.println(bean2);
    }

}

2.2 Bean Name默认命名规则

(1)类注解

  • 类名前两位为大写,如USerinfo=>bean name 为原类名
  • 其他情况=>bean name 为类名的小驼峰形式

(2)方法注解

  • bean name 为方法名

2.3 扫描路径

默认情况下,Spring Boot 只扫描启动类所在包及其子包

但是可以通过**@ComponentScan(basePackages="")**来指定要扫描的路径

2.4@Autowired和@Resource区别

@Autowired是Spring 提供的注解,根据类型进行注入

@Resource是JDK 提供的注解,根据名称注入

2.5 @Autowired查找Bean顺序

  1. 按类型查找 Bean

    • 根据依赖的类型(Class/Interface)在 Spring 容器中查找匹配的 Bean。
  2. 判断是否找到

    • 如果未找到​ →报错

    • 如果找到​ → 进入下一步判断。

  3. 是否配置了 @Qualifier参数?

    • 如果已配置 @Qualifier ​ → 按 @Qualifier指定的名称进一步筛选 Bean。

      • 如果找到唯一匹配 → 装配成功 ✅。

      • 如果未找到或仍匹配多个 → 抛异常 ❌。

    • 如果**未配置 @Qualifier**​ → 进入默认策略(按字段名匹配)。

  4. 字段名匹配

    • 匹配成功→装配成功

    • 未匹配→说明未指定名称,进入下一判断

  5. 是否只有一个Bean

    • 如果只有一个bean → 直接装配

3.详解DI

依赖注入是一个过程 ,是ioc容器创建Bean的过程中提供所依赖的资源 ,这个资源就是对象

Spring提供了三种方式:

(1)属性注入@Autowired

@Component
public class UserService {
// 方式1:直接注入到属性
@Autowired
private UserRepository userRepository;
}

(2)构造方法注入

@Service

public class UserService {

// 使用final,确保依赖不可变

private final UserRepository userRepository;

// 构造方法注入

public UserService(UserRepository userRepository) {

this.userRepository = userRepository;

}

}

注意:

  • 只有一个构造方法时默认执行这一个
  • 多个构造方法默认执行无参构造,若没有无参构造则报错
  • 可通过@Autowired注解指定执行的构造方法

(3)Setter注入

@Service

public class UserService {

private UserService userService;

// Setter注入

@Autowired

public voidsetUserService (UserService userService) {

this.userService = userService;

}

}

Spring三种依赖注入方式优缺点对比

对比维度 构造方法注入 Setter注入 属性注入
代码简洁性 ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
可读性 优秀(依赖一目了然) 良好 (依赖关系隐藏)
不可变性 支持final(线程安全) 不支持final 不支持final
单元测试 最容易(直接传参) 容易 困难(需要反射)
依赖完整性 保证(对象创建即完整) 不保证(可能部分注入) 不保证
循环依赖检测 启动时报错 Spring可处理 Spring可处理
可选依赖支持 ⭐ 有限(需Optional包装) 优秀(required=false) 支持
框架耦合度 ⭐ 低 ⭐ 低 (强依赖Spring)
代码重构 ⭐⭐⭐ ⭐⭐⭐⭐ 最容易
复制代码
复制代码
复制代码
相关推荐
zhoutongsheng1 小时前
C#怎么实现Swagger文档 C#如何在ASP.NET Core中集成Swagger自动生成API文档【框架】
jvm·数据库·python
WinterKay1 小时前
【开源】我写了一个轻量级本地数据库浏览工具,支持 MySQL/Redis 只读查询
数据库·mysql·开源
zxrhhm2 小时前
Oracle 索引完整指南
数据库·oracle
程序猿乐锅3 小时前
【Tilas|第三篇】多表SQL语句
数据库·经验分享·笔记·学习·mysql
Navicat中国4 小时前
使用 Navicat 导入向导导入 Excel 数据时,系统提示导入成功,表中也能看到数据,但行数统计显示为 0,这是什么原因?
数据库·excel·导入
gmaajt4 小时前
Golang怎么做国际化多语言_Golang i18n教程【核心】
jvm·数据库·python
折哥的程序人生 · 物流技术专研4 小时前
从“卡死”到“秒过”:WMS销售数据跨库回填的极限优化之旅
数据库·机器学习·oracle
李可以量化4 小时前
DeepSeek 量化交易实战:用标准化提示词模板实现 AI 辅助交易决策
大数据·数据库·人工智能
maqr_1104 小时前
CSS如何利用Sass定义全局阴影方案_通过变量实现统一CSS风格
jvm·数据库·python
m0_613856294 小时前
uni-app怎么做类似于美团的商家评价星级 uni-app五星评分组件制作【实战】
jvm·数据库·python