深入浅出:Spring IOCDI
什么是IOC
IOC
IOC(Inversion of Control),是一种设计思想,在之前的SpringMVC里就在类上添加@RestController和@Controller注解就是使用了IOC,这两个注解就是在Spring中创建一个对象,并将注解下的类交给Spring管理,Spring框架启动时就会自己加载这个类,交给Spring管理的类我们统称为Bean。
IOC的意思就是控制反转,在之前我们的面向对象编程中,我们要使用一个对象需要自己new出来,但是现在使用IOC思想后,我们创建对象的时候,是通过容器来创建的,对象的创建过程由自己控制,变成了由第三方容器控制,而程序中只需要注入这个对象就行,就是控制反转了。所谓的控制反转就是对象控制权的反转。
控制反转这个词听起来很高大上其实就是一个很简单的思想,比如之前我们需要什么工具都要自己去找,这个工具可能在车库可能在卧室。而现在有了工具箱(IOC容器),我们就可以把这些工具放到工具箱里(使用注解交给Spring管理),想用的时候直接从工具箱中拿就行(依赖注入)。
代码演示IOC
以造一辆车来进行举例
传统开发方式
先设计轮子,然后根据轮子大小设计底盘,之后再根据底盘设计车身,最后根据车身设计好整个车。这里就存在一个很明显的依赖连,每创建一个对象都要依赖另一个对象。
public class Main {public static void main(String[] args) {Car car = new Car();car.run();}
}class Car {private Framework framework;public Car() {framework = new Framework();System.out.println("car init");}public void run() {System.out.println("car run run!");}
}//车身
class Framework {private Bottom bottom;public Framework() {bottom = new Bottom();System.out.println("Framework init");}
}//底盘
class Bottom {private Tire tire;public Bottom() {tire = new Tire();System.out.println("bottom init");}
}//轮子
class Tire {//轮子尺寸private int size;public Tire() {this.size = 25;System.out.println("tire init");}
}
如果此时有新的需求要求早造一个20的轮子,那么就要给Tire加一个有参构造方法,相应的要给上面所有有依赖关系类全部都加一个参数。
不过你可能会想,我一开始就给Tire一个有参的构造函数不就好了。
public class Main {public static void main(String[] args) {Car car = new Car(size);car.run();}
}class Car {private Framework framework;public Car(int size) {framework = new Framework(size);System.out.println("car init");}public void run() {System.out.println("car run run!");}
}//车身
class Framework {private Bottom bottom;public Framework(int size) {bottom = new Bottom(size);System.out.println("Framework init");}
}//底盘
class Bottom {private Tire tire;public Bottom(int size) {tire = new Tire(size);System.out.println("bottom init");}
}//轮子
class Tire {//轮子尺寸private int size;public Tire(int size) {this.size = size;System.out.println("tire init");}
}
确实如果一开始就设计好,的确可以解决这个问题,但是如果之后生产慢慢扩大,需求越来越多样化,还能一开始就设计好吗?如果我轮子加点东西,车身再加点东西,底盘再加点东西,那么要改的地方就太多工作量太大了而且也会出现大量冗余,这样牵一发而动全身的方法明显是不太合理的。
IOC开发
现在我们换一种思路,先提前设计车子的大概样子,然后根据车子的样子设计车身,根据车身设计底盘,最后根据底盘设计轮子。就相当于把一个汽车分为多个配件,把这个配件造完后再拼接到另一个配件上,最后就可以组装好一个汽车了,这样把每个配件单独造的方式就算每个配件之后的变化再大也不会影响其他配件。
public class Tire {private int size;public Tire(int size) {this.size = size;System.out.println("轮胎的尺寸为:" + this.size);}
}
public class Bottom {public Tire tire;public Bottom(Tire tire) {this.tire = tire;System.out.println("Bottom loading...");}
}
public class Framework {public Bottom bottom;public Framework(Bottom bottom) {this.bottom = bottom;System.out.println("bottom loading...");}
}
public class Car {public Framework framework;public Car(Framework framework) {this.framework = framework;System.out.println("car loading...");}public void run(){System.out.println("car is running...");}public static void main(String[] args) {Tire tire = new Tire(17);Bottom bottom = new Bottom(tire);Framework framework = new Framework(bottom);Car car = new Car(framework);car.run();}
}
之前我们把创建下级类的任务交给类本身,现在我们把创建下级类的任务交给Main去做,这样就达到了和其他类解耦合的效果,这里就可以把Main看作一个IOC容器。当我们需要使用某个对象时,只需要从IOC容器中取就好了,而且不用考虑其细节。Spring就是一种IOC容器,来帮助我们做这些资源管理。
DI(依赖注入)
DI就是依赖注入的意思,IOC是一种思想,DI是IOC的一种具体的实现方式,就是在创建对象的时候,把该对象依赖的对象注入给该对象,而不是由对象自己创建,这样做就可以很好的降低对象之间的耦合度。
注入这个词听起来可能有些抽象,我个人理解就是假如,对象A的创建依赖对象B,那么我不在对象A内部new对象B,而是在它外部提前把对象B创建好,然后通过某种方法传给对象A,这个过程就是注入,而这个"某种方法"可以有多种,刚刚的例子就是通过构造函数的方式,将依赖对象传递给要使用的对象中的。
Spring中使用IOC和DI
IOC
既然Spring就是一种IOC容器,那么就应该具备向容器中存,和从容器中取得功能,Spring容器管理得主要是对象,这些被管理的对象我们称之为Bean。把这些对象交给Spring管理后,由Spring来负责对象得创建和销毁,我们只需要知道怎么存和怎么取就行。
Bean的存储
Spring为了更好的服务web应用程序,提供了很多注解
- 类注解:
- @Controller:用于标识一个SpringMVC中的控制器,负责处理用户请求,并返回响应。
- @Service:用于标注一个类作为业务层的组件处理具体的业务逻辑,通常标识Service层的类
- @Repository:用于标识数据访问层,也称为持久层(Dao层),负责数据访问操作。
- @Component:这是Spring中所有具体组件的通用注解,表示该类用作Spring容器管理,会被Spring自动加载,并可以通过依赖注入来使用该类。
- @Configuration:用于表示这是一个配置类,处理一些配置信息。
- 方法注解:
- @Bean:类注解是用来添加到某个类上的,这样就会有两个问题,使用外部包里的类没有办法对只读文件添加注解,一个类需要多个对象,但是我们类注解的对象只能创建一个时,此时就要使用@Bean
这些注解都有一个统一的功能就是将注解所修饰的类或方法,交给Spring管理,相当于一个存的操作。
可以看到Controller和Service注解都封装了@component注解,而且不止这两个注解其他注解也是一样,所以这些类也可以称为@component的衍生注解
其实这些类在功能上差不多,主要是用来区分不同层的类,和我们程序的结构分层是对应的,当程序员看到这些注解标识的类,就知道这是哪个层的类主要负责什么功能。
因为这些注解的使用方法都一样所以就用@Controller来演示
@Controller//将对象存储交给Spring管理
public class HelloController {public void hello(){System.out.println("hello Spring-ioc");}
}
在类上面加上对应注解就可以把该类的对象交给Spring管理了,接下来我们从容器中拿取该对象
获取Bean
按照类型获取
//获取Spring上下文对象
ApplicationContext context = SpringApplication.run(DemoApplication.class,args);//根据类型查找
//从Spring上下文中获取对象HelloController bean = context.getBean(HelloController.class);
//调用对象方法System.out.println(bean);bean.hello();
通过这种方法就可以根据类型查找到我们需要的类对象。
这个ApplicationContext就是Spring的上下文,负责管理 Bean 的生命周期和配置信息,里面包含我们存储的Bean的信息。
不过上述代码是根据类型来查找对象,如果同一个类型存在多个Bean呢?ApplicationContext也提供了其他的获取Bean的方法,这些方法是ApplicationContext继承了BeanFactory类,实现了BeanFactory的getBean方法。
public interface BeanFactory {//以上省略... // 1. 根据bean名称获取bean Object getBean(String var1) throws BeansException;// 2. 根据bean名称和类型获取bean <T> T getBean(String var1, Class<T> var2) throws BeansException;// 3. 按bean名称和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的bean Object getBean(String var1, Object... var2) throws BeansException;// 4. 根据类型获取bean <T> T getBean(Class<T> var1) throws BeansException;// 5. 按bean类型和构造函数参数动态创建bean, 只适⽤于具有原型(prototype)作⽤域的
bean<T> T getBean(Class<T> var1, Object... var2) throws BeansException;//以下省略...
}
可以看到整个接口中提供了多种获取bean的方法
按照bean名称获取
//根据名字查找(小驼峰)HelloController bean2 = (HelloController) context.getBean("helloController");System.out.println(bean2);bean2.hello();
//传入的类名要是小驼峰,因为Spring会给管理的Bean按照小驼峰的方式命名
Spring官方有一个Bean的命名约定
命名约定使用Java标准约定作为Bean的名字,也就是小驼峰比如
UserController -> bean名称:userController
不过也有特殊情况,就是类名的第一个和第二个都是大写时,将会保留原始大小写比如
Ucontroller -> bean名称:UController
通过Bean名称和类型获取Bean对象
//根据名字和类形查找HelloController bean3 = context.getBean("helloController",HelloController.class);System.out.println(bean3);bean3.hello();
这里我们查看一下运行结果(这是和上述两个例子一样的结果)
可以看到地址都是一样的,说明这三种方法获取到的是同一个对象,因为Spring管理的对象默认是单例的。
以上就是比较常用的方法,其他的博主在这里就不过多介绍了。
ApplicationContext和BeanFactory
- BeanFactory是在第一次请求获取Bean对象时才进行实例化,ApplicationContext是在容器启动时就一次性加载并初始化所有的Bean对象。
- ApplicationContext属于BeanFactory的子类,它继承了BeanFactory的所有功能之外,还扩展了很多其他功能,比如国际化处理,事件传播,AOP管理等。
- ApplicationContext和BeanFactory都负责管理Bean的生命周期,是Spring中的两个顶级接口
方法注解@Bean
使用方法和类注解一样,只不过@Bean是用来修饰方法,而且@Bean注解需要在类注解注解下的类中使用,返回一个想要交给Spring管理的对象
@Configuration
public class UserConfig {@Beanpublic Student student(){Student user = new Student();user.setName("zhangsan");user.setAge(18);return user;}}
获取Bean对象
ApplicationContext context = SpringApplication.run(DemoApplication.class,args);
Student student = context.getBean(Student.class);System.out.println(student);
同一个类获取多个对象
@Configuration
public class UserConfig {@Beanpublic Student student1(){Student user = new Student();user.setName("zhangsan");user.setAge(18);return user;}@Beanpublic Student student2(){Student user = new Student();user.setName("李四");user.setAge(18);return user;}}
当一个类有多个Bean注解时获取@Bean修饰的Bean对象不能只用类型的方法查找,因为这些对象的类型都一样,自然也就查找不到,使用就要用Bean名称的方法查找,@Bean修饰的Bean名称就是方法名(也可以名称+类型)。
Student student1 = (Student) context.getBean("student1");Student student2 = (Student) context.getBean("student2");System.out.println(student1);System.out.println(student2);System.out.println(student1 == student2);
可以看到是两个不同的对象
也可以使用name参数比如@Bean(name = {"s1","student1"})的方式给Bean重命名
扫描路径
并不是所有加了注解的类都能被Spring管理,必须在Spring的扫描路径下的才行,Spring默认的扫描路径就是启动类所在的包路径
也就是必须在这个类的同级目录或者子目录才可以被Spring管理,如果不在Spring就扫描不到,不过也可以使用@ComponentScan注解手动添加扫描路径,相当于告诉Spring除了默认的路径,还要再额外加一些扫描路径。
DI
前面讲述的大多都是IOC,现在来讲讲DI(依赖注入)的细节,依赖注入就是,IOC容器在创建Bean时,去提供运行时所依赖的资源(对象),其实就是把Spring管理的对象取出来,放到某个类属性里,(可以使用@Autowired完成依赖注入操作)。
Spring给我们提供了三种依赖注入的方法
- 属性注入
- 构造方法注入
- Setter方法注入
这里将Service注入到Controller中
属性注入
Service
@Service
public class UserService {public void hello(){System.out.println("hello service");}
}
controller(借助@Autowired注解)
@Controller
public class HelloController {//属性注入@Autowiredprivate UserService service;public void hello(){System.out.println("hello Spring-ioc");service.hello();}
}
获取controller
ApplicationContext context = SpringApplication.run(DemoApplication.class,args);HelloController controller = context.getBean(HelloController.class);controller.hello();
//这里的@Autowired作用就相当于给service属性赋值
构造方法注入
交给Spring管理的类,Spring会使用反射获取到对应类的构造方法来创建对象
controller
@Controller
public class HelloController {//属性注入private UserService service;@Autowiredpublic HelloController(UserService service){this.service = service;}public void hello(){System.out.println("hello Spring-ioc");service.hello();}
}
//如果类只有⼀个构造方法,那么@Autowired注解可以省略;如果类中有多个构造方法, 那么需要添加上@Autowired来明确指定到底使用哪个构造方法
Setter方法注入
使用方法其实和用构造方法注入差不多
@Controller
public class HelloController {//属性注入private UserService service;@Autowiredpublic void Setter(UserService userService){this.service = service;}public void hello(){System.out.println("hello Spring-ioc");service.hello();}
}
三种注入方法的优缺点
属性注入
- 优点:简洁,使用方便。
- 缺点:只能用于IoC容器,非IoC容器不可用,并且只有在使用的时候才会出现空指针异常,不能注入一个Final修饰的属性。
构造函数注入
优点:
- 可以注入final修饰的属性。
- 注入的对象不会被修改。
- 依赖对象在使用前一定会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法。
- 通用性好,构造方法是JDK支持的,所以更换任何框架,它都是适用的。
缺点:
- 注入多个对象时,代码会比较繁琐。
Setter注入
- 优点:方便在类实例化之后,重新对该对象进行配置或者注入。
- 缺点:不能注入一个Final修饰的属性。
@Autowied存在的问题
当同⼀类型存在多个bean时,使用@Autowired会存在问题
controller
@Controller
public class HelloController {//属性注入@Autowiredprivate Student student;@Autowired//通过注入获得student对象private UserService service;public void hello(){System.out.println("hello Spring-ioc");System.out.println(student);}
}
config
@Configuration
public class UserConfig {@Beanpublic Student student1(){Student user = new Student();user.setName("zhangsan");user.setAge(18);return user;}@Beanpublic Student student2(){Student user = new Student();user.setName("李四");user.setAge(20);return user;}}
看以看到此时就报了一个错,显示是非唯一的Bean对象。使用@Autowired注入时,Spring会先根据对象的类型去寻找Bean对象,如果找到多个相同类型的Bean就根据名称再寻找。
解决方法
Spring提供了三种解决方法分别是@Primary,@Qualifier,@Resource。
@Primary:当存在多个相同类型的Bean时,可以通过@Primary注解来缺点默认bean的实现
@Configuration
public class UserConfig {@Primary@Beanpublic Student student1(){Student user = new Student();user.setName("zhangsan");user.setAge(18);return user;}@Beanpublic Student student2(){Student user = new Student();user.setName("李四");user.setAge(20);return user;}}
通过这种方法Spring就会直接使用第一个加了@Primary注解的Bean。
@Qualifier:指定当前要注⼊的bean对象。在@Qualifier的value属性中,可以指定注入的bean 的名称。但是Qualifier必须和@Autowired一起使用
@Controller
public class HelloController {//属性注入@Qualifier("student2")@Autowiredprivate Student student;@Autowiredprivate UserService service;public void hello(){System.out.println("hello Spring-ioc");service.hello();System.out.println(student);student.hello();}
}
@Resource:是按照bean的名称进行注⼊。通过name属性指定要注⼊的bean的名称
@Controller
public class HelloController {//属性注入@Resource(name = "student2")private Student student;public void hello(){System.out.println(student);student.hello();}
}
@Autowird和@Resource的区别
- @Autowird是Spring框架提供的注解,而@Resource是JDK提供的注解
- @Autowired默认是按照类型注入,而@Resource是按照名称注入.相比于@Autowired来说,@Resource支持更多的参数设置,例如name设置,根据名称获取Bean.
@Autowird装配顺序
相关文章:

