[Java EE 进阶] 一文吃透 Spring IoC&DI:核心概念 + 实战用法 + 面试考点

[Java EE 进阶] 一文吃透 Spring IoC&DI:核心概念 + 实战用法 + 面试考点
摘要 : 

本文深入解析了Spring框架的核心机制IoC(控制反转)和DI(依赖注入)。通过汽车制造案例,对比传统高耦合开发与Spring解耦方案,阐释IoC将对象创建权交给容器、DI实现依赖注入的核心思想。详细介绍了Bean存储的五种类注解(@Controller/@Service等)和@Bean方法注解,以及三种依赖注入方式(属性/构造方法/Setter注入)。针对多Bean注入问题,提出@Primary、@Qualifier和@Resource三种解决方案。最后总结IoC与DI的关系,指出这是理解Spring框架设计思想的基础,也是学习AOP、事务管理等高级特性的前提。文章系统性地讲解了Spring IoC&DI的核心概念和实战应用
在 Spring 体系的学习中,IoC(控制反转)和 DI(依赖注入)是贯穿始终的核心,也是 Spring 框架的灵魂。掌握 IoC&DI 不仅能理解 Spring 的设计思想,更能写出低耦合、高可维护的企业级代码。本文将从概念解析实战实现核心细节面试考点四个维度,彻底讲透 Spring IoC&DI,同时理清 Spring、Spring MVC、Spring Boot 三者的关系

一.IOC&DI 介绍

1. 传统程序开发 的问题 : 高耦合

以 “造一辆车” 为例,传统开发中对象的创建和依赖关系由自身控制:汽⻋依赖⻋⾝,⻋⾝依赖底盘,底盘依赖轮 ;

所有的对象都通过 new 手动创建 ; 当底层组件(如轮胎尺寸) 发生变化时 , 整个调用链上的所有代码都需要修改 , 程序耦合度高 , 可维护性差

public class Main { public static void main(String[] args) { Car car = new Car(21); car.run(); } } public class Bottom { private Tire tire; public Bottom(Integer size) { this.tire = new Tire(size); System.out.println("bottom init..."); } } public class Car { private Framework framework; public Car(Integer size) { this.framework = new Framework(size); System.out.println("car init..."); } public void run() { System.out.println("car run..."); } } public class Framework { private Bottom bottom; public Framework(Integer size) { this.bottom = new Bottom(size); System.out.println("framework init..."); } } public class Tire { int size; public Tire(Integer size) { this.size = size; System.out.println("tire init, size:"+ size); } } 
注意 : 上述代码分为 5 个类



随着车辆的个性化需求增多 , 如果我们修改代码 , 会发现修改成本很高 , 例如 :

从上述代码可以看出 , 问题出现在 : 当最底层代码改动后 , 整个调用链上的所有代码都需要修改



在上⾯的程序中, 我们是根据轮⼦的尺⼨设计的底盘,轮⼦的尺⼨⼀改,底盘的设计就得修改.

同样因为我们是根据底盘设计的车身,那么车身也得改,同理汽⻋设计也得改,也就是整个设计⼏乎都得改

2. IoC (Inversion of Control,控制反转)

Spring 是一个 “控制反转” 的容器,本质是对象的创建权 由程序自身反转给 Spring 容器

  • 传统模式:程序需要主动通过 new 关键字创建对象
  • IoC 模式:对象的创建和管理交给 IoC 容器,程序只需从容器中获取对象即可
  • IoC 的核心价值:实现程序解耦,将对象之间的依赖关系从代码中剥离,由容器统一管理,底层组件变化时,上层代码无需修改
举一个例子直观理解 IoC 思想 : 自动驾驶传统驾驶 : 车辆的控制权由驾驶员掌握自动驾驶 : 控制权反转 , 交给驾驶自动化系统处理

3. 解决方式 : DI (Dependency Injection,依赖注入)

它是实现 IoC 的主要方式 , Spring 容器在创建对象时,容器会动态地为程序提供运行时所以来的资源(对象)

  • 关系 : Ioc 是思想/目标 , DI 是现实手段 , 二者在不同的角度描述这同一间事情--解耦对象依赖
