Arouter详解・常见面试题
前言:ARouter是一个用于 Android App 进行组件化改造的路由框架 —— 支持模块间的路由、通信、解耦。
一、路由简介:
路由:就是通过互联的网络把信息从源地址传输到目的地址的活动。完成路由这个操作的实体设备就是 路由器(Router)。
二、原理解析:
2.1 使用步骤
使用ARouter在进行Activity跳转:初始化ARouter、添加注解@Route、发起路由跳转。
- 步骤1:初始化ARouter
ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化
- 步骤2:注册Activity路由
// moduleA 在支持路由的页面上添加注解 路径注意至少需有两级,/xx/xx
@Route(path = "/test/activity1", name = "测试用 Activity")
public class Test1Activity extends BaseActivity {@Autowiredint age = 10;protected void onCreate(Bundle savedInstanceState) {ARouter.getInstance().inject(this);}
}
- 步骤3:通过path发起路由跳转
// moduleB 没有依赖 moduleA
ARouter.getInstance().build("/test/activity").navigation();
这样就使得 没有依赖moduleA的moduleB能跳转到moduleA的Activity了。
那么 ARouter 是如何做到只通过简单2步 就完成 解耦组件间的路由操作呢?我们通过源码一步步理解。
2.2 架构解析
ARouter的源码架构:
- app:是ARouter提供的一个测试Demo
- arouter-annotation:这个lib模块中声明了很多注解信息和一些枚举类
- arouter-api:ARouter的核心api,转换过程的核心操作都在这个模块里面
- arouter-compiler:APT处理器,自动生成路由表的过程就是在这里面实现的
- arouter-gradle-plugin:这是一个编译期使用的Plugin插件,主要作用是用于编译器自动加载路由表,节省应用的启动时间。
基础概念:
(1)PostCard(明信片):ARouter就是使用PostCard这个类来存储寄件人和收件人信息的。
java
复制代码
public final class Postcard extends RouteMeta {// Baseprivate Uri uri; //如果使用Uri方式发起luyouprivate Object tag; // A tag prepare for some thing wrong. inner params, DO NOT USE!private Bundle mBundle; // 需要传递的参数使用bundle存储private int flags = 0; // 启动Activity的标志:如NEW_FALGprivate int timeout = 300; // 路由超时private IProvider provider; // 使用IProvider的方式跳转private boolean greenChannel; //绿色通道,可以不经过拦截器private SerializationService serializationService; //序列化服务serializationService:需要传递Object自定义类型对象,就需要实现这个服务private Context context; // May application or activity, check instance type before use it.private String action; //Activity跳转的Action// Animationprivate Bundle optionsCompat; // The transition animation of activityprivate int enterAnim = -1;private int exitAnim = -1;...
}
PostCard继承了RouteMeta:
java
复制代码
public class RouteMeta {private RouteType type; // 路由类型:如Activity,Fragment,Provider等private Element rawType; // 路由原始类型,在编译时用来判断private Class<?> destination; // 目的Class对象private String path; // 路由注册的pathprivate String group; // 路由注册的group分组private int priority = -1; // 路由执行优先级,priority越低,优先级越高,这个一般在拦截器中使用private int extra; // Extra dataprivate Map<String, Integer> paramsType; // 参数类型,例如activity中使用@Autowired的参数类型private String name; //路由名字,用于生成javadocprivate Map<String, Autowired> injectConfig; // 参数配置(对应paramsType).}
RouteMeta:主要存储的是一些目的对象的信息,这些对象是在路由注册的时候才会生成。
(2)Interceptor拦截器:
ARouter中,所有的路由调用过程在到达目的地前都会先经过自定义的一系列拦截器,实现一些AOP切面编程。
java
复制代码
public interface IInterceptor extends IProvider {/*** The operation of this interceptor.** @param postcard meta* @param callback cb*/void process(Postcard postcard, InterceptorCallback callback);
}
IInterceptor是一个接口,继承了IProvider,所以其也是一个服务类型,只需要实现process方法就可以实现拦截操作。
(3)greenChannel:绿色通道
设置了绿色通道的跳转过程,可以不经过拦截器
(4)Warehouse:路由仓库
Warehouse意为仓库,用于存放被 @Route、@Interceptor标注的 路由相关的信息,也就是我们关注的destination等信息,它存着所有路由信息。
java
复制代码
class Warehouse {//所有IRouteGroup实现类的class对象,是在ARouter初始化中赋值,key是path第一级//(IRouteGroup实现类是编译时生成,代表一个组,即path第一级相同的所有路由,包括Activity和Provider服务)static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>(); //所有路由元信息,是在completion中赋值,key是path//首次进行某个路由时就会加载整个group的路由,即IRouteGroup实现类中所有路由信息。包括Activity和Provider服务static Map<String, RouteMeta> routes = new HashMap<>();//所有服务provider实例,在completion中赋值,key是IProvider实现类的classstatic Map<Class, IProvider> providers = new HashMap<>();//所有provider服务的元信息(实现类的class对象),是在ARouter初始化中赋值,key是IProvider实现类的全类名。//主要用于使用IProvider实现类的class发起的获取服务的路由,例如ARouter.getInstance().navigation(HelloService.class)static Map<String, RouteMeta> providersIndex = new HashMap<>();//所有拦截器实现类的class对象,是在ARouter初始化时收集到,key是优先级static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("...");//所有拦截器实例,是在ARouter初始化完成后立即创建static List<IInterceptor> interceptors = new ArrayList<>();
...
}
2.3 源码分析:
下面我们分别来分析使用过程:
步骤1分析:ARouter.init(this)
/*** Init, it must be call before used router.*/
public static void init(Application application) {if (!hasInit) {logger = _ARouter.logger;_ARouter.logger.info(Consts.TAG, "ARouter init start.");hasInit = _ARouter.init(application);if (hasInit) {_ARouter.afterInit();}_ARouter.logger.info(Consts.TAG, "ARouter init over.");}
}
调用了_ARouter同名init方法:
protected static synchronized boolean init(Application application) {mContext = application;LogisticsCenter.init(mContext, executor);logger.info(Consts.TAG, "ARouter init success!");hasInit = true;mHandler = new Handler(Looper.getMainLooper());return true;
}
内部初始化了一些mContext,mHandler以及字段信息 最重要的是LogisticsCenter.init(mContext, executor):进入看看:
/*** LogisticsCenter init, load all metas in memory. Demand initialization*/
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {try {//使用AGP插件进行路由表的自动加载loadRouterMap();//如果registerByPlugin被设置为true,说明使用的是插件加载,直接跳过if (registerByPlugin) {logger.info(TAG, "Load router map by arouter-auto-register plugin.");} else {//如果是false,则调用下面步骤加载Set<String> routerMap;// 如果是debug模式或者是新版本的,则每次都会去加载routerMap,这会是一个耗时操作if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {logger.info(TAG, "Run with debug mode or new install, rebuild router map.");// These class was generated by arouter-compiler.routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);if (!routerMap.isEmpty()) {context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();}PackageUtils.updateVersion(context); // Save new version name when router map update finishes.} else {//如果是其他的情况,则直接去文件中读取。logger.info(TAG, "Load router map from cache.");routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));}//这里循环获取routerMap中的信息for (String className : routerMap) {//如果className = "com.alibaba.android.arouter.routes.ARouter$$Root"格式,则将路由组信息添加到Warehouse.groupsIndex中if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {// This one of root elements, load root.((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);//如果className = "com.alibaba.android.arouter.routes.ARouter$$Interceptors"格式,则将拦截器信息添加到Warehouse.interceptorsIndex中} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {// Load interceptorMeta((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);//如果className = "com.alibaba.android.arouter.routes.ARouter$$Providers"格式,则将服务Provider信息添加到Warehouse.providersIndex中} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {// Load providerIndex((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);}}}} catch (Exception e) {throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");}
}
ARouter的init操作总结:
-
1.优先使用插件加载路由表信息到仓库中,如果没有使用插件,则使用包名com.alibaba.android.arouter.routes去dex文件中查找对应的类对象 查找到后,保存到sp文件中,非debug或者新版本的情况下,下次就直接使用sp文件中缓存的类信息即可。
-
2.查找到对应的类文件后,使用反射调用对应的类的loadInto方法,将路由组,拦截器以及服务Provider信息加载到Warehouse仓库中
步骤2分析:注册Activity路由
我们注册的Activity,Provider等路由信息,在编译期被注解处理器处理生成对应的路由表:路由表在ARouter初始化的时候被加载到Warehouse中;
步骤3分析:通过path启动对应的Activity
ARouter.getInstance().build("/test/activity2").navigation();
- getInstance(): 做了init检查并创建了一个ARouter对象
public static ARouter getInstance() {if (!hasInit) {throw new InitException("ARouter::Init::Invoke init(context) first!");} else {if (instance == null) {synchronized (ARouter.class) {if (instance == null) {instance = new ARouter();}}}return instance;}
}
- build():这里创建了一个Postcard,传入path和group;
public Postcard build(String path) {return _ARouter.getInstance().build(path);
}
调用了_ARouter的同名build方法
protected Postcard build(String path) {if (TextUtils.isEmpty(path)) {throw new HandlerException(Consts.TAG + "Parameter is invalid!");} else {PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);if (null != pService) {path = pService.forString(path);}return build(path, extractGroup(path), true);}
}
1.使用PathReplaceService,可以替换原path为新的path
继续看build方法:
protected Postcard build(String path, String group, Boolean afterReplace) {if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {throw new HandlerException(Consts.TAG + "Parameter is invalid!");} else {if (!afterReplace) {PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);if (null != pService) {path = pService.forString(path);}}return new Postcard(path, group);}
}
- navigation():执行_ARouter中的同名navigation方法,内容如下
1.预处理服务
2.完善PostCard信息
3.如果是非绿色通道,则使用拦截器处理请求
4.调用_navigation()处理
直接看 _navigation() 方法:
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {final Context currentContext = postcard.getContext();switch (postcard.getType()) {case ACTIVITY:// Build intentfinal Intent intent = new Intent(currentContext, postcard.getDestination());intent.putExtras(postcard.getExtras());// Set flags.int flags = postcard.getFlags();if (0 != flags) {intent.setFlags(flags);}// Non activity, need FLAG_ACTIVITY_NEW_TASKif (!(currentContext instanceof Activity)) {intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);}// Set ActionsString action = postcard.getAction();if (!TextUtils.isEmpty(action)) {intent.setAction(action);}// Navigation in main looper.runInMainThread(new Runnable() {@Overridepublic void run() {startActivity(requestCode, currentContext, intent, postcard, callback);}});break;case PROVIDER:return postcard.getProvider();case BOARDCAST:case CONTENT_PROVIDER:case FRAGMENT:Class<?> fragmentMeta = postcard.getDestination();try {Object instance = fragmentMeta.getConstructor().newInstance();if (instance instanceof Fragment) {((Fragment) instance).setArguments(postcard.getExtras());} else if (instance instanceof android.support.v4.app.Fragment) {((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());}return instance;} catch (Exception ex) {logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));}case METHOD:case SERVICE:default:return null;}return null;
}
这个方法其实就是根据PostCard的type来处理不同的请求了
- 1.Activity,直接跳转
- 2.Fragment,Provider,BroadcaseReceiver和ContentProvider,直接返回类的实例对象。
inject简介:
通过@Autowired注解的属性,通过调用ARouter.getInstance().inject(this);可以实现自动注入。我们知道原生的Activity传递数据是通过Bundle携带的。因此,ARouter的数据传递肯定也是基于Bundle,并实现了自动赋值的功能。
//生成类的类名 = 目标类名+ "$$ARouter$$Autowired"(固定后缀)
public class BlankFragment$$ARouter$$Autowired implements ISyringe {private SerializationService serializationService;@Overridepublic void inject(Object target) {serializationService = ARouter.getInstance().navigation(SerializationService.class);//强转成目标类BlankFragment substitute = (BlankFragment)target;//给每个注解了@Autowired的属性赋值//这里的key为属性名或者注解中给定的namesubstitute.name = substitute.getArguments().getString("name");// 如果存在Bundle无法携带的类型,则会通过SerializationService序列化成json的String传递,SerializationService没有提供默认实现,需要用户自己实现if (null != serializationService) {substitute.obj = serializationService.parseObject(substitute.getArguments().getString("obj"), new com.alibaba.android.arouter.facade.model.TypeWrapper<TestObj>(){}.getType());} else {Log.e("ARouter::", "You want automatic inject the field 'obj' in class 'BlankFragment' , then you should implement 'SerializationService' to support object auto inject!");}if (null == substitute.obj) {Log.e("ARouter::", "The field 'obj' is null, in class '" + BlankFragment.class.getName() + "!");}}
}
前面分析navigation的时候我们看到了将Uri参数存入Bundle的过程(或者由用户手动调用withXX存入bundle),此处则是将Bundle中的数据取出,并赋值给@Autowired注解的属性。
Arouter流程图简介
三、Arouter常见面试问题:
3.1 使用方法
在Android开发中,使用ARouter进行应用内跳转非常简单。首先,需要在要跳转的Activity上添加@Route注解。其次,在应用启动时初始化ARouter,通常是在Application的onCreate方法中完成。初始化时,可以根据需要开启日志和调试模式。最后,通过ARouter的getInstance().build(path).navigation()方法进行跳转,其中path是目标Activity的路由地址1。
3.2 简述原理
ARouter的原理主要包括两个步骤:注册子模块信息到路由表和寻址操作。
- 注册子模块信息到路由表:这一步并不是手动完成的,而是采用编译器APT技术,在编译的时候扫描自定义注解,通过注解获取子模块信息,并自动注册到路由表中去。
- 寻址操作:在需要跳转的时候,ARouter会根据提供的路由地址,在路由表中寻找到对应的子模块信息,并完成交互,实现跳转。
3.3、ARouter 通信示意图
3.4、ARouter 的底层实现原理
ARouter的底层实现原理主要包括:通过编译时注解和注解处理器APT生成路由表,记录页面与URL的映射关系;应用启动时,通过反射加载路由表到内存;当需要路由跳转时,根据URL在路由表中查找对应的页面信息,创建Intent并设置参数,最后通过startActivity实现页面跳转。这种方式实现了模块间的解耦,使得不同模块间可以相互独立、互不依赖地进行通信和跳转。
相关文章:

Arouter详解・常见面试题
前言:ARouter是一个用于 Android App 进行组件化改造的路由框架 —— 支持模块间的路由、通信、解耦。 一、路由简介: 路由:就是通过互联的网络把信息从源地址传输到目的地址的活动。完成路由这个操作的实体设备就是 路由器(Rout…...

全志开发板 视频输入框架
笔记来源于百问网出品的教程。 1.VIN camera驱动框架 • 使用过程中可简单的看成是vin 模块 device 模块af driver flash 控制模块的方式; • vin.c 是驱动的主要功能实现,包括注册/注销、参数读取、与v4l2 上层接口、与各device 的下层接口、中断处…...

寒假学web--day10
简介 一些高级的反序列化 phar反序列化 phar类似于java的jar包,将多个php文件合并为独立的压缩包,不用解压就能执行里面的php文件,支持web服务器和命令行 metadata $phar->setmetadata($h); metadata可以存放一个类实例,…...

【全栈】SprintBoot+vue3迷你商城(9)
【全栈】SprintBootvue3迷你商城(9) 往期的文章都在这里啦,大家有兴趣可以看一下 后端部分: 【全栈】SprintBootvue3迷你商城(1) 【全栈】SprintBootvue3迷你商城(2) 【全栈】Spr…...

系统思考—问题分析
很多中小企业都在面对转型的难题:市场变化快,资源有限,团队协作不畅……这些问题似乎总是困扰着我们。就像最近和一位企业主交流时,他提到:“我们团队每天都很忙,但效率始终没见提升,感觉像是在…...

系统架构设计师教材:信息系统及信息安全
信息系统 信息系统的5个基本功能:输入、存储、处理、输出和控制。信息系统的生命周期分为4个阶段,即产生阶段、开发阶段、运行阶段和消亡阶段。 信息系统建设原则 1. 高层管理人员介入原则:只有高层管理人员才能知道企业究竟需要什么样的信…...

美国三种主要的个人数据产业模式简析
文章目录 前言一、个人征信(Credit Reporting)模式1、定义:2、特点:数据来源:核心功能:服务对象:代表性公司:监管框架:示例应用:二、面向垂直场景的个人数据公司(Consumer Reporting,消费者报告模式)1、定义:2、特点:数据来源:核心功能:服务对象:主要公司:监…...

js手撕 | 使用css画一个三角形 使用js修改元素样式 驼峰格式与“-”格式相互转化
1.使用css画一个三角形 借助 border 实现,在 width 和 height 都为 0 时,设置 border,便会呈现三角形。想要哪个方向的三角形,设置其他三边为 透明即可。同时,可以通过调整不同边的宽度,来调整三角形的高度…...

每日一道算法题
题目:最长递增子序列的个数 给定一个未排序的整数数组,找到最长递增子序列的个数。 示例 1 输入:nums [1,3,5,4,7]输出:2解释:有两个最长递增子序列,分别是 [1,3,4,7] 和 [1,3,5,7] 。 示例 2 输入&a…...

低代码系统-产品架构案例介绍、明道云(十一)
明道云HAP-超级应用平台(Hyper Application Platform),其实就是企业级应用平台,跟微搭类似。 通过自设计底层架构,兼容各种平台,使用低代码做到应用搭建、应用运维。 企业级应用平台最大的特点就是隐藏在冰山下的功能很深…...

论文笔记(六十三)Understanding Diffusion Models: A Unified Perspective(三)
Understanding Diffusion Models: A Unified Perspective(三) 文章概括 文章概括 引用: article{luo2022understanding,title{Understanding diffusion models: A unified perspective},author{Luo, Calvin},journal{arXiv preprint arXiv:…...

利用机器学习创建基于位置的推荐程序
推荐系统被广泛应用于不同的应用程序中,用于预测用户对产品或服务的偏好或评价。在过去的几分钟或几小时里,你很可能在网上遇到过或与某种类型的推荐系统进行过互动。这些推荐系统有不同的类型,其中最突出的包括基于内容的过滤和协作过滤。在…...

每日一题 429. N 叉树的层序遍历
429. N 叉树的层序遍历 /*class Solution { public:vector<vector<int>> levelOrder(Node* root) {queue<Node*> que;que.push(root);vector<vector<int>> ans;if(root nullptr){return ans;}while(!que.empty()){int sizeQue que.size();vec…...

AIP-132 标准方法:List
编号132原文链接AIP-132: Standard methods: List状态批准创建日期2019-01-21更新日期2022-06-02 在许多API中,通常会向集合URI(例如 /v1/publishers/1/books )发出GET请求,获取集合中资源的列表。 面向资源设计(AIP…...

CSAPP学习:前言
前言 本书简称CS:APP。 背景知识 一些基础的C语言知识 如何阅读 Do-做系统 在真正的系统上解决具体的问题,或是编写和运行程序。 章节 2025-1-27 个人认为如下章节将会对学习408中的操作系统与计算机组成原理提供帮助,于是先凭借记忆将其简单…...
【统计的思想】假设检验(二)
假设检验是根据人为设定的显著水平,对被测对象的总体质量特性进行统计推断的方法。 如果我们通过假设检验否定了零假设,只是说明在设定的显著水平下,零假设成立的概率比较小,并不是说零假设就肯定不成立。如果零假设事实上是成立…...

KNN算法学习实践
1.理论学习 原文链接 ShowMeAI知识社区 2.案例实践 假如一套房子打算出租,但不知道市场价格,可以根据房子的规格(面积、房间数量、厕所数量、容纳人数等),在已有数据集中查找相似(K近邻)规格…...

数据可视化的图表
1.折线图反映了一段时间内事物连续的动态变化规律,适用于描述一个变量随另一个变量变化的趋势,通常用于绘制连续数据,适合数据点较多的情况。 2.散点图是以直角坐标系中各点的密集程度和变化趋势来表示两种现象间的相关关系,常用于显示和比较数值。当要在不考虑时间…...

动手学深度学习-卷积神经网络-3填充和步幅
目录 填充 步幅 小结 在上一节的例子(下图) 中,输入的高度和宽度都为3,卷积核的高度和宽度都为2,生成的输出表征的维数为22。 正如我们在 上一节中所概括的那样,假设输入形状为nhnw,卷积核形…...

【JS|第28期】new Event():前端事件处理的利器
日期:2025年1月24日 作者:Commas 签名:(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释:如果您觉得有所帮助,帮忙点个赞,也可以关注我,我们一起成长;如果有不对的地方…...

Spring Boot 中的事件发布与监听:深入理解 ApplicationEventPublisher(附Demo)
目录 前言1. 基本知识2. Demo3. 实战代码 前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 基本的Java知识推荐阅读: java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全&am…...

【Spring】Spring启示录
目录 前言 一、示例程序 二、OCP开闭原则 三、依赖倒置原则DIP 四、控制反转IOC 总结 前言 在软件开发的世界里,随着项目的增长和需求的变化,如何保持代码的灵活性、可维护性和扩展性成为了每个开发者必须面对的问题。传统的面向过程或基于类的设计…...

ospf动态路由配置,cost路径调整,ospf认证实验
一、实验拓扑如图: 接口ip配置网络 :10.17.12.* 10.17.13.* ,10.17.23.* 回环接口配置分别为 10.0.1.1 ,10.0.1.2,10.0.1.3对应三台路由器 ar1配置接口ip interface GigabitEthernet0/0/0 ip address 10.17.12.1…...

在Rust应用中访问.ini格式的配置文件
在Rust应用中访问.ini格式的配置文件,你可以使用第三方库,比如 ini 或 config. 下面是一个使用 ini 库的示例,该库允许你读取和解析.ini文件。 使用 ini 库 添加依赖 首先,你需要在你的 Cargo.toml 文件中添加 ini 库的依赖&am…...

批量处理多个模型的预测任务
#!/bin/bash# 检查是否传入必要的参数,若未传入参数则打印用法并退出 if [ "$#" -lt 1 ]; thenecho "用法: $0 <file_path>"echo "示例: $0 /home/aistudio/work/PaddleSeg/city/cityscapes_urls_extracted.txt"exit 1 fi# 读取…...

Java 编程初体验
Java学习资料 Java学习资料 Java学习资料 一、引言 在当今数字化的时代,编程已然成为一项极具价值的技能。而 Java 作为一门广泛应用于企业级开发、移动应用、大数据等众多领域的编程语言,吸引着无数初学者投身其中。当我们初次踏入 Java 编程的世界&…...

element-plus 的table section如何实现单选
如果是单选那么全新的按钮应该隐藏或者不可编辑的状态。但是我没找到改变成不可编辑的方法,只能采取隐藏 <template><!-- 注意要包一层div根元素,否则css样式可能会不生效,原因不详 --><div><el-table ref"proTab…...

【JavaEE进阶】图书管理系统 - 壹
目录 🌲序言 🌴前端代码的引入 🎋约定前后端交互接口 🚩接口定义 🍃后端服务器代码实现 🚩登录接口 🚩图书列表接口 🎄前端代码实现 🚩登录页面 🚩…...

牛客周赛 Round 77 题解
文章目录 A-时间表B-数独数组D-隐匿社交网络E-1or0 A-时间表 签到题 #include <bits/stdc.h> using namespace std;int main() {int a[6] {20250121,20250123,20250126,20250206,20250208,20250211};int n; cin >> n;cout << a[n - 1];return 0; }B-数独数…...

Mybatis配置文件详解
MyBatis通过XML或注解的方式将Java对象与数据库中的记录进行映射,极大地简化了数据访问层的开发。而在MyBatis的核心组成部分中,配置文件扮演着举足轻重的角色。它不仅定义了MyBatis的运行环境,还配置了数据源、事务管理、映射器等关键元素&a…...