【Spring专题】Spring底层核心原理解析
目录
- 前言
- 阅读导航
- 前置知识
- Q1:你能描述一下JVM对象创建过程吗?
- Q2:Spring的特性是什么?
- 前置知识总结
- 课程内容
- 一、Spring容器的启动
- 二、一般流程推测
- 2.1 扫描
- 2.2 IOC
- 2.3 AOP
- 2.4 小结
- 三、【扫描】过程简单推测
- 四、【IOC】过程简单推测
- 4.1 推断构造方法过程细讲
- 五、【AOP】过程简单推测
- 六、Spring事务
- 学习总结
前言
Spring啊,可以说是我们大部分Java玩家【最熟悉的陌生人】了吧。八个字形容:似懂非懂,会也不会
你说简单应用,我们大家都会,那真要展开说两句的话,那只能来这么两句:这是第一句,接着是第二句,好了我说完了。
但是啊xdm,据说Spring是一份非常非常非常优秀的源码,不但有丰富的设计模式应用场景,代码写的也很优美,有条理,所以非常推荐大家学习。除了能在日常装逼以外,还能丰富一下见识,提升自己写代码的能力。
阅读导航
阅读对象:有过Spring开发经验的人
前置知识
Q1:你能描述一下JVM对象创建过程吗?
答:看图说话:
- 类加载:在使用一个类之前,Java虚拟机需要先将类的字节码加载到内存中。类加载是Java虚拟机的核心过程,它负责查找类的字节码文件并加载到内存的方法区。类加载包括加载、验证、准备、解析和初始化这五个阶段。
- 分配内存:在类加载完成后,Java虚拟机会为对象分配内存空间。内存分配通常在堆(Heap)上进行,但也有一些特殊情况下的对象可以在栈(Stack)上分配内存,例如线程栈上的局部对象。
- 实例化(初始化零值):在分配内存后,Java虚拟机会将对象的内存空间初始化为零值。这包括基本类型的默认值(例如0、false等)和引用类型的默认值(null)。
- 设置对象头:Java对象在内存中的布局包括对象头和实例数据两部分。对象头存储了一些元数据,如对象的哈希码、锁状态等。在对象创建过程中,Java虚拟机会设置对象头的值。
- 执行构造方法:对象创建的最后一步是执行构造方法。构造方法用于初始化对象的实例数据,并执行其他必要的初始化操作。构造方法可以是类的默认构造方法,也可以是自定义的构造方法。
- 返回对象引用:对象创建完成后,Java虚拟机会返回一个指向该对象的引用。通过引用,程序可以操作对象的属性和方法。
(PS:为什么要问这个问题?因为Spring是IOC技术,就算再怎么玩出花来,他也要按照这个基本流程来创建对象。只不过,可以提前告知大家的是,SpringIOC在这个流程之中,新增了很多槽点,通过热插拔的方式,丰富了IOC的功能!)
Q2:Spring的特性是什么?
答:Spring的特性就是IOC跟AOP两大概念!甚至可以这么说:Spring就是实现了AOP技术的IOC容器。(容器,容器,容器)
Q3:什么是IOC,什么是AOP?
答:下面答案来源于百度【文心一言】:
- IOC(控制反转)是一种设计模式(思想),它允许将对象的创建和管理交给Spring容器来处理,而不是在代码中直接创建对象。通过使用IOC,可以将对象的依赖关系从代码中解耦,使得代码更加灵活、可维护和可测试。
- AOP(面向切面编程)也是一种设计模式(思想),它通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态添加功能。AOP解决了面向对象编程中无法解决的问题,例如事务管理、安全性、日志记录等。
Spring框架通过实现IOC和AOP,使得程序更加模块化、灵活和易于维护。同时,Spring还提供了许多其他模块和功能,如DAO、ORM、WebMVC等,使得它成为一个功能强大的Java开发框架。
前置知识总结
从上面的问题里面,我们提到了一个很重要的东西,即:Spring就是实现了AOP技术的IOC容器。并且,也概括地描述了IOC跟AOP的概念。既然我们也知道了,IOC其实也管理了对象的创建,那么说到对象创建,肯定也离不开我们在Q1说的,对象创建的过程。而且,无论对象怎么创建,谁创建,都没办法离开上面的流程的。
事实上,可以提前告诉大家的是,IOC在创建对象的过程,无非就是在上面的对象创建流程中,丰富了一些细节,新增了一些拓展点,为Spring功能实现提供支持。
课程内容
为了展开对Spring源码的研究,我们这里线大致地串讲一下Spring的一些核心知识点,让大家对Spring的底层一些基础逻辑有个清晰的认知。
一、Spring容器的启动
我想,经历过SSM/SSH时代的朋友,对下面的代码都不会陌生:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();
System.out.println(userService);
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsd"><!-- <import resource="引入其他bean xml配置文件" />--><bean id="userService" class="org.example.spring.bean.UserService"/>
</beans>
如果真的很陌生也没关系,下面这个可能就相对熟悉一点了:(后面也会围绕这个启动方式的Spring讲解。除了是下面的比较主流,也因为,下面这种方式使用更广、更新,内容相对丰富点!)
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();
System.out.println(userService);
@Component
public class UserService {public void test() {System.out.println("这是一个测试方法");}
}
哈哈,我估计很多直接进入了Java【SpringBoot】时代的朋友,可能连上面这个都没看到过。
那上面段代码是干啥的呢?很简单,就是启动一个Spring容器而已。上面两个不同的启动方式,也仅仅是Bean注册方式不一样。比如前者是通过读取xml
里面的<bean>
标签定义,后者是读取的注解式Bean。
到这里,想问大家一个问题,那就是,通过上面第二种方式的代码,你发现了什么?我的发现是:我仅仅只是调用了一行代码,就可以开始使用Spring定义的Bean了,什么依赖注入,AOP啥的,我都没管,直接就可以了。这证明了啥?其实很粗浅,也有点废话,那就是证明:通过这一行代码,里面就帮我完成了所有我们平时使用过的,Spring的基础能力。
二、一般流程推测
根据我们之前学习过Spring相关的操作,简单推测一下,这一行代码里面干了什么。
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
2.1 扫描
首先是第一点【扫描】。我们在项目中,写了这么多Bean,或者说,我项目里面这么多类,哪些是Bean,哪些是普通类,Spring是怎么识别到的?其实道理很简单的,Spring它也没那么智能,想要获取这个类的信息,Spring肯定要【亲自】去看一眼,才能知道这个类的具体信息。你有这么多少个文件,他就要扫描多少个类。关键代码如下:
// 定义需要扫描的基础包名
@ComponentScan("org.tuling.spring")
public class AppConfig {
}
2.2 IOC
扫描完了所有的文件,那基本上Spring已经能确定,哪些是Bean,哪些是普通类了。接下来,就可以开始创建Bean了,这里,就是所谓的IOC过程
2.3 AOP
AOP肯定是发生在IOC之后的,如果你们了解设计模式里面的【代理模式】的话,理解这一点并不难。毕竟,如果目标对象功能不完整,代理对象的功能也会收到影响。
2.4 小结
三、【扫描】过程简单推测
之前我们也说了,扫描,就是需要Spring亲自去看一看,哪些是需要被创建Bean的,哪些是不需要的。就拿我们举例用的new AnnotationConfigApplicationContext(AppConfig.class)
来说,大概步骤如下:(简单推测,不详)
- 它需要先看
AppConfig.class
,读取扫描包的基础路径 - 根据上一步读到的基础路径,遍历包下所有的文件,如果类上存在
@Component
、@Service
等注解,则确认为是一个Bean - 筛选完后将读取的Bean信息记录下来,比如说存到一个Map里面,方便后续遍历
四、【IOC】过程简单推测
IOC过程,其实在Spring中有个比较专业的术语,叫做:Bean的生命周期。简单的几个字,包含了很多内容。在此之前,大家先看一看【前置知识】里面【JVM对象创建过程】,加深一下印象。
其中有:
- 新建一个Bean对象。说到新建Bean对象,无论如何都绕不开构造方法的调用。我有多个构造函数,那我应该使用哪个呢?这在Spring内部,叫做:【推断构造方法】;
- Bean对象创建好了之后,当然不能忘了我们的:【依赖注入】;
- 有一点大家可能用的会比较少,但如果有过经验的同学可能会比较熟悉,比如:
ApplicationContextAware
,我们可以直接直译,叫做:ApplicationContext感知,感知ApplicationContext,所以通过设置,我们就可以在Bean里面获取到这个组件了; init
方法。如果大家有过@PostConstruct
以及InitializingBean
使用经验的话,或许知道,创建完Bean之后,在返回Bean之前,还有这一步动作。事实上,根据我们在前言里面说的【IOC在创建对象的过程,无非就是在上面的对象创建流程中,丰富了一些细节,新增了一些拓展点】,所以,在IOC里面还有很多切入点,比如:实例化前、实例化后;初始化前、初始化后等等- 最后,就是进入AOP的流程了。如果需要AOP,则进行AOP,并且返回AOP对象;否则直接返回已经创建好的对象
另外需要注意的是,Bean对象创建出来后:
- 如果当前Bean是单例Bean,那么会把该Bean对象存入一个Map<String, Object>,Map的key为beanName,value为Bean对象。这样下次getBean时就可以直接从Map中拿到对应的Bean对象了(实际上,在Spring源码中,这个Map就是单例池);
- 如果当前Bean是原型Bean,那么后续没有其他动作,不会存入一个Map,下次getBean时会再次执行上述创建过程,得到一个新的Bean对象。
4.1 推断构造方法过程细讲
Spring在基于某个类生成Bean的过程中,需要利用该类的构造方法来实例化得到一个对象,但是如果一个类存在多个构造方法,Spring会使用哪个呢?
Spring的判断逻辑如下:
- 如果一个类只有一个构造函数,不管构造函数是有参,还是无参,Spring都会使用这个构造函数创建对象,因为没得选了;
- 如果这个类存在多个构造函数:
- 如果存在无参构造函数,则使用无参构造函数。因为在Java里面,无参构造函数本身就具有默认的意思在里面;
- 如果没有无参构造函数,则看多个无参构造函数,哪个有
@Autowired
修饰,有就选择;没有就只能报错了
还有一个问题。如果Spring选择了一个有参的构造方法,Spring在调用这个有参构造方法时,需要传入参数,那这个参数是怎么来的呢?答案是:Spring会根据入参的类型和入参的名字去Spring中找Bean对象。
3. 先根据入参类型找,如果只找到一个,那就直接用来作为入参;
4. 如果根据类型找到多个,则再根据入参名字来确定唯一一个;
5. 最终如果没有找到,则会报错,无法创建当前Bean对象。
五、【AOP】过程简单推测
AOP就是进行动态代理,在创建一个Bean的过程中,Spring在最后一步(放入单例池之前)会去判断当前正在创建的这个Bean是不是需要进行AOP,如果需要则会进行动态代理。
那么,如何判断一个Bean是否需要被AOP代理呢?步骤如下:
- 找出所有的切面Bean(切面也是Bean来的,或者叫做:特殊的Bean)
- 遍历切面中的每个方法,看是否写了@Before、@After等注解(通知)
- 如果写了,则判断所对应的Pointcut是否和当前Bean对象的类是否匹配
- 如果匹配则表示当前Bean对象有匹配的的Pointcut,表示需要进行AOP
利用cglib进行AOP的大致流程:(看上面的代理范式大概就知道了)
- 新增一个代理类XxxProxy,继承自被代理对象XxxTarget,并且持有一个XxxTarget成员变量(这个成员变量需要经过一个Bean的声明周期,即,完成了IOC等)
- 在代理类中重写父类的方法
- 执行代理类的方法时,调用的代理类的方法,但同时也需要执行切面的逻辑
然后这里给大家一个【代理模式】的范式:
// 被代理对象
public class ProxyTarget {public void run() {System.out.println("这是普通对象的run");}
}// 代理对象
public class ProxyModel extends ProxyTarget {private ProxyTarget proxyTarget;public void setProxyTarget(ProxyTarget proxyTarget) {this.proxyTarget = proxyTarget;}@Overridepublic void run() {System.out.println("我代理对象可以在这里做加强---1");super.run();System.out.println("我代理对象也可以在这里做加强---2");}
}
六、Spring事务
当我们在某个方法上加了@Transactional注解后,就表示该方法在调用时会开启Spring事务,而这个方法所在的类所对应的Bean对象会是该类的代理对象。
Spring事务的代理对象执行某个方法时的步骤:
- 判断当前执行的方法是否存在@Transactional注解
- 如果存在,则利用事务管理器(TransactionMananger)新建一个数据库连接
- 修改数据库连接的autocommit为false
- 执行target.test(),执行程序员所写的业务逻辑代码,也就是执行sql
- 执行完了之后如果没有出现异常,则提交,否则回滚
Spring事务是否会失效的判断标准:某个加了@Transactional注解的方法被调用时,要判断到底是不是直接被代理对象调用的,如果是则事务会生效,如果不是则失效。(PS:这一点很容易被疏忽)
另外,还有个经典例子,那就是@Bean
在有跟没有@Configuration
的时候,结果是不一样的,如下:
声明Bean的方法:
@ComponentScan("org.tuling.spring")
@Configuration
public class AppConfig {@Beanpublic UserService userService() {return new UserService(walletService());}@Beanpublic UserService userService1() {return new UserService(walletService());}@Beanpublic WalletService walletService() {return new WalletService();}
}// UserService声明
public class UserService {private WalletService walletService;public UserService() {}public UserService(WalletService walletService) {this.walletService = walletService;}public WalletService getWalletService() {return walletService;}/*** 自我介绍*/public void selfIntroduction() {System.out.println("你好,我是阿通,我有好多钱");walletService.showMyBalance();}
}
大家看上面的声明Bean的方法,按照设想,WalletService
肯定也是一个单例嘛,所以,userService
跟userService1
持有的walletService
对象肯定是一样的。
调用方法:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = (UserService)context.getBean("userService");System.out.println(userService);System.out.println(userService.getWalletService());System.out.println("--------------------------------");UserService userService1 = (UserService)context.getBean("userService1");System.out.println(userService1);System.out.println(userService1.getWalletService());
结果输出如下:
org.tuling.spring.bean.UserService@2c34f934
org.tuling.spring.bean.WalletService@12d3a4e9
--------------------------------
org.tuling.spring.bean.UserService@240237d2
org.tuling.spring.bean.WalletService@12d3a4e9
看结果,没什么问题,如期输出。但如果我们把声明Bean的方法里面的@Configuration
去掉,结果会变成这样:
@ComponentScan("org.tuling.spring")
//@Configuration
public class AppConfig {@Beanpublic UserService userService() {return new UserService(walletService());}@Beanpublic UserService userService1() {return new UserService(walletService());}@Beanpublic WalletService walletService() {return new WalletService();}
}
org.tuling.spring.bean.UserService@710726a3
org.tuling.spring.bean.WalletService@646007f4
--------------------------------
org.tuling.spring.bean.UserService@481a15ff
org.tuling.spring.bean.WalletService@78186a70
为什么,只是简单注释了一个@Configuration
结果就不一样了呢?分析如下:
@Bean
注解可以将方法返回的对象注册为一个 Bean,并且该 Bean 会被 Spring 容器管理。仅此而已- 所以,在
userService()
方法重调用walletService()
方法,实际上就是一个普通Java调用而已,肯定会重新new WalletService()
- 而被
@Configuration
注解之后,所有方法都将被代理(暂时还没找到源码证据,等后面我看懂了再附上)
学习总结
- 简单学习了Spring启动的流程
- 通过一些常见Spring操作的串讲,大概了解了一下IOC和AOP的大致流程
相关文章:

