java实现分类下拉树,点击时对应搜索---后端逻辑
一直想做分类下拉,然后选择后搜索的页面,正好做项目有了明确的需求,查找后发现el-tree的构件可满足需求,数据要求为:{ id:1, label:name, childer:[……] }形式的,于是乎,开搞!
一、效果预览
点击后
二、实现步骤
1、数据库设计层面:
在设计数据库的分类表时,需要设置为层级结构的样式,指明父分类id和祖分类id,如此才可满足分类需求。
具体如下,比如我的书签分类表:markClass(以下只写出主要三列)
属性 | 数据类型 | 字段说明 | 举例 |
markClassId | int | 书签分类的id | 1001 |
parentid | int | 该分类的上级分类id(父id) | 1000 |
ancestors | string | 该分类所有上属分类id(祖id) | 0,10,100,1000 |
2、后端基础的构建
依据表格完成对后端基础增删改的设置,可以用Ruoyi一键生成。以下主要说明插入时,ancestors的设置:
插入时需要依据父分类id来完成对ancestors的设置。(注意设置ancestors的getter、setter方法)
private MarkclassMapper markclassMapper;
//这个是在你写的Impl层中的那个类里,你的selectById方法在哪里就引用哪个public int insertMarkclass(Markclass markclass){Markclass mar=markclassMapper.selectMarkclassByMarkClassId(markclass.getParentid());//依据传入的markclass对象的父分类id查找具体的分类markclass.setAncestors(mar.getAncestors()+","+markclass.getParentid());//调用set方法。按照字符串拼接的方法,获取父分类所包含的祖分类id,并加上父分类id。return markclassMapper.insertMarkclass(markclass);}
3、主代码——构建树相关
(1)创建适配el-tree数据类型的类
el-tree的构件数据要求为:{ id:1, label:name, childer:[……] }形式,于是可定义一个“TreeSelect”类(类名称随意)
package com.blhq.wjs.domain;
import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;import com.blhq.common.core.domain.entity.SysMenu;
import com.fasterxml.jackson.annotation.JsonInclude;
//引入需要的你定义到的实体类/*** Treeselect树结构实体类* 数据类型依据el-tree数据需求进行定义,即为:id、label、children,* @author blhq*/
public class TreeSelect implements Serializable
{private static final long serialVersionUID = 1L;/** 节点ID */private Long id;/** 节点名称 */private String label;/** 子节点 */// @JsonInclude(JsonInclude.Include.NON_EMPTY)private List<TreeSelect> children;public TreeSelect(Class aclass) {this.id = aclass.getClassid();this.label = aclass.getClassname();// 检查此处是否正确设置了 class_children 属性this.children = aclass.getClass_children().stream().map(TreeSelect::new).collect(Collectors.toList());}//写了多个TreeSelect构造类,以满足后续其它类型对象的调用,public TreeSelect(Markclass markclass){this.id=markclass.getMarkClassId();this.label=markclass.getClassname();this.children=markclass.getMarkChildren().stream().map(TreeSelect::new).collect(Collectors.toList());}public TreeSelect(SysMenu menu){this.id = menu.getMenuId();this.label = menu.getMenuName();this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());}public Long getId(){return id;}public void setId(Long id){this.id = id;}public String getLabel(){return label;}public void setLabel(String label){this.label = label;}public List<TreeSelect> getChildren(){return children;}public void setChildren(List<TreeSelect> children){this.children = children;}
}
补充:Markclass类
private List<Markclass> markChildren=new ArrayList<Markclass>();
//必须实例化,不然会报错java.lang.NullPointerException: null
//同时设置getter、setter方法
public List<Markclass> getMarkChildren() {return markChildren;
}public void setMarkChildren(List<Markclass> markChildren) {this.markChildren = markChildren;}
(2)service层:
在你的service类中创建方法:(如果没有service层,则无视,直接看实现层操作即可)
注意:部分返回值类型为刚刚定义的TreeSelect的列表。
public List<TreeSelect> selectMarkClassTreeList(Markclass markclass);//接收用户传入的分类,即为当前操作的分类public List<Markclass> buildMarkClassTree(List<Markclass> markclassList);//构建树的核心方法public List<TreeSelect> buildMarkClassTreeSelect(List<Markclass> classes);//注意返回值类型为刚刚定义的TreeSelect的列表。
(3)Impl实现层:
核心代码:
@Overridepublic List<TreeSelect> selectMarkClassTreeList(Markclass markclass) {//传入用户操作的分类,获取筛选后的书签分类列表List<Markclass> markClasses = this.selectMarkclassList(markclass);//selectMarkclassList()为查找分类列表用的方法,返回值为List<Markclass>类型return buildMarkClassTreeSelect(markClasses);//构建树}@Overridepublic List<TreeSelect> buildMarkClassTreeSelect(List<Markclass> classes) {List<Markclass> markClasses = buildMarkClassTree(classes);return markClasses.stream().map(TreeSelect::new).collect(Collectors.toList());}/*** 构建前端所需要树结构*/@Overridepublic List<Markclass> buildMarkClassTree(List<Markclass> markclassList) {List<Markclass> returnList=new ArrayList<Markclass>();存储当前分类出现的节点List<Long> tempList = markclassList.stream().map(Markclass::getMarkClassId).collect(Collectors.toList());for (Markclass markclass : markclassList) {if(!tempList.contains(markclass.getParentid())){// 如果是顶级节点, 遍历该父节点的所有子节点recursionFn(markclassList, markclass);returnList.add(markclass);//不存在的加入进去,从而完成一整个树的遍历}}if (returnList.isEmpty()) {returnList = markclassList;}return returnList;}private void recursionFn(List<Markclass> list, Markclass t) {// 递归。得到子节点列表List<Markclass> childList = getChildList(list, t);t.setMarkChildren(childList);for (Markclass tChild : childList) {if (hasChild(list, tChild)) {recursionFn(list, tChild);}}}/*** 得到子节点列表*/private List<Markclass> getChildList(List<Markclass> list, Markclass t) {List<Markclass> tlist = new ArrayList<Markclass>();for (Markclass n : list) {if (StringUtils.isNotNull(n.getParentid()) && n.getParentid().longValue() == t.getMarkClassId().longValue()) {tlist.add(n);}}return tlist;}private boolean hasChild(List<Markclass> list, Markclass t) {return getChildList(list, t).size() > 0;}
完整Impl层代码:
package com.blhq.wjs.service.impl;import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;import com.blhq.common.utils.StringUtils;
import com.blhq.wjs.domain.TreeSelect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.blhq.wjs.mapper.MarkclassMapper;
import com.blhq.wjs.domain.Markclass;
import com.blhq.wjs.service.IMarkclassService;/*** 书签分类Service业务层处理* * @author blhq* @date 2024-06-19*/
@Service
public class MarkclassServiceImpl implements IMarkclassService
{@Autowiredprivate MarkclassMapper markclassMapper;/*** 查询书签分类* * @param markClassId 书签分类主键* @return 书签分类*/@Overridepublic Markclass selectMarkclassByMarkClassId(Long markClassId){return markclassMapper.selectMarkclassByMarkClassId(markClassId);}/*** 查询书签分类列表* * @param markclass 书签分类* @return 书签分类*/@Overridepublic List<Markclass> selectMarkclassList(Markclass markclass){return markclassMapper.selectMarkclassList(markclass);}/*** 新增书签分类* * @param markclass 书签分类* @return 结果*/@Overridepublic int insertMarkclass(Markclass markclass){if (markclass.getMarkClassId() == null) {return markclassMapper.insertMarkclass(markclass);}else {Markclass mar=markclassMapper.selectMarkclassByMarkClassId(markclass.getParentid());markclass.setAncestors(mar.getAncestors()+","+markclass.getParentid());return markclassMapper.insertMarkclass(markclass);}}/*** 修改书签分类* * @param markclass 书签分类* @return 结果*/@Overridepublic int updateMarkclass(Markclass markclass){return markclassMapper.updateMarkclass(markclass);}/*** 批量删除书签分类* * @param markClassIds 需要删除的书签分类主键* @return 结果*/@Overridepublic int deleteMarkclassByMarkClassIds(Long[] markClassIds){return markclassMapper.deleteMarkclassByMarkClassIds(markClassIds);}/*** 删除书签分类信息* * @param markClassId 书签分类主键* @return 结果*/@Overridepublic int deleteMarkclassByMarkClassId(Long markClassId){return markclassMapper.deleteMarkclassByMarkClassId(markClassId);}@Overridepublic List<TreeSelect> selectMarkClassTreeList(Markclass markclass) {//传入用户操作的分类,获取筛选后的书签分类列表List<Markclass> markClasses = this.selectMarkclassList(markclass);//selectMarkclassList()为查找分类列表用的方法,返回值为List<Markclass>类型return buildMarkClassTreeSelect(markClasses);//构建树}@Overridepublic List<TreeSelect> buildMarkClassTreeSelect(List<Markclass> classes) {List<Markclass> markClasses = buildMarkClassTree(classes);return markClasses.stream().map(TreeSelect::new).collect(Collectors.toList());}/*** 构建前端所需要树结构*/@Overridepublic List<Markclass> buildMarkClassTree(List<Markclass> markclassList) {List<Markclass> returnList=new ArrayList<Markclass>();存储当前分类出现的节点List<Long> tempList = markclassList.stream().map(Markclass::getMarkClassId).collect(Collectors.toList());for (Markclass markclass : markclassList) {if(!tempList.contains(markclass.getParentid())){// 如果是顶级节点, 遍历该父节点的所有子节点recursionFn(markclassList, markclass);returnList.add(markclass);//不存在的加入进去,从而完成一整个树的遍历}}if (returnList.isEmpty()) {returnList = markclassList;}return returnList;}private void recursionFn(List<Markclass> list, Markclass t) {// 递归。得到子节点列表List<Markclass> childList = getChildList(list, t);t.setMarkChildren(childList);for (Markclass tChild : childList) {if (hasChild(list, tChild)) {recursionFn(list, tChild);}}}/*** 得到子节点列表*/private List<Markclass> getChildList(List<Markclass> list, Markclass t) {List<Markclass> tlist = new ArrayList<Markclass>();for (Markclass n : list) {if (StringUtils.isNotNull(n.getParentid()) && n.getParentid().longValue() == t.getMarkClassId().longValue()) {tlist.add(n);}}return tlist;}private boolean hasChild(List<Markclass> list, Markclass t) {return getChildList(list, t).size() > 0;}}
(4)controller层:
这时候,我们直接添加即可:推荐把它放到对应需要分类显示的文件中,比如我的是在书签的controller中。
@GetMapping("/deptTree")public AjaxResult deptTree(Markclass markclass){List<TreeSelect> list = markclassService.selectMarkClassTreeList(markclass);return success(list);}
(5)xml文件:
修改查找逻辑的语句:将分类的查询改为,从markclas表的ancestors查:
<select id="selectBookmarkList" parameterType="Bookmark" resultMap="BookmarkResult">select b.markId, b.markName, b.markClassId, b.website, b.`desc`, b.createTime, b.editTime, b.icon, b.statue, b.commonGrade, b.allGrade, b.markPlot, b.likes, b.markExtend, c.classname, c.ancestors from bookmark bleft join markclass c on b.markClassId = c.markClassIdwhere 1=1<if test="markId != null "> and markId = #{markId}</if><if test="markName != null and markName != ''"> and markName like concat('%', #{markName}, '%')</if><if test="markClassId != null ">AND (b.markClassId = #{markClassId} OR b.markClassId IN ( SELECT c.markClassId FROM class c WHERE find_in_set(#{markClassId}, ancestors) ))</if><if test="website != null and website != ''"> and website like concat('%', #{website}, '%')</if><if test="desc != null and desc != ''"> and b.`desc` like concat('%', #{desc}, '%')</if><if test="createTime != null "> and b.createTime >= #{createTime}</if><if test="editTime != null "> and b.editTime <= #{editTime}</if><if test="icon != null and icon != ''"> and icon like concat('%', #{icon}, '%')</if><if test="statue != null "> and statue = #{statue}</if><if test="commonGrade != null "> and commonGrade = #{commonGrade}</if><if test="allGrade != null "> and allGrade like concat('%', #{allGrade}, '%')</if><if test="markPlot != null and markPlot != ''"> and markPlot = #{markPlot}</if><if test="likes != null "> and likes like concat('%', #{likes}, '%')</if><if test="markExtend != null "> and markExtend like concat('%', #{markExtend}, '%')</if></select>
4、前端应用:
整体思路就是引用它,把它调过来使用就行。下面按我的思路来演示:
(1)引入api:
// 查询书签分类列表--tree
export function markClassTreeSelect(query) {return request({url: '/markclass/markclass/deptTree',method: 'get',params: query})
}
(2)页面导入
注意将v-model中的值,改为你自己的。
<template><el-row :gutter="20"><el-col :span="4" :xs="24"><div class="head-container"><el-inputv-model="markName"placeholder="请输入书签分类名称"clearablesize="small"prefix-icon="el-icon-search"style="margin-bottom: 20px"/></div><el-tree:data="markOptions":props="defaultProps":expand-on-click-node="false":filter-node-method="filterNode"ref="tree"node-key="id"default-expand-allhighlight-current@node-click="handleNodeClick"/><!-- </div>--></el-col><el-col :span="20" :xs="24">…………</el-col></el-row>…………//你的其它页面布局代码,上文gutter表示左侧分类栏占比大小。</template>
<script></script>内需要添加的:
data() {return {//data内需要添加的数据:// 书签分类名称markName:undefined,// 书签分类选项markOptions: undefined,//设置分类数据样式:defaultProps: {children: "children",//children:就是告诉el-tree,//它需要的children,在你这里的数据,叫啥名,比如我的为children//就是TreeSelect类中定义的childrenlabel: "label"},
},
},watch: {//在watch内,添加如下内容,没watch就自己加,在data(){},后面。// 根据名称筛选树markName(val) {this.$refs.tree.filter(val);}},created() {//调用方法this.getMarkTree();},methods: {// 获取书签分类树getMarkTree() {markClassTreeSelect().then(response => {this.markOptions = response.data;console.log(response.data);// console.log(response);});},// 筛选节点filterNode(value, data) {if (!value) return true;return data.label.indexOf(value) !== -1;},// 节点单击事件handleNodeClick(data) {this.queryParams.markClassId = data.id;this.handleQuery();},},
5、成果展示:
点击游戏类后:
三、我遇到的问题
(1):无法自动装配。找不到'MarkclassMapper’类型的 Bean。
方案:在MarkclassMapper中,最前面加入@Mapper注解即可
(2)Failed to instantiate: Factory method 'sqlSessionFactory' threw exception; TypeException: The alias 'TreeSelect' is already mapped to the value 'com.blhq.wjs.domain.TreeSelect'.
方案:如果提示这个,请把我们刚才定义的TreeSelect类,重构一下,改个名就行。
更详细说明请看下文:
分类树实现时遇见的bug:如XXXController $$EnhancerBySpringCGLIB$$c8ca0a15 cannot be cast to XXX……-CSDN博客文章浏览阅读2次。实现分类树实现时遇见的bughttps://blog.csdn.net/qq_64595427/article/details/139843770?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22139843770%22%2C%22source%22%3A%22qq_64595427%22%7D
如有问题,欢迎留言讨论哦~
相关文章:
java实现分类下拉树,点击时对应搜索---后端逻辑
一直想做分类下拉,然后选择后搜索的页面,正好做项目有了明确的需求,查找后发现el-tree的构件可满足需求,数据要求为:{ id:1, label:name, childer:[……] }形式的,于是乎,开搞! 一…...

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 披萨大作战(100分) - 三语言AC题解(Python/Java/Cpp)
🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 …...

探索Facebook对世界各地文化的影响
随着数字化时代的到来,社交媒体已成为连接世界各地人们的重要平台之一。而在这个领域的巨头之一,Facebook不仅是人们沟通交流的场所,更是一座桥梁,将不同地域、文化的人们联系在一起。本文将探索Facebook对世界各地文化的影响&…...
导出requirements.txt
文章目录 requirements.txt导出环境中所有包导出当前项目的包可能遇到的问题 requirements.txt 在Python项目中,通常使用requirements.txt文件来列出所有需要的第三方库和模块。这个文件通常位于项目的根目录下,并且在安装Python项目时,可以…...

我主编的电子技术实验手册(09)——并联电路
本专栏是笔者主编教材(图0所示)的电子版,依托简易的元器件和仪表安排了30多个实验,主要面向经费不太充足的中高职院校。每个实验都安排了必不可少的【预习知识】,精心设计的【实验步骤】,全面丰富的【思考习…...

数据结构_二叉树
目录 一、树型结构 二、二叉树 2.1 概念 2.2 特殊的二叉树 2.3 二叉树的性质 2.4 二叉树的存储 2.5 遍历二叉树 2.6 操作二叉树 总结 一、树型结构 树是一种非线性的数据结构,它是由 n(n>0) 个有限结点组成一个具有层次关系的集合,一棵 n 个…...

Java线程池七个参数详解
ThreadPoolExecutor 是JDK中的线程池实现,这个类实现了一个线程池需要的各个方法,它提供了任务提交、线程管理、监控等方法 下面是 ThreadPoolExecutor 类的构造方法源码,其他创建线程池的方法最终都会导向这个构造方法,共有7个参…...

产品Web3D交互展示有什么优势?如何快速制作?
智能互联网时代,传统的图片、文字、视频等产品展示方式,因为缺少互动性,很难引起用户的兴趣,已经逐渐失去了宣传优势。 Web3D交互展示技术的出现,让众多品牌和企业找到了新的方向,线上产品展示不在枯燥无趣…...

Python | Leetcode Python题解之第171题Excel列表序号
题目: 题解: class Solution:def titleToNumber(self, columnTitle: str) -> int:number, multiple 0, 1for i in range(len(columnTitle) - 1, -1, -1):k ord(columnTitle[i]) - ord("A") 1number k * multiplemultiple * 26return n…...

【银河麒麟】高可用触发服务器异常重启,处理机制详解
1.服务器环境以及配置 【机型】物理机 处理器: Intel 内存: 126G 【内核版本】 4.19.90-25.16.v2101.ky10.x86_64 【银河麒麟操作系统镜像版本】 Kylin-Server-10-SP2-Release-Shenzhen-Metro-x86-Build01-20220619 Kylin-HA-10-SP2-Release-S…...

性能工具之 JMeter 常用组件介绍(七)
文章目录 一、后置处理器1、Regular Expression Extractor(正则表达式提取器)2、JSON Extractor(JSON表达式提取器)3、Regular Expression Extractor(正则表达式提取器) 二、小结 本文主要介绍JMeter主流后置处理器的功能 一、后置处理器 从上面可以看出后置处理可以插件挺多&a…...

Python学习笔记15:进阶篇(四)文件的读写。
文件操作 学习编程操作中,我觉得文件操作是必不可少的一部分。不管是读书的时候学习的c,c,工作的前学的java,现在学的Python,没学过的php和go,都有文件操作的模块以及库的支持,重要性毫无疑问。…...

角度调制与解调电路
music! (黄佳庆老师可爱捏) 调角 角度调制有较好的抗噪性能。 调相 相位变化的频率变化的微分,频率变化是相位变化的积分 相位的变化率就是频率 调频 调相与调频的关系 大F是输入信号的频率,大Ω是输入信号的角频率 …...

数据分析:微生物组差异丰度方法汇总
欢迎大家关注全网生信学习者系列: WX公zhong号:生信学习者Xiao hong书:生信学习者知hu:生信学习者CDSN:生信学习者2 介绍 微生物数据具有一下的特点,这使得在做差异分析的时候需要考虑到更多的问题&…...

Linux驱动开发(二)--字符设备驱动开发提升 LED驱动开发实验
1、地址映射 在编写驱动之前,需要知道MMU,也就是内存管理单元,在老版本的 Linux 中要求处理器必须有 MMU,但是现在Linux 内核已经支持无 MMU 的处理器了。 MMU的功能如下: 完成虚拟空间到物理空间的映射 内存保护&…...

钡铼BL101网关助力智慧城市路灯远程智能管控
在迈向智慧城市的征途中,基础设施的智能化改造是关键一环,而路灯作为城市脉络的照明灯塔,其智能化升级对于节能减排、提升城市管理效率具有重要意义。钡铼BL101网关,作为Modbus转MQTT的专业桥梁,正以其卓越的性能和广泛…...

如何优雅的使用Github Action服务来将Hexo部署到Github Pages
文章目录 参考文章前提条件1. 初始化Hexo2. 初始化仓库3. 创建Token4. 修改_config.yml5. 配置Github Action工作流6. 推送验证7. 配置Github Pages8. 修改Hexo主题样式10. 添加文章遇到了一些问题和方案1. 网站没有样式问题2. 图片不显示 参考文章 Bilibili视频教程-9分钟零成…...

After Effects 2024 mac/win版:创意视效,梦想起航
After Effects 2024是一款引领视效革命的专业软件,汇聚了创意与技术的精华。作为Adobe推出的全新版本,它以其强大的视频处理和动画创作能力,成为从事设计和视频特技的机构,如电视台、动画制作公司、个人后期制作工作室以及多媒体工…...

信息打点web篇----web后端源码专项收集
前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 专栏描述:因为第一遍过信息收集的时候,没怎么把收集做回事 导致后来在实战中,遭遇资产获取少,可渗透点少的痛苦,如今决定 从头来过,全面全方位…...

ArcGIS批量投影转换的妙用(地理坐标系转换为平面坐标系)
点击下方全系列课程学习 点击学习—>ArcGIS全系列实战视频教程——9个单一课程组合系列直播回放 这次文章我们来介绍一下,如何巧妙用要素数据集来实现要素的批量投影。不需要ArcGIS的模型构建器与解决。 例如,有多个要素要将CGCS_2000地理坐标系投…...

接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...

Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...

路由基础-路由表
本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中,往往存在多个不同的IP网段,数据在不同的IP网段之间交互是需要借助三层设备的,这些设备具备路由能力,能够实现数据的跨网段转发。 路由是数据通信网络中最基…...