深入浅出:Spring IOCDI
什么是IOC IOC IOC(Inversion of Control),是一种设计思想,在之前的SpringMVC里就在类上添加RestController和Controller注解就是使用了IOC,这两个注解就是在Spring中创建一个对象,并将注解下的类交给Spring管理,Spr…...

PlankAssembly 笔记 DeepWiki 正交视图三维重建
manycore-research/PlankAssembly | DeepWiki PlankAssembly项目原理 这个项目是一个基于深度学习的3D重建系统,其核心原理是从三个正交视图的工程图纸中重建出3D形状的结构化程序表示。 核心技术原理 1. 问题定义 PlankAssembly旨在从三个正交视图的工程图纸中…...

某验4无感探针-js逆向
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、总体概述二、请求分析1.分析请求流程三、逆向分析四、执行验证总结一、总体概述 本文主要实现用协议过某验4无感探针,相关的链接:aHR0cHM6Ly9ndDQuZ2VldGVzdC5jb20vZGVtb3Y0L2ludmlzaWJsZS1…...
js中common.js和ECMAScript.js区别
以下是关于 CommonJS 和 ECMAScript Modules(ESM)的详细对比分析,包含底层原理和示例说明: 🧩 核心差异对比表 特性CommonJSES Modules来源Node.js 社区规范ECMAScript 语言标准加载方式动态加载(运行时解…...
C语言操作Kafka
Kafka服务 Kafka的快速入门 文档很详细,基本上几步就可以搭建一个Kafka测试环境。 下载Kafka的二进制包,然后解压。 wget https://www.apache.org/dyn/closer.cgi?path/kafka/4.0.0/kafka_2.13-4.0.0.tgz tar -xzf kafka_2.13-4.0.0.tgz cd kafka_2.…...
STM32架构解析
在嵌入式开发领域,STM32作为广泛应用的Cortex-M系列微控制器,常常被问及一个基础而深刻的问题:STM32是哈佛结构,还是冯诺依曼结构?这个问题看似简单,却涉及到计算机架构发展的历史、理论与现实的融合。 一、计算体系结构基础:冯诺依曼 vs 哈佛 1.1 冯诺依曼结构的特性…...
在线政治采购系统架构构建指南
一、系统架构设计原则 合规性优先 系统需严格遵循《中华人民共和国政府采购法》及最新修订要求,例如采购流程需满足公开招标不少于 20 日的法定时限,合同需在中标通知书发出后 30 日内签订并备案。同时,需预留接口以适应未来法律修订带来的流…...

UHF RFID无源标签的芯片供电原理
作为无源物联网技术中最基础的一环,UHF RFID无源标签已经被广泛用于商超零售、物流仓储、图书档案、防伪溯源等量非常大的应用领域,仅2021年度,全球出货量就超过200亿。在实际应用中UHF RFID无源标签的芯片是究竟依靠什么来供电的呢? UHF RFID无源标签供电特点 1.借助无线…...

【NLP入门系列一】NLP概述和独热编码
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 博主简介:努力学习的22级本科生一枚 🌟;探索AI算法,C,go语言的世界;在迷茫中寻找光芒…...

洛谷习题V^V
1.帮贡排序 解题思路:按照题意,排序模拟即可 #include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std;struct Member {string name;string position;int contribution;int level;…...

Wireshark 在 macOS 上使用及问题解决
wireshark概述 Wireshark 是被广泛使用的免费开源网络协议分析软件(network protocol analyzer)或网络数据包分析工具,它可以让你在微观层面上查看网络上发生的事情。它的主要功能是截取网络数据包,并尽可能详细地展示网络数据包…...

不同电脑同一个网络ip地址一样吗?如何更改
想象一下,你住在同一栋公寓楼里,所有住户对外共享一个统一的小区地址(类似公网IP),但每家每户又有独立的门牌号(类似内网IP)。网络世界中的IP地址也遵循这一逻辑:同一局域网内的设备…...
Qt使用智能指针
第一步:导入头文件 #include <QScopedPointer> 第二步:创建对象 .h文件 QSharedPointer<Student> m_pClass; .cpp文件 m_pClass.reset(new Student(param1,param2,...,param_n)); 第三步:绑定信号槽 connect(m_pClass.data(), &Class::sign…...

微软 Azure AI Foundry(国际版)十大重要更新
2025 年被广泛视为 “AI 智能体元年”。在过去半年,微软密集发布众多创新技术,构建起从基础设施层、开发工具层到场景应用层的完整技术矩阵,加速推动诸多具备自主决策能力的 “超级助理” 智能体落地,形成完整的 AI 赋能生态&…...
Realsense D435i 使用说明
D435i 驱动安装 及 ROS使用 Ubuntu16.04适配https://blog.csdn.net/lemonxiaoxiao/article/details/107834936 过程中遇到fatal error ; 需要添加标签。 使用下面网址的博客解决了。https://blog.csdn.net/xuzhengzhe/article/details/135407342 最终如下: target…...

PostgreSQL如何更新和删除表数据
这节说下怎样更新和删除表数据,当然认识命令了,可以问AI帮忙写。 接上节先看下天气表weather的数据,增加了杭州和西安的数据: 一.UPDATE更新命令 用UPDATE命令更新现有的行。 假设所有 杭州 5月12日的温度低了两度,用…...
【leetcode】704. 二分查找
二分查找 题目代码 题目 704. 二分查找 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。 示例 1: 输入: nums [-1,0,3,…...

Golang | 运用分布式搜索引擎实现视频搜索业务
把前面所设计好的搜索引擎引用进来开发一个简单的具体的视频搜索业务。代码结构: handler目录:后端接口,负责接收请求并返回结果,不存在具体的搜索逻辑。video_search目录:具体的搜索逻辑存放在这,包括reca…...
针对Helsinki-NLP/opus-mt-zh-en模型进行双向互翻的微调
引言 题目听起来有点怪怪的,但是实际上就是对Helsinki-NLP/opus-mt-en-es模型进行微调。但是这个模型是单向的,只支持中到英的翻译,反之则不行。这样的话,如果要做中英双向互翻就需要两个模型,那模型体积直接大了两倍…...

【笔记】Trae+Andrioid Studio+Kotlin开发安卓WebView应用
文章目录 简介依赖步骤AS(Andriod Studio)创建项目AS创建虚拟机TRAE CN 修改项目新增按键捕获功能 新增WebViewWebView加载本地资源在按键回调中向WebView注入JS代码 最终关键代码吐槽 简介 使用Trae配合Andriod Studio开发一个内嵌WebView的安卓应用, 在WebView中加载本地资源…...
Github 2025-05-30Java开源项目日报Top10
根据Github Trendings的统计,今日(2025-05-30统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Java项目10C++项目1TypeScript项目1Keycloak: 现代应用程序和服务的开源身份和访问管理解决方案 创建周期:3846 天开发语言:Java协议类型:Ap…...

Github上一些使用技巧(缩写、Issue的Highlight)自用
1. GIthub中的一些缩写 LGTM ! 最近经常看到一些迷之缩写,感觉挺有意思的,但是有时候看到一些没见过的缩写还是有点懵逼,不过缩写确实也是很方便去review,这里就记录汇总一下;顺便加了一些git的基操单词(加…...

TextIn OCR Frontend前端开源组件库发布!
为什么开源 TextIn OCR Frontend 前端组件库? 在 TextIn 社群中,我们时常接到用户反馈,调取 API 进行票据等文件批量识别后,需要另行完成前端工程,实现比对环节。为助力用户节省工程成本,TextIn 团队正式开…...
GitLens 教学(学习更新中)
GitLens 是什么? GitLens 是安装在 Visual Studio Code (VS Code) 中的一个功能极其强大的扩展程序,它直接内嵌在您的代码编辑器中,极大地增强了 VS Code 内置的 Git 功能。它的核心目标是: 深刻理解代码历史: 让您轻…...

C#中数据绑定的简单例子
数据绑定允许将控件的属性和数据链接起来——控件属性值发生改变,会导致数据跟着自动改变。 数据绑定还可以是双向的——控件属性值发生改变,会导致数据跟着自动改变;数据发生改变,也会导致控件属性值跟着自动改变。 1、数据绑定…...

VR 技术在农业领域或许是一抹新曙光
在科技日新月异的今天,VR(虚拟现实)技术已不再局限于游戏、影视等娱乐范畴,正逐步渗透到各个传统行业,为其带来全新的发展契机,农业领域便是其中之一。VR 技术利用计算机生成三维虚拟世界,给予用户视觉、听觉、触觉等多…...

【JVM】Java程序运行时数据区
运行时数据区 运行时数据区是Java程序执行过程中管理的内存区域 Java 运行时数据区组成(JVM 内存结构) Java 虚拟机(JVM)的运行时数据区由以下核心部分组成: 线程私有:程序计数器、Java虚拟机栈、本地方…...
NVIDIA英伟达describe-anything软件本地电脑安装部署完整教程
describe-anything是英伟达联合其他大学开发的一款图片视频内容分析总结软件,可通过AI描述任意图片视频选中区域内容,非常强大,下面是describe-anything本地电脑安装部署教程。 首先电脑上安装git https://github.com/git-for-windows/git/…...

计算机视觉入门:OpenCV与YOLO目标检测
计算机视觉入门:OpenCV与YOLO目标检测 系统化学习人工智能网站(收藏):https://www.captainbed.cn/flu 文章目录 计算机视觉入门:OpenCV与YOLO目标检测摘要引言技术原理对比1. OpenCV:传统图像处理与机器学…...
Java 中的 ThreadLocal 详解:从基础到源码
Java 中的 ThreadLocal 详解:从基础到源码 引言 在 Java 多线程编程中,ThreadLocal是一个经常被提及的概念。它提供了一种线程局部变量的机制,使得每个线程都可以独立地存储和访问自己的变量副本,而不会与其他线程产生冲突。本文…...