【Spring专题】Spring底层核心原理解析
目录 前言阅读导航前置知识Q1:你能描述一下JVM对象创建过程吗?Q2:Spring的特性是什么?前置知识总结 课程内容一、Spring容器的启动二、一般流程推测2.1 扫描2.2 IOC2.3 AOP 2.4 小结三、【扫描】过程简单推测四、【IOC】过程简单推…...

出于网络安全考虑,印度启用本土操作系统”玛雅“取代Windows
据《印度教徒报》报道,印度将放弃微软系统,选择新的操作系统和端点检测与保护系统。 备受期待的 "玛雅操作系统 "将很快用于印度国防部的数字领域,而新的端点检测和保护系统 "Chakravyuh "也将一起面世。 不过…...
tensotflow中tf.title()和tf.broadcast()
tf.tile() 和 tf.broadcast_to() 都是 TensorFlow 中用于张量复制的函数,但它们的实现方式和使用场景略有不同。 tf.tile() 函数的定义如下: tf.tile(input, multiples, nameNone) 其中,input 表示要复制的张量,multiples 表示…...

想要延长Macbook寿命?这六个保养技巧你必须get!
Mac作为我们工作生活的伙伴,重要性不需要多说。但在使用的过程中,我们总会因不当操作导致Mac出现各种问题。 要想它长久的陪伴,平时的维护与保养自然不能少,Mac的保养很重要的两点就是硬件保养和电脑系统保养,硬件保养…...

