当前位置: 首页 > news >正文

SpringDataJpa源码分析

我们在定义Repository的时候通常定义的时一个接口,而并没有去实现这个接口,那么Jpa是如何让开发者无需自己实现接口就可以使用Repository去操作数据库?
动态代理!!!

Repository原理

试想一下JPA是如何做的?

  1. 通过动态代理将接口实例化成对应的类。
  2. 解析方法名或根据指定的方法名
  3. 并根据得到的结果转换为数据库操作

尝试写一个示例

  • 自定义一个Repository
public interface CustomRepository<T, ID> extends Repository<T, ID> {/*** 查询前num条数据* @param num* @return*/List<T> findByFirst(Long num);
}
  • 对自定义Repository的代理逻辑
/*** @author yuanmengfan(mf.yuan @ qq.com) on 2024/8/7 23:26* 自定义Repository的代理*/
@Data
public class CustomRepositoryInvocationHandler implements InvocationHandler {// 用于操作数据库private EntityManager em;// 知晓操作那个对象private Class<?> entityClass;public CustomRepositoryInvocationHandler(EntityManager em, Class<?> entityClass) {this.em = em;this.entityClass = entityClass;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) {Object result = null;String name = method.getName();switch (name) {// 根据方法名定义不同操作数据库的逻辑case "findByFirst":CriteriaBuilder cn = em.getCriteriaBuilder();CriteriaQuery query = cn.createQuery(entityClass);Root from = query.from(entityClass);result = em.createQuery(query.select(from)).setMaxResults(Math.toIntExact((Long) args[0])).getResultList();break;}return result;}
}
  • 测试下创建出来的代理对象是否能正常运行