举个例子 : 理解 IOC 和 DI 之间的关系"想吃个好的"(Ioc 思想) , 选择"吃火锅"或者"吃烤肉"(DI 具体实现) , 思想指导实现 , 实现落地思想

容器在运行期间 , 动态的为应用程序提供运行时依赖的资源

我们尝试换一种思路 , 先设计车身 , 根据车身来设计底盘 , 根据底盘来设计轮子 ; 得到依赖关系 : 轮子依赖底盘 , 底盘依赖⻋车身 , 车身依赖汽车

只需要将原本由自己创建的下级类 , 改为传递(注入)的方式

通过构造函数的方式 , 把依赖对象注入到需要使用的对象中

public class main { //注意别调用成v1版本的构造方法 public static void main(String[] args) { Tire tire = new Tire(2,"red"); Bottom bottom = new Bottom(tire); Framework framework = new Framework(bottom); Car car = new Car(framework); car.run(); } } public class Tire { int size; String color; public Tire(Integer size , String color){ this.color = color; this.size = size; System.out.println("tire init:"+color + size); } } public class Bottom { private Tire tire; public Bottom(Tire Tire){ this.tire = tire; System.out.println("bottom init...."); } } public class Framework { private Bottom bottom; public Framework(Bottom bottom){ this.bottom = bottom; System.out.println("Framework init...."); } } public class Car { private Framework framework; public Car(Framework framework){ this.framework = framework; System.out.println("car init..."); } public void run(){ System.out.println("car run..."); } } 

通过上述调整 : 无论底层如何变化 , 整个调用链不做任何变化 , 这样就 完成了代码的解耦

4.IoC 容器的核心能力

Spring 作为 IoC 容器 , 核心只做两件事 :存 : 将对象(Bean)交给 Spring 容器管理取 : 程序需要时 , 从 Spring 容器中获取依赖的 Bean 对象

三 . IoC 实战 : Bean 的存储(将对象交给 Spring 管理)

将对象交给 Spring 容器管理 , 即 Bean 的注册 , Spring 提供类注解和方法注解两种方式 , 其中类注解是日常开发的主流 , 方法注解用于特殊场景

1. 类注解

注释

对应分层

核心作用

@Controller

控制层(Web)

接收请求,处理请求,响应结果

@Service

业务逻辑层(Service)

处理具体的业务逻辑

@Repository

数据访问层(Dao)

负责数据库/数据源操作

@Configuration

配置层

处理项目的配置信息

@Component

通用组件层

非分层的通用组件注册

使用规则 : 直接将注解加到类上即可 , Spring 启动会自动扫描并将该实体类实例化作为 Bean , 纳入容器管理

为什么有多个类注解 : 可以让开发者看到注解就能直接判断出 类的用途 , 符合后端分层开发的规范 , 提升代码可读性

1.1@Component(通用注解)

核心作用 : 最基础的注解 , 告诉 Spring "这个类需要被 IoC 容器实例化并管理 " , 是其他 4 个注解的"父注解" (其它注解本质上就是对@Controller 的特殊化)

使用场景 : 当你的类不属于 Controller/Service/Repository/Configuration 任何一层 , 又需要被 Spring 管理时使用(如工具,组件)

package com.boop.springioc.TestNode.Component; import org.springframework.stereotype.Component; @Component public class UserComponent { public void print(){ System.out.println("do Component"); } }
package com.boop.springioc; import com.boop.springioc.TestNode.Component.UserComponent; import com.boop.springioc.TestNode.Controller.UserController; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; @SpringBootApplication public class SpringIocApplication { public static void main(String[] args) { //获取Spring上下文 ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args); //测试Conponent UserComponent userComponent = context.getBean(UserComponent.class); userComponent.print(); } }

注意 :

① 如果手动删除@Coponent





报错信息 : 找不到类型是'com.boop.springioc.TestNode. Component.UserComponent'的 bean

②ApplicationContext 获取 bean 对象的功能 , 是父类 BeanFactory 提供的

③ 默认 bean 名称 :





根据 Bean 的命名规则 , 来手动获取 Bean











1.2@Controller(表现层注解)

核心作用 : 标记类为 SpringMVC 的控制器(处理 HTTP 请求) , 并且是 Web 层的组件

额外特性 :

  • 配合@RequestMapping 等注解处理特殊请求
  • Spring MVC 的异常处理器 , 参数绑定等功能会优先识别该类注解标记的类
  • 本质:@Controller = @Component + Web层语义
package com.boop.springioc.TestNode.Controller; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @RequestMapping("/usctrl") @ResponseBody @Controller //@Service public class UserController { @RequestMapping("/sayHi") public void sayHi(){ System.out.println("hi,UserController..."); } }
package com.boop.springioc; import com.boop.springioc.TestNode.Controller.UserController; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; @SpringBootApplication public class SpringIocApplication { public static void main(String[] args) { //获取Spring上下文 ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args); //测试@Controller //使用Spring上下文获取对象 UserController userController = context.getBean(UserController.class); //使用对象 userController.sayHi(); } }

注意事项 :

① Spring 上下文对象 :

Spring 上下文对象(ApplicationContext) :

本质是 : Spring 框架的核心容器+运行上下文环境

是 Spring 整个应用的"总控室"

它继承了多个核心接口 , 具备 : IoC 容器核心 , 环境与配置管理 , 资源加载器 , 事件发布/监听 , 国际化支持 , 应用层上下文整合

② 为什么还要加 @ResponseBody如果只加 @Controller 时 , Spring 会把返回值当作视图名取寻找模板(如 xxx.html) ; 示例中的方法 sayHI() 返回值是 void , 如果要直接输出字符串/JSON 给前端 , 必须告诉 Spring 这是响应体 , 而不是页面 ; 此时就需要用到 @ResponseBody , 加在类上 , 并且表示类的所有接口都直接返回数据 , 不找视图我还想让方法 sayHI() 成为一个可以访问的 HTTP 接口 , 就必须加@ResponseBody,@RequestMapping 等注解 ; 如果方法 sayHI() 只是在内部调用 , 不对外提供接口 , 那就不需要加@ResponseBody

1.3@Service(业务层注解)

核心作用 : 标记类为业务逻辑层组件 , 语义上明确这是"处理核心业务逻辑" 的类

额外特性 :

  • 语义化强 , 便于团队协作和代码维护
  • 本质 : @Service = @Component + 业务层语义
package com.boop.springioc.TestNode.Service; import org.springframework.stereotype.Service; @Service public class UserService { public void print(){ System.out.println("do Service"); } }
 import...//此处省略 @SpringBootApplication public class SpringIocApplication { public static void main(String[] args) { //获取Spring上下文 ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args); //测试@Service UserService userService = context.getBean(UserService.class); userService.print(); } }

省去@Service 同样报错

1.4@Repository(数据访问层注解)

核心作用 : 标记类为数据访问层(Dao)组件 , 负责与数据库/数据源交互

额外特性 :

  • 自动转换 JDBC 相关的异常(将原生 SQL 异常转化为 SPring 统一的 DataAccessException)
  • 语义上明确这是"数据访问层" , 是持久化操作的核心
  • 本质 : @Repository = @Component + 数据访问层语义 + 异常转换
package com.boop.springioc.TestNode.Respository; import org.springframework.stereotype.Repository; @Repository public class UserRepository { public void print(){ System.out.println("do Respository"); } }
@SpringBootApplication public class SpringIocApplication { public static void main(String[] args) { //获取Spring上下文 ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args); //测试Repository UserRepository userRepository = context.getBean(UserRepository.class); userRepository.print(); } }

删掉@Repository 同样报错

1.5@Configuration(配置类注解)

核心作用 : 标记类为 Spring 的配置类 , 替代传统的 XML 配置文件 , 用于定义 Bean,配置依赖等

额外特性 :

  • 配合@Bean 注释可以手动注册 Bean(方法即注解)
  • 配置类本身也是一个 Bean , 但优先级高于普通 @Component
  • 支持@ComponentScan(扫描指定包下的注解) , @Import(导入其他配置类)等
  • 本质 : @Configuration = @Component + 配置类语义 + 增强的Bean定义能力
package com.boop.springioc.TestNode.Config; import org.springframework.context.annotation.Configuration; @Configuration public class UserConfig { public void print(){ System.out.println("do config"); } }
@SpringBootApplication public class SpringIocApplication { public static void main(String[] args) { //获取Spring上下文 ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args); //测试@Configuration UserConfig userConfig = context.getBean(UserConfig.class); userConfig.print(); } }

同样的 , 删除@Configuration, 也会报错

2.方法注解

类注解适用于自定义类的 Bean 注册 , 但遇到外部包的类(如第三方工具类) 或 一个类需要多个实例(如多数据源) 时 , 类注解无法使用 , 此时就需要 @Bean 注解

2.1@Bean

@Bean 是方法注解 , 必须配合类注解使用 (如@Component , @Configuration) , 通过方法返回值将对象注册为 Bean , 默认 Bean 名称为方法名

package com.boop.springioc.TestNode.model; import lombok.Data; @Data public class User { private String name ; private Integer age; }
package com.boop.springioc.TestNode.Config; import com.boop.springioc.TestNode.model.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class UserConfig { @Bean public User user(){ User user = new User(); user.setName("zhangsan"); user.setAge(12); return user; } }

执行结果如下

注意 :

① @Bean 注解一定要配合类注解使用

在 Spring 框架设计中 , 方法注解@Bean 要配合类注解才能将对象正常的存储到 Spring 容器中

如果不加类注解 :





② 如果同一个类 , 定义多个对象
@Configuration public class UserConfig { @Bean public User user(){ User user = new User(); user.setName("zhangsan"); user.setAge(12); return user; } @Bean public User user2(){ User user = new User(); user.setName("lisi"); user.setAge(118); return user; } }

异常解析 : NoUniqueBeanDefinitionException ; 这是 Spring 容器中同类型 Bean 不唯一时抛出的异常

接下来的内容就解决此问题

2.2 @Bean 多个实例时 , 根据名称来获取对象

@SpringBootApplication public class SpringIocApplication { public static void main(String[] args) { //获取Spring上下文 ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args); //测试@Bean User user1 = (User) context.getBean("user"); System.out.println(user1); User user2 = (User)context.getBean("user2"); System.out.println(user2); } }

2.3 重命名 Bean (通过@Bean(name = "别名"))

(通过@Bean(name = "别名")) 为 Bean 设置自定义名称 , 支持多个别名

@SpringBootApplication public class SpringIocApplication { public static void main(String[] args) { //获取Spring上下文 ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args); //测试@Bean User u1 = (User)context.getBean("u1"); User user1 = (User)context.getBean("user1"); System.out.println(u1); System.out.println(user1); User u2 = (User)context.getBean("u2"); User user2 = (User)context.getBean("user2"); System.out.println(u2); System.out.println(user2); } }
@Configuration public class UserConfig { @Bean(name = {"u1", "user1"}) public User user(){ User user = new User(); user.setName("zhangsan"); user.setAge(12); return user; } @Bean(name = {"u2", "user2"}) public User user2(){ User user = new User(); user.setName("lisi"); user.setAge(118); return user; } }

3.Bean 的扫描路径

使用类注解/方法注解的 Bean , 并非一定能被 Spring 管理 , 前提是 : Bean 所在的包必须能被 Spring 扫描到

3.1 默认扫描路径

Spring Boot 的启动类注解 @SpringBootApplication 中内置了@ComponentScan , 默认扫描范围是 : 启动类所在包及其所有子类

3.2 手动配置扫描路径

@ComponentScan("com.boop.springioc.Test") @SpringBootApplication public class SpringIocApplication { public static void main(String[] args) { //获取Spring上下文 ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args); //测试扫描路径 Test1 test = context.getBean(Test1.class); test.print(); } }

注意 :

① 被扫描类也需要写@Component 等注释

② 只需要写到被扫描类的完整包名即可

③ 开发推荐 : 将启动类放在项目的根包下(如 com.example. demo) , 将所有业务作为子包 , 无需手动配置扫描路径 , 简化开发

4.Bean 的命名规则

Spring 会为每个 Bean 分配唯一名称

4.1 五大类注解的 Bean 命名

  • 默认 : 类名首字母小写 (如 UserController->userController)
  • 特殊 : 类名前两位均大写时 , 保留原类名(如 UCtroller->UCtroller)
  • 自定义 : 通过注解的 value 属性设置(如@Controller("user"))

4.2@Bean 注解的 Bean 命名

  • 默认 : 类名首字母小写
  • 自定义 : 通过@Bean(name = "别名")设置

四.DI 实战 : Bean 的获取(依赖注入的三种方式)

依赖注入是指 从 Spring 容器中获取 Bean , 并注入到需要依赖它的对象中 , Spring 提供了属性注入 , 构造方法注入 , Setter 注入三种方式

1.属性注入(常用)

将 @Autowride 注解 直接标注在类的属性上

特点 : 代码简洁 , 开发效率高

@Service public class UserService { public void print(){ System.out.println("do Service"); } } @Configuration public class UserConfig { public void print(){ System.out.println("do config"); } }
@Controller public class HController { //属性注入 @Autowired private UserService us; @Autowired private UserConfig userConfig; public void print(){ System.out.println("注入测试...."); System.out.println(us); us.print(); userConfig.print(); } }
@SpringBootApplication public class SpringIocApplication { public static void main(String[] args) { //获取Spring上下文 ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args); //测试属性注入 HController hController = context.getBean(HController.class); HController hController1 = (HController) context.getBean("HController"); hController.print(); System.out.println(); hController1.print(); } }

异常解析 :

如果去掉其中一个@Autowired , NullPointerException ;

Hctroller 类中的 us 字段为 null , 调用它的 print()方法时触发了空指针异常 ; 异常的本质时 Spring 依赖注入失败

2.构造方法注入

将@Autowride 标注在构造方法上

特点 : 支持注入 final 修饰的属性 , 依赖对象在使用前一定会被完全初始化号 , 通用性好

@Controller public class HController { //构造方法注入 private UserService us; private UserConfig userConfig; @Autowride public HController(UserConfig uc , UserService us){ this.us = us; this.userConfig = uc; } public void print(){ System.out.println("注入测试...."); System.out.println(us); us.print(); userConfig.print(); } }

如果类只有一个构造方法 , 那么@Autowried 注解可以省略 ; 如果类中有多个构造方法 , 那么需要添加上@Autowried 来明确到底使用哪个构造方法

异常解析 :

① 这个类有多个构造方法 , 且没有被@Autowried 标记



② 如果提供多个构造方法 , 且均被@Autowired 标记 , 服务会启动失败 ;

原因 : Spring 要求 , 一个类最多只能由一个构造方法被@Aurowired 标记(无论 required 是 true 还是 false) , 否则就会抛异常






解决方法 :只提供一个构造方法只给一个构造方法添加@Autowired 注解

3.Setter 注入(灵活)

将@Autowride 注解标注在 Setter 方法上

特点 : 支持在对象实例化后 , 动态修改/重新注入依赖对象 , 灵活

@Controller public class HController { //Setter方法注入 private UserService us; private UserConfig userConfig; @Autowired public void setUs(UserService us) { this.us = us; } @Autowired public void setUserConfig(UserConfig userConfig) { this.userConfig = userConfig; } public void print(){ System.out.println("注入测试...."); System.out.println(us); us.print(); userConfig.print(); } }

异常解析 :

① 如果不加@Autowried , 同样报 NullPointerException



② 如果只在部分 Setter 方法上加@Autowried , 只有部分对象注入成功





4.三种注入的优缺点

注入方式

优点

缺点

属性注入

代码简洁、开发效率高

仅适用于 IoC 容器,无法注入 final 属性,可能出现 NPE

构造方法注入

支持 final 属性,初始化安全,通用性好

注入多个 Bean 时,代码繁琐

Setter 注入

支持动态修改依赖对象

无法注入 final 属性,对象可能被多次修改

4.DI 进阶 : 解决同一个类型多个 Bean 的注入问题

当 Spring 容器中存在同一类型的多个 Bean时,直接使用 @Autowired 注入会抛出NoUniqueBeanDefinitionException(非唯一 Bean 异常),Spring 提供了 3 种解决方案,是核心面试考点

4.1@Primary : 指定默认 Bean (配合 @Bean 使用)

在其中一个 Bean 上添加 @Primary 注解 , 表示该 Bean 是同类型的默认 Bean , @Autowired 注入时 , 会优先选择该 Bean

@Configuration public class UserConfig { @Primary @Bean(name = {"u1", "user1"}) public User user(){ User user = new User(); user.setName("zhangsan"); user.setAge(12); return user; } @Bean(name = {"u2", "user2"}) public User user2(){ User user = new User(); user.setName("lisi"); user.setAge(118); return user; } public void print(){ System.out.println("do config"); } } @Controller public class HController { //属性注入 @Autowired private User us; @Autowired private UserConfig userConfig; public void print(){ System.out.println("注入测试...."); System.out.println(us); us.toString(); userConfig.print(); } }
@SpringBootApplication public class SpringIocApplication { public static void main(String[] args) { //获取Spring上下文 ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args); //测试注入多个Bean的解决方式 HController hController2 = context.getBean(HController.class); hController2.print(); System.out.println(); } }

4.2 @Qualifier : 指定 Bean 名称注入 (配合 @Autowired 使用)

@Qualifier 注解 必须配合 @Autowired 使用,通过value属性指定要注入的 Bean 名称,精准定位目标 Bean,解决同类型多 Bean 的歧义问题

注意 : @Qualifier 的优先级高于@Primary

@Controller public class HController { //属性注入 @Qualifier("u2") @Autowired private User us; @Autowired private UserConfig userConfig; public void print(){ System.out.println("注入测试...."); System.out.println(us); us.toString(); userConfig.print(); } }

4.3@Resource(非 Spring 注解)

@Resource 注解JDK 原生注解(非 Spring 提供),默认按 Bean 名称注入,通过name属性指定目标 Bean 名称,无需配合其他注解,直接替代 @Autowired+@Qualifier

注意 : @Resource 优先级最高;@Resource(name = "xxx") > @Qualifier("xxx") > @Primary > 类型匹配

@Controller public class HController { //属性注入 //@Qualifier("u2") //@Autowired @Resource(name = "u2") private User us; @Autowired private UserConfig userConfig; public void print(){ System.out.println("注入测试...."); System.out.println(us); us.toString(); userConfig.print(); } }

4.4 @Autowired vs @Resource

  1. 所属框架不同 : @Autowired 是 Spring 框架提供的 ; @Resource 是 JDK 原生的注解
  2. @Autowired 默认按类型注入 , 失败时按名称匹配 ; @Resource 默认按名称注入 , 失败时按类型匹配
  3. @Autowired 只有 required 属性 ; @Resouce 有 name , type 等多个属性

五、总结

Spring 的核心是IoC 容器,IoC 是思想,DI 是具体实现,二者的核心目标是实现程序解耦

  1. IoC:将对象的创建权从程序反转给 Spring 容器,容器负责 Bean 的注册和管理,核心是Bean 的存储
  2. DI:容器为程序动态注入依赖的 Bean,核心是Bean 的获取,有属性注入、构造方法注入、Setter 注入三种方式;
  3. Bean 注册:五大类注解(@Controller/@Service/@Repository/@Configuration/@Component)用于自定义类,@Bean 注解用于外部包类或多实例场景,需保证 Bean 在扫描路径内;
  4. 多 Bean 注入解决:@Primary 指定默认 Bean,@Qualifier+@Autowired 按名称注入,@Resource 原生按名称注入;
  5. 三者关系:Spring 是 IoC 容器基础,Spring MVC 是 Spring 的 Web 子框架,Spring Boot 是 Spring 的快速开发脚手架,核心能力均基于 IoC&DI。

IoC&DI 是 Spring 体系的入门基础,也是后续学习 AOP、事务管理、Spring Cloud 等内容的前提,掌握其核心思想和实战用法,才能真正理解 Spring 的设计精髓,写出优雅的企业级 Java 代码。

Read more

previous preparation error: The developer disk image could not be unmounted on the device;An unknow

这个错误: previous preparation error: The developer disk image could not be unmounted on the device; An unknown error message 'internalError'; was from the device. 是 Xcode 在真机运行 / 调试时挂载 Developer Disk Image (DDI) 失败的典型情况,主要原因是 设备调试环境卡住或残留。 1️⃣ 主要原因 1. 之前调试挂载的 Developer Disk Image 没被正确卸载 * 比如上次调试时直接拔了线,或者设备崩溃/重启了。 2. Xcode

By Ne0inhk
FPGA开发常用软件盘点:Vivado、Quartus、ModelSim全面对比

FPGA开发常用软件盘点:Vivado、Quartus、ModelSim全面对比

在FPGA开发过程中,EDA工具(Electronic Design Automation) 是工程师的生产力核心。不同厂商的FPGA芯片通常配套不同的开发工具,但在项目实践中,很多工程师往往会接触多种EDA软件。 本文将带你系统梳理三款FPGA开发中最常用的软件:Vivado、Quartus、ModelSim,从功能、适用场景、优缺点等多个维度进行全面对比,助你快速入门并合理选择。 一、Vivado —— Xilinx官方旗舰开发平台 1. 基本简介 Vivado是Xilinx(现为AMD)推出的综合性FPGA开发环境,主要面向7系列、UltraScale、Versal等高端FPGA器件。 它集成了综合、布局布线、时序分析、仿真、硬件调试等完整流程,是Xilinx FPGA开发的首选工具。 2. 核心功能 * RTL综合与实现:支持Verilog、VHDL和SystemVerilog,自动进行逻辑优化与布局布线。 * IP Integrator:可视化模块连接工具,适合SoC级设计。 * 仿真与调试:内置Vivado Simulator,也可外接ModelSim进行

By Ne0inhk

直播效率工具如何提升互动体验:神奇弹幕机器人全功能指南

直播效率工具如何提升互动体验:神奇弹幕机器人全功能指南 【免费下载链接】Bilibili-MagicalDanmaku【神奇弹幕】哔哩哔哩直播万能场控机器人,弹幕姬+答谢姬+回复姬+点歌姬+各种小骚操作,目前唯一可编程机器人 项目地址: https://gitcode.com/gh_mirrors/bi/Bilibili-MagicalDanmaku 直播间互动效率低下?场控人手不足导致观众流失?试试这款可编程直播助手——神奇弹幕机器人,它能让你轻松实现弹幕智能管理、自动互动响应和个性化内容推送,全方位提升直播专业度。作为目前唯一支持自定义脚本的B站直播辅助工具,它将帮助主播从繁琐的重复工作中解放出来,专注于内容创作与观众互动。 1. 为什么选择可编程直播助手? 在直播行业竞争日益激烈的今天,高效的场控工具已成为主播的核心竞争力。传统人工场控不仅需要投入大量人力成本,还难以应对高并发互动场景。神奇弹幕机器人通过模块化设计,将直播互动流程自动化、智能化,让单人主播也能打造出专业团队级别的直播效果。 主播视角的实时互动控制面板,可快速调整直播标题、封面等核心设置 核心价值对

By Ne0inhk
无人机避障——Mid360+Fast-lio感知建图+Ego-planner运动规划(胎教级教程)

无人机避障——Mid360+Fast-lio感知建图+Ego-planner运动规划(胎教级教程)

电脑配置:Xavier-nx、ubuntu 18.04、ros melodic 激光雷达:Livox_Mid-360 结果展示:左边Mid360+Fast-lio感知建图,右边Ego-planner运动规划 1、读取雷达数据并显示 无人机避障——感知篇(采用Livox-Mid360激光雷达获取点云数据显示)-ZEEKLOG博客 看看雷达数据话题imu以及lidar两个话题  2、读取雷达数据并复现fast-lio  无人机避障——感知篇(采用Mid360复现Fast-lio)-ZEEKLOG博客 启动fast-lio,确保话题有输出   由于此处不需要建图,因此不打开rviz,launch文件如下修改: <launch> <!-- Launch file for Livox MID360 LiDAR --> <arg name="rviz&

By Ne0inhk