mysql基础之触发器的简单使用
1.建立学生信息表 -- 触发器 -- 建立学生信息表 create table s1(id int unsigned auto_increment,name varchar(30),score tinyint unsigned,dept varchar(50),primary key(id) );2.建立学生补考信息表 -- 建立学生补考信息表 create table s2 like s1;3.建立触发器…...

Spring Boot 配置多数据源【最简单的方式】
Druid连接池 Spring Boot 配置多数据源【最简单的方式】 文章目录 Druid连接池 Spring Boot 配置多数据源【最简单的方式】 0.前言1.基础介绍2.步骤2.1. 引入依赖2.2. 配置文件2.3. 核心源码Druid数据源创建器Druid配置项 DruidConfig 3.示例项目3.1. pom3.1.1. 依赖版本定义3.…...

1、Java简介+DOS命令+编译运行+一个简单的Java程序
Java类型: JavaSE 标准版:以前称为J2SE JavaEE 企业版:包括技术有:Servlet、Jsp,以前称为J2EE JavaME 微型版:以前称为J2ME Java应用: Android平台应用。 大数据平台开发:Hadoo…...
Linux 文件与目录管理,Linux 文件内容查看
目录 Linux 文件与目录管理 处理目录的常用命令 ls (列出目录) mv (移动文件与目录,或修改名称)...