@Testpublic void test() {// 初始化IOC容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(JpaConfig.class);LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = applicationContext.getBean(LocalContainerEntityManagerFactoryBean.class);// 创建EntityManagerEntityManager em = localContainerEntityManagerFactoryBean.getNativeEntityManagerFactory().createEntityManager();Class<?> proxyClass = UserCustomRepository.class;// 获取接口泛型信息ParameterizedType parameterizedType = (ParameterizedType) proxyClass.getGenericInterfaces()[0];Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();Class entityClass = (Class) actualTypeArguments[0];Class idClass = (Class) actualTypeArguments[1];// 通过动态代理创建对象UserCustomRepository proxyInstance = (UserCustomRepository) Proxy.newProxyInstance(proxyClass.getClassLoader(), new Class[]{proxyClass}, new CustomRepositoryInvocationHandler(em, entityClass));System.out.println(proxyInstance.findByFirst(5L));}

image.png
已经可以根据我们指定的类型来操作数据库了。

源码跟踪验证

@Test
public void test(){// 初始化IOC容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(JpaConfig.class);UserJpaRepository bean = applicationContext.getBean(UserJpaRepository.class);Optional<TUser> byId = bean.findById(5L);
}

能发现这里在IOC容器中的是代理对象。
image.png
可以明显的看出这里是使用的JDK动态代理。
image.png
这里可以看见实际上我们的对象是SimpleJpaRepository
image.png
实际调用findById也是通过EntityManage去操作数据库
image.png

Spring是如何整合Jpa呢?

疑问

怎么知道要注册那些Repository

@ComponentScan+@Component

// 添加 
@ComponentScan(basePackages = "com.mfyuan")
public class JpaConfig{}// 添加
@Component
public interface UserCustomRepository extends CustomRepository<TUser, Long> {}@Test
public void test1() {// 初始化IOC容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(JpaConfig.class);// 尝试从容器中获取BeanUserCustomRepository bean = applicationContext.getBean(UserCustomRepository.class);
}

image.png

为什么还是拿不到这个Bean呢?
因为Repository是接口不是类,不会被注册到IOC容器中。在扫描成BeanDefinition是被过滤掉了。
ConfigurationClassParser#doProcessConfigurationClass
ComponentScanAnnotationParser#parse 处理@Component的解析
ClassPathBeanDefinitionScanner#doScan
#findCandidateComponents
#scanCandidateComponents
#isCandidateComponent(metadataReader) 判断是否包含@Component注解
image.png
#isCandidateComponent(sbd) 判断是否为接口或者抽象类
image.png
:::
如何解决不扫描接口的问题呢?

  1. ClassPathBeanDefinitionScanner:自定义一个扫描器(让接口也可以别扫描到)。
  2. BeanDefinitionRegistryPostProcessor:在BeanDefinition注册的过程中可以允许进干预。
    :::
public class CustomClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {public CustomClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {super(registry);// 添加过滤器只扫描实现CustomRepository接口的类}/*** 允许接口被扫描** @param beanDefinition the bean definition to check* @return*/@Overrideprotected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {AnnotationMetadata metadata = beanDefinition.getMetadata();return metadata.isInterface();}
}
@Component
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {CustomClassPathBeanDefinitionScanner definitionScanner = new CustomClassPathBeanDefinitionScanner(registry);// 清空原有filter definitionScanner.resetFilters(false);// 添加过滤器只扫描实现CustomRepository接口的类definitionScanner.addIncludeFilter(new AssignableTypeFilter(CustomRepository.class));// 能将 com.mfyuan.repository 下的接口也进行注册了definitionScanner.scan("com.mfyuan.repository");}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {BeanDefinitionRegistryPostProcessor.super.postProcessBeanFactory(beanFactory);}
}

到现在我们的接口就可以被扫描成BeanDefinition,但是又遇到一个问题?
image.png
与之前的错误不同的是已经不再提前找不到对应类型的Bean了,而是接口不能实例化的。
:::

如何将对应的Repository接口注入到容器中?

可以将通过动态代理后的对象放入到容器中,这里就解决了接口不能实例化的问题。
但是不可能一个一个去创建动态代理的对象吧,这样想想就很痛苦。
使用FactoryBean来定义创建过程。与前面Repository原理结合。
:::

@Data
public class CustomRepositoryFactoryBean implements FactoryBean {private Class<?> repositoryInterface;@Autowiredprivate LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean;public CustomRepositoryFactoryBean(Class<?> repositoryInterface) {this.repositoryInterface = repositoryInterface;}/*** FactoryBean的特性这个方法的返回值是注册到容器中对象*/@Overridepublic Object getObject() {// 创建EntityManagerEntityManager em = localContainerEntityManagerFactoryBean.getNativeEntityManagerFactory().createEntityManager();// 获取接口泛型信息ParameterizedType parameterizedType = (ParameterizedType) repositoryInterface.getGenericInterfaces()[0];Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();Class<?> entityClass = (Class<?>) actualTypeArguments[0];// Class idClass = (Class) actualTypeArguments[1];// 通过动态代理创建对象return Proxy.newProxyInstance(repositoryInterface.getClassLoader(), new Class[]{repositoryInterface}, new CustomRepositoryInvocationHandler(em, entityClass));}/*** FactoryBean的特性这个方法的返回值是注册到容器的对象类型*/@Overridepublic Class<?> getObjectType() {return repositoryInterface;}
}

重写CustomClassPathBeanDefinitionScanner#doScan 对扫描到的BeanDefinition进行修改

public class CustomClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {// .....@SneakyThrows@Overrideprotected Set<BeanDefinitionHolder> doScan(String... basePackages) {// 这里拿到的就行自定义扫描器 扫出来的所有BeanDefinitionSet<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {ScannedGenericBeanDefinition beanDefinition = (ScannedGenericBeanDefinition) beanDefinitionHolder.getBeanDefinition();// 得到扫描到的repositoryClassString repositoryClassName = beanDefinition.getBeanClassName();// 设置BeanDefinition的Class为FactoryBean// mybatis 这里也是这样做的 只不过是新注册了一个BeanDefinitionbeanDefinition.setBeanClass(CustomRepositoryFactoryBean.class);// 添加一个构造参数beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(repositoryClassName);}return beanDefinitionHolders;}
}

测试

@Test
public void test1() {// 初始化IOC容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(JpaConfig.class);// 尝试从容器中获取BeanUserCustomRepository bean = applicationContext.getBean(UserCustomRepository.class);System.out.println(bean.findByFirst(5L));
}

能够走自定义Repository里面的方法了。

image.png解释

CustomRepository:相当于一个顶层接口,他去定义对应的统一的数据库操作。类似JpaRepositoryCrudRepository
CustomRepositoryInvocationHandler:相当于是对顶层接口所有的方法进行了一个实现。类似SimpleJpaRepository

源码验证

// 导入JpaRepositoriesRegistrar
@Import(JpaRepositoriesRegistrar.class)
public @interface EnableJpaRepositories {}// 实现了ImportBeanDefinitionRegistrar就代表有动态注册BeanDefinition的能力
class JpaRepositoriesRegistrar extends RepositoryBeanDefinitionRegistrarSupport {}public abstract class RepositoryBeanDefinitionRegistrarSupportimplements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry,BeanNameGenerator generator) {// ...// 委托RepositoryConfigurationDelegate进行注册RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(configurationSource, resourceLoader,environment);delegate.registerRepositoriesIn(registry, extension);}@Overridepublic Streamable<BeanDefinition> getCandidates(ResourceLoader loader) {// 定义的扫描器RepositoryComponentProvider scanner = new RepositoryComponentProvider(getIncludeFilters(), registry);scanner.setConsiderNestedRepositoryInterfaces(shouldConsiderNestedRepositories());scanner.setEnvironment(environment);scanner.setResourceLoader(loader);getExcludeFilters().forEach(scanner::addExcludeFilter);// 查找满足条件的组件return Streamable.of(() -> getBasePackages().stream()//.flatMap(it -> scanner.findCandidateComponents(it).stream()));}
}public class RepositoryConfigurationDelegate {// ...public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry,RepositoryConfigurationExtension extension) {Collection<RepositoryConfiguration<RepositoryConfigurationSource>> configurations = extension.getRepositoryConfigurations(configurationSource, resourceLoader, inMultiStoreMode);}
}public abstract class RepositoryConfigurationExtensionSupport implements RepositoryConfigurationExtension {public <T extends RepositoryConfigurationSource> Collection<RepositoryConfiguration<T>> getRepositoryConfigurations(T configSource, ResourceLoader loader, boolean strictMatchesOnly) {// ...// 调用RepositoryBeanDefinitionRegistrarSupport.getCandidates 获得合适的对象for (BeanDefinition candidate : configSource.getCandidates(loader)) {}// ...}
}class RepositoryComponentProvider extends ClassPathScanningCandidateComponentProvider {public RepositoryComponentProvider(Iterable<? extends TypeFilter> includeFilters, BeanDefinitionRegistry registry) {// ....if (includeFilters.iterator().hasNext()) {for (TypeFilter filter : includeFilters) {addIncludeFilter(filter);}} else {// 添加 是Repository接口的类super.addIncludeFilter(new InterfaceTypeFilter(Repository.class));// 添加定义了RepositoryDefinition注解的类super.addIncludeFilter(new AnnotationTypeFilter(RepositoryDefinition.class, true, true));}// 排除带有NoRepositoryBean注解的类addExcludeFilter(new AnnotationTypeFilter(NoRepositoryBean.class));}@Overridepublic Set<BeanDefinition> findCandidateComponents(String basePackage) {// 这里就走到的Spring的findCandidateComponents 里面// 关键的两个方法则是isCandidateComponent(metadataReader) 与 isCandidateComponent(sbd)Set<BeanDefinition> candidates = super.findCandidateComponents(basePackage);// ...}// 这样的话就能将Repository接口及@RepositoryDefinition扫描成BeanDefinition了protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {for (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return false;}}for (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return isConditionMatch(metadataReader);}}return false;}@Overrideprotected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {boolean isNonRepositoryInterface = !ClassUtils.isGenericRepositoryInterface(beanDefinition.getBeanClassName());boolean isTopLevelType = !beanDefinition.getMetadata().hasEnclosingClass();boolean isConsiderNestedRepositories = isConsiderNestedRepositoryInterfaces();return isNonRepositoryInterface && (isTopLevelType || isConsiderNestedRepositories);}
}public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>, S, ID>implements InitializingBean, RepositoryFactoryInformation<S, ID>, FactoryBean<T>, BeanClassLoaderAware,BeanFactoryAware, ApplicationEventPublisherAware {public void afterPropertiesSet() {// ...// 传入repositoryInterface来得到代理对象this.repository = Lazy.of(() -> this.factory.getRepository(repositoryInterface, repositoryFragmentsToUse));// ...}
}public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, BeanFactoryAware {public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments) {// ...// 这里就是最终创建代理对象的地方,会判断是JDK动态代理还是CGLIB代理T repository = (T) result.getProxy(classLoader);// ...return repository;}
}

  1. @EnableJpaRepositories(basePackages = "com.mfyuan.repository")
  2. @Import(JpaRepositoriesRegistrar.class)
  3. JpaRepositoriesRegistrar实现ImportBeanDefinitionRegistrar拥有动态注册的能力与BeanDefinitionRegistryPostProcessor一样
  4. 自定义扫描器RepositoryComponentProvide添加扫描Repository口及RepositoryDefinition注解的includeFilterNoRepositoryBean注解的excludeFilter
  5. 将扫描成功的获选Bean的信息创建成BeanDefinition并修改它的BeanClassName(实际是JpaRepositoryFactoryBean
  6. JpaRepositoryFactoryBean是一个BeanFactory,会为我们创建出一个动态代理的对象并放入到IOC容器中。

相关文章:

SpringDataJpa源码分析

我们在定义Repository的时候通常定义的时一个接口&#xff0c;而并没有去实现这个接口&#xff0c;那么Jpa是如何让开发者无需自己实现接口就可以使用Repository去操作数据库&#xff1f; 动态代理&#xff01;&#xff01;&#xff01; Repository原理 试想一下JPA是如何做的…...

卷积神经网络 - 卷积神经网络与深度学习的历史篇

序言 卷积神经网络&#xff08; Convolutional Neural Networks, CNN \text{Convolutional Neural Networks, CNN} Convolutional Neural Networks, CNN&#xff09;与深度学习作为人工智能领域的两大重要分支&#xff0c;其发展历程充满了探索与突破。深度学习&#xff0c;作…...

初识 Floodfall 算法

文章目录 **一、Floodfall 算法的概述****二、深度优先搜索&#xff08;DFS&#xff09;和广度优先搜索&#xff08;BFS&#xff09;在 Floodfall 算法中的应用****三、算法的基本原理****四、应用场景** 一、Floodfall 算法的概述 Floodfall 算法通常用于解决与区域填充、图的…...

[Linux] LVM挂载的硬盘重启就掉的问题解决

问题&#xff1a;系统重启后挂在逻辑卷的盘会掉&#xff08;必现&#xff09; 环境&#xff1a;SUSE Linux 11 SP4 原因&#xff1a;boot.lvm是关闭的 解决&#xff1a;boot.lvm设置开启 参考资料&#xff1a; linux下lvm状态Not avaliable问题排查及处理(常见Suse操作系统…...

YOLOv8改进 | 主干网络 | 用EfficientNet卷积替换backbone【教程+代码 】

秋招面试专栏推荐 :深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡 专栏目录 :《YOLOv8改进有效涨点》专栏介绍 & 专栏目录 | 目前已有80+篇内容,内含各种Head检测头、损失函数Loss、…...

数据库规范化设计 5大基本原则

规范化设计原则是数据库设计的基本原则&#xff0c;有助于减少数据冗余&#xff0c;提高数据一致性和完整性&#xff0c;简化数据管理&#xff0c;增强数据安全性&#xff0c;对整个开发项目至关重要。而缺乏规范化设计会导致数据冗余&#xff0c;增加存储成本&#xff0c;引发…...

【nginx】解决k8s中部署nginx转发不会自动更新域名解析启动失败的问题

文章目录 1. 问题2.解决办法3.扩展说明3.1 DNS解析阶段划分3.2 问题说明3.2.1 先看/etc/resolv.conf说明3.2.2 针对第一个问题3.2.3 针对第二个问题 【后端】NginxluaOpenResty高性能实践 参考&#xff1a; https://blog.csdn.net/u010837612/article/details/123275026 1. 问…...

LeetCode637 二叉树的层平均值

前言 题目&#xff1a; 637. 二叉树的层平均值 文档&#xff1a; 代码随想录——二叉树的层平均值 编程语言&#xff1a; C 解题状态&#xff1a; 求取平均值的时候出现了点问题 思路 C中&#xff0c;浮点数的相加会产生精度误差&#xff0c;求取平均值时最好只在最后一步进行…...

王学岗ASM

服务发现 package com.example.testasm;import android.content.Context; import android.os.Bundle;import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat;…...

【数据结构】—— 队列

1、队列的概念2、队列的结构如何选择合适的数据结构实现队列&#xff08;数组or链表&#xff09; 3、队列的链式存储3.1 队列的链式存储结构3.2 队列的常见接口3.3 队列的接口实现初始化判空入队列出队列获取队头元素获取队尾元素获取节点个数销毁 3.4 源代码 4、队列的顺序存储…...

vue中openlayers过滤高亮显示某个图层

vue中openlayers过滤高亮显示某个图层 openlayers库没有直接支持这样设置&#xff0c;所以可以使用库&#xff1a;ol-ext&#xff0c;地址&#xff1a;https://viglino.github.io/ol-ext/examples/filter/map.filter.crop.html 效果&#xff1a; 关键代码&#xff1a; /**…...

WPF篇(11)-ToolTip控件(提示工具)+Popup弹出窗口

ToolTip控件 ToolTip控件继承于ContentControl&#xff0c;它不能有逻辑或视觉父级&#xff0c;意思是说它不能以控件的形式实例化&#xff0c;它必须依附于某个控件。因为它的功能被设计成提示信息&#xff0c;当鼠标移动到某个控件上方时&#xff0c;悬停一会儿&#xff0c;…...

【mysql 第一篇章】系统和数据库的交互方法

一、宏观的查看系统怎么和数据库交互 在我们刚刚接触系统和数据库的时候不明白其中的原理&#xff0c;只知道系统和数据库是需要交互的。所以我们会理解成上图的形式。 二、MYSQL 驱动 随着我们的学习时间的加长以及对程序的了解&#xff0c;发现链接数据库是需要有别的工具辅…...

数据结构-位运算总结

位运算总结&#xff1a; 1.求位1的个数 191. 位1的个数 - 力扣&#xff08;LeetCode&#xff09; 有两种写法&#xff1a; 1.是把该数不断的去与0x1相与&#xff0c;得到该数的最后一位的值&#xff0c;然后判断他是不是1&#xff0c;再把该数更新一下整体往后移动一位也就…...

java 异常堆栈的由来

编写的程序代码内部错误产生的异常&#xff0c;如调用对象为空(空指针异常)、数组越界异常、除0异常等。这种通常称为未检查的异常&#xff08;Runtime异常子类&#xff09;&#xff0c;在虚拟机中执行时会集中处理这些异常。其他运行中异常&#xff0c;通过throw语句主动抛出的…...

【推荐系统】【多任务学习】Progressive Layered Extraction (PLE)

Progressive Layered Extraction (PLE): A Novel Multi-Task Learning (MTL) Model for Personalized Recommendations 文章目录 Progressive Layered Extraction (PLE): A Novel Multi-Task Learning (MTL) Model for Personalized Recommendations1 论文出处2 背景2.1 背景介…...

java -转win32/win64免安装jre环境运行

由于java 转为exe&#xff0c;只能在装有JDK环境的电脑运行&#xff0c; 发给其他人也不能运行&#xff0c;缺少环境&#xff0c;程序自己背着jre走 1.先打好jar 包 2.使用exe4j 把jar包转成exe 运行程序 3.使用inno stup &#xff0c;把exe运行程序加上jre环境 以下是具体实现…...

算法板子:容斥原理——求出 1∼n 中能被质数 p1,p2,…,pm 中的至少一个数整除的整数有多少个

1. 题目要点 1. 设&#xff1a;求1~10中能被质数2和3中至少一个数整除的数有多少个。1~10中能被质数2整除的数的集合记为S1{2,4,6,8,10}&#xff0c;能被质数3整除的数的集合记为S2{3,6,9}&#xff0c;能同时被质数2和3整数的数的集合为S1∩S2{6} 2. 这道题的目的是求S1∪S2∪S…...

用gurobipy求解带不等式约束条件的优化问题

1. 引入 在当今的数据驱动世界中&#xff0c;优化问题无处不在&#xff0c;从工程设计到经济模型&#xff0c;再到机器学习算法的调参&#xff0c;优化都是实现效率最大化、成本最小化或性能最优化的关键工具。 这里有一个典型的数学优化问题&#xff0c;目标是在给定的约束条…...

漏洞复现-Adobe ColdFusion 远程代码执行漏洞(CVE-2023-38203)

1.漏洞描述 Adobe ColdFusion是一种服务器端的Web应用开发平台。它由Adobe Systems开发&#xff0c;用于创建动态的、交互式的Web应用程序和网站。 Adobe ColdFusion在2018u17及之前版本、2021u7及之前版本和2023u1及之前版本中存在任意代码执行漏洞。该漏洞是由于反序列化不…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

【单片机期末】单片机系统设计

主要内容&#xff1a;系统状态机&#xff0c;系统时基&#xff0c;系统需求分析&#xff0c;系统构建&#xff0c;系统状态流图 一、题目要求 二、绘制系统状态流图 题目&#xff1a;根据上述描述绘制系统状态流图&#xff0c;注明状态转移条件及方向。 三、利用定时器产生时…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...