Spring IoC&DI

目录

1.Spring是什么?

2.详解Ioc

2.1Bean的存储

2.2 Bean Name默认命名规则

2.3 扫描路径

2.4@Autowired和@Resource区别

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 void setUserService(UserService userService) {
        this.userService = userService;
    }
   
}

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

对比维度

构造方法注入

Setter注入

属性注入

代码简洁性

⭐⭐⭐

⭐⭐⭐⭐

⭐⭐⭐⭐⭐

可读性

优秀(依赖一目了然)

良好

 (依赖关系隐藏)

不可变性

支持final(线程安全)

不支持final

 不支持final

单元测试

最容易(直接传参)

容易

 困难(需要反射)

依赖完整性

保证(对象创建即完整)

 不保证(可能部分注入)

 不保证

循环依赖检测

启动时报错

Spring可处理

Spring可处理

可选依赖支持

⭐ 有限(需Optional包装)

优秀(required=false)

支持

框架耦合度

⭐ 低

⭐ 低

(强依赖Spring)

代码重构

⭐⭐⭐

⭐⭐⭐⭐

最容易

Read more

LeetCode——双指针(初阶)

LeetCode——双指针(初阶)

文章目录 * 简要介绍 * 对撞指针 * 快慢指针 * 相关例题 * 移动零 * 题目描述 * 实现思路 * 版本一 * 版本二 * 最终版 * 复写零 * 题目描述 * 实现思路 * 版本一 * 版本二 简要介绍 我们的双指针算法是算法题中比较常见的一种算法,常见的双指针实际上是有两种的,一种是对撞指针,一种就是我们的快慢指针。 对撞指针 一般用于我们的顺序结构当中,也叫左右指针。 实现思路: 1、对撞指针就是从序列两端向中间移动。 2、终止条件一般就是两个指针相遇了或是错开了。 快慢指针 这个指针又叫龟兔赛跑算法,就是使用两个移动速度不同的指针在序列上移动。常用于我们的环形链表或是数组中。 实现思路: 1、研究问题是不是有循环往复的现象出现。 2、设置一个快指针和一个慢指针,比如让快指针移动两步,慢指针移动一步。 相关例题 移动零 题目描述 给定一个数组 nums,编写一个函数将所有 0 移动到数组

By Ne0inhk
LeetCode算法日记 - Day 5: 长度最小的子数组、无重复字符的最长子串

LeetCode算法日记 - Day 5: 长度最小的子数组、无重复字符的最长子串

目录 1. 长度最小的子数组 1.1 题目解析 1.2 解法 1.3 代码实现 2. 无重复字符的最长子串 2.1 题目解析 2.2 解法 2.3 代码实现 1. 长度最小的子数组 209. 长度最小的子数组 - 力扣(LeetCode) 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。 示例 1: 输入:

By Ne0inhk
初探算法的魅力——【暴力枚举】

初探算法的魅力——【暴力枚举】

点击下面查看作者专栏🔥🔥C语言专栏🔥🔥🌊🌊编程百度🌊🌊🌠🌠如何获取自己的代码仓库🌠🌠 🌐索引与导读 * 暴力枚举(BF)的概念 * 暴力枚举的算法步骤 * 例题讲解 * 经典案例讲解一:百鸡问题 * 题目解析 * 思路方案 * 经典案例讲解二:盛最多水的容器 * 暴力枚举算法 * 最优解 * 经典案例讲解三:两数之和 * 经典案例讲解四:2025 * 💻 代码实现 * 希望读者多多三连 * 给小编一些动力 * 蟹蟹啦! 暴力枚举(BF)的概念 暴力枚举也称为穷举法,是计算机算法中最基础、最直观,但也是最费劲的一种解题思路 像我们平时没有最优解的算法题,往往都可以通过暴力枚举去算出最终结果 * 核心思想 不靠巧妙的技巧,而是利用计算机强大的计算能力,把所有可能的情况列举出来,一个一个去验证,直到找到正确答案 暴力枚举的算法步骤 * 列举 :确定解空间的范围,列出所有可能的解候选者 * 检验 :对每一个候选者进行判断,看它是否满足题目

By Ne0inhk
数据结构:kmp算法,Trie树,以及并查集的干货详解---小白也能看懂

数据结构:kmp算法,Trie树,以及并查集的干货详解---小白也能看懂

🎬 博主名称:个人主页 🔥 个人专栏: 《算法通关》,《Java讲解》 ⛺️心简单,世界就简单 序言 昨晚数据结构写了一半,做图太累了,文章写的比较慢,这篇应该就是第二篇,后面还有一篇,太困了,真不行了 今天讲一下 kmp算法,Trie, 并查集 目录 序言 KMP算法 原理 next数组 匹配过程 Trie树 并查集 KMP算法 这里说一下kmp的大致情况 用于处理字符串匹配问题,他也是十分的抽象                给一个S[]主串(比较长的那个),P[]为模板串,kmp我们一般用下标1来开始遍历 接下来我们需要去思考的是 1,暴力去怎么做 2,怎么去优化 下面是暴力的模板代码 大概意思就是,每当我们匹配到不一样的部位,我们的P就要从头开始再跟刚刚s的起点+1位置重新匹配,        这样就会造成串的长度很长时候,就会超时,所以我们就要从这个过程中找性质了

By Ne0inhk