Mysql按小时进行分组统计数据
目录 前言 按1小时分组统计 按2小时分组统计 按X小时分组统计 前言 统计数据时这种是最常见的需求场景,今天写需求时发现按2小时进行分组统计也特别简单,特此记录下。 按1小时分组统计 sql: select hour(pass_time) …...
springboot3日志配置
简介 Spring 使用commons-logging作为内部日志,但是底层日志实现是开放的,可以对接其他日志框架 spring5以及以后common-logging被spring直接自己写了 支持jul, log4j2,logback,springBoot提供了默认的控制台输出配置,也可以配置…...
7款轻量级平面图设计软件推荐
平面图设计的痕迹体现在日常生活的方方面面,如路边传单、杂志、产品包装袋或手机开屏海报等,平面设计软件层出不穷。Photoshop是大多数平面图设计初学者的入门软件,但随着设计师需求的不断提高,平面图设计软件Photoshop逐渐显示出…...

SpringCloud实用篇5——elasticsearch基础
目录 1.初识elasticsearch1.1 了解ES1.1.1 elasticsearch的作用1.1.2 ELK技术栈1.1.3 elasticsearch和lucene1.1.4 总结 1.2.倒排索引1.2.1.正向索引1.2.2.倒排索引1.2.3.正向和倒排 1.3 es的一些概念1.3.1 文档和字段1.3.2 索引和映射1.3.3 mysql与elasticsearch 1.4 部署单点…...

SpringCloud整体架构概览
什么是SpringCloud 目标 协调任何服务,简化分布式系统开发。 简介 构建分布式系统不应该是复杂的,SpringCloud对常见的分布式系统模式提供了简单易用的编程模型,帮助开发者构建弹性、可靠、协调的应用程序。SpringCloud是在SpringBoot的基…...

(el-switch)操作(不使用 ts):Element-plus 中 Switch 将默认值修改为 “true“ 与 “false“(字符串)来控制开关
Ⅰ、Element-plus 提供的 Switch 开关组件与想要目标情况的对比: 1、Element-plus 提供 Switch 组件情况: 其一、Element-ui 自提供的 Switch 代码情况为(示例的代码): // Element-plus 自提供的代码: // 此时是使用了 ts 语言环…...

AI绘画网站都有哪些比较好用?
人工智能绘画网站是一种利用人工智能技术进行图像处理和创作的网站。这些绘画网站通常可以帮助艺术家以人工智能绘画的形式快速生成有趣、美丽和独特的绘画作品。无论你是专业的艺术家还是对人工智能绘画感兴趣的普通人,人工智能绘画网站都可以为你提供新的创作灵感…...

Android应用开发(35)SufaceView基本用法
Android应用开发学习笔记——目录索引 参考Android官网:https://developer.android.com/reference/android/view/SurfaceView 一、SurfaceView简介 SurfaceView派生自View,提供嵌入视图层次结构内部的专用绘图表面,SurfaceView可以在主线程之…...

原生JS手写扫雷小游戏
场景 实现一个完整的扫雷游戏需要一些复杂的逻辑和界面交互。我将为你提供一个简化版的扫雷游戏示例,帮助你入门。请注意,这只是一个基本示例,你可以根据自己的需求进行扩展和改进。 思路 创建游戏板(Grid)࿱…...

网络安全进阶学习第十五课——Oracle SQL注入
文章目录 一、Oracle数据库介绍二、Oracle和MySQL的语法差异:三、Oracle的数据库结构四、Oracle的重点系统表五、Oracle权限分类1、系统权限2、实体权限3、管理角色 六、oracle常用信息查询方法七、联合查询注入1、order by 猜字段数量2、查数据库版本和用户名3、查…...
线程池死循环系统卡住
案例: 同一个线程池。 首先核心线程数是8,我一次提交了 > 8个主任务,然后主任务又各自开启了几个子任务。 所以子任务没有核心线程来跑,只能放进阻塞队列等。 但主任务又等待子任务的结果,不释放占用线程ÿ…...

多用户微商城多端智慧生态电商系统搭建
多用户微商城多端智慧生态电商系统的搭建步骤如下: 系统规划:在搭建多用户微商城多端智慧生态电商系统之前,需要进行系统规划。包括确定系统的目标、功能、架构、技术选型、开发流程等方面。市场调研:进行市场调研,了…...

智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...

让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...

企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...

中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...