iOS APP包分析工具 | 京东云技术团队

介绍

分享一款用于分析iOSipa包的脚本工具,使用此工具可以自动扫描发现可修复的包体积问题,同时可以生成包体积数据用于查看。这块工具我们团队内部已经使用很长一段时间,希望可以帮助到更多的开发同学更加效率的优化包体积问题。

工具下载地址

背景

APPAnalyze工具最早诞生主要是为了解决以下包体积管理的问题:

对于定位下沉市场的APP来讲,包体积是一个非常重要的性能指标,包体积过大会影响用户下载APP的意愿。但是在早期我们缺少一些手段帮助我们更高效的去进行包体积管理。

自动发现问题
  • 提升效率- 人工排查问题效率低,对于常见的问题尽可能自动扫描出来。并且对于组件化工程来讲,很多外部组件是通过Framework方式提供,没有仓库源码权限用于分析包体积问题。

  • 流程化- 形成自动化的质量流程,添加到CI流水线自动发现包体积问题。

数据指标量化
  • 包体积问题- 提供数据化平台查看每个组件的包体积待修复问题

  • 包体积大小- 提供数据化平台查看每个组件的包体积占比,包括总大小,单个文件二进制大小和每个资源大小。可以针对不同的APP版本进行组件化粒度的包体积数据对比,更方便查看每个版本的组件大小增量。

实现方式

我们选择了不依赖源码而是直接扫描二进制库的方式来实现这个能力,总体的执行流程一下:
执行流程

提示:基于组件化工程的扫描方式内部支持,只是暂时不对外开放。

使用指南

安装

无需安装。通过下载链接直接下载终端可执行命令文件APPAnalyzeCommand到本地即可使用。

APPAnalyzeCommand 下载地址

使用

$ /Users/Test/APPAnalyzeCommand --help
OPTIONS:--version <version>     当前版本 1.0.0--output <output>       输出文件目录。必传参数--config <config>       配置JSON文件地址。非必传参数--ipa <ipa>             ipa.app文件地址。必传参数-h, --help              Show help information.

执行

打开终端程序直接执行以下shell指令,即可生成ipa的包体积数据以及包体积待修复问题。

提示:不能直接使用AppStore的包,AppStore的包需要砸壳。建议尽量使用XCodeDebug的包。

/Users/Test/APPAnalyzeCommand --ipa ipas/JDAPP/JDAPP.app --output ipas/JDAPP

提示:如果提示permission denied没有权限,执行sudo chmod -R 777 /Users/a/Desktop/ipas/APPAnalyzeCommand即可。

生成产物

生成产物
指令执行完成以后,会在ouput参数指定的文件夹生成APPAnalyze文件夹。具体文件介绍如下:

包体积信息
  • app_size.html- 展示ipa每个framework的包体积数据,可直接用浏览器打开。

提示:按照主程序和动态库进行粒度划分

app_size.html

  • framework_size.html- 展示单个framework所有的包体积数据,二级页面不要直接打开

framework_size.html

提示:XCode生成Assets.car时会将一些小图片拼接成一张PackedAssetImage的大图片。

  • package_size.json-ipa包体积 JSON 数据
包体积待修复问题
  • app_issues.html- 展示ipa每个framework的包体积待修复问题数量,可直接用浏览器打开。

提示:按照主程序和动态库进行粒度划分
app_issues.html

  • framework_issues.html- 展示单个framework所有的待修复问题详细数据,二级页面不要单独打开

framework_issues.html

  • issues.json-ipa待修复包体积问题 JSON 数据

提示:json数据可用于搭建自己的数据平台,扩展更多的能力。例如查看不同APP版本以及支持多个APP版本对比等。

规则介绍

规则

包体积

未使用的类

定义了类没有被使用到,包含ObjC类和Swift类。

扫描规则

  • 没有查到到对应的ObjC类被引用

  • 没有被当做父类使用

  • 没有使用的字符串和类名一致

  • 没有被当做属性类型使用

  • 没有被创建或调用方法

  • 没有实现+load方法

可选的修复方式

  • 移除未使用的类

  • Swift类如果只是用了static方法考虑修改成Enum类型

  • 如果只是在类型转换时使用了也会检测出是未使用的类,例如(ABCClass *)object;。建议检查是否真的有没有到相关类后删除

  • 对于ObjC,如果只是作为方法参数类型使用也会被检测出是未使用的类。建议删除相关方法即可。

提示:删除类相对是一种安全的行为,因为删除后如果有被使用到会产生编译时错误。虽然有做字符串调用的扫描过滤,不过还是建议检查是否可能被Runtime动态创建调用

未使用的ObjC协议

定义了ObjC协议没有被类使用

扫描规则

  • 对应的协议没有被类引用

可选的修复方式

  • 移除未使用的协议
Bundle内多Scale图片

Bundle内同一张图片包含多个Scale会导致更大的包体积。

扫描规则

  • 同一个Bundle内存在同名但是scale不同的图片。例如a@2x.png/a@3x.png

可选的修复方式

  • 移除Scale更低的图片
大资源

文件大小超过一定大小的即为大资源,默认为20KB

扫描规则

  • 某个文件超过设置的大资源限额

可选的修复方式

  • 移除资源动态下发

  • 使用更小的数据格式,例如使用更小的图片格式

重复的资源文件

存在多个同样的重复文件。

扫描规则
  • 多个文件MD5一致即判定为重复文件。

可选的修复方式

  • 移除多余的文件
未使用的类Property属性

ObjC类中定义的属性没有被使用到。

扫描规则

  • 对应的属性没有被调用 set/get 方法,同时也没有被_的方式使用

  • 不是来自实现协议的属性

  • 不是来自Category的属性

  • 不存在字符串使用和属性名一致

可选的修复方式

  • 移除对应的属性

  • 如果是接口协议的属性,需要添加类实现此接口

注意事项

  • 可能存在部分动态使用的场景,需要进行一定的检查。例如一些继承NSObject的数据模型类,可能存在属性没有被直接使用到,但是可能会被传唤成JSON作为参数的情况。例如后台下发的数据模型
未使用的ImageSet/DataSet

包含的Imageset/DataSet并没有被使用到。

扫描规则

  • 未检测到和Imageset同样名字的字符串使用

可选的修复方式

  • 移除ImageSet/DataSet

注意事项

  • 某些Swift代码中使用的字符串不能被发现所以会被当做未使用。

  • 使用字符串拼接的名字作为imageset的名字。

  • 被合成到PackedAssetImage里的Imageset不能被扫描出来

未使用的ObjC方法

定义的ObjCCategory 方法并未被使用到。

扫描规则

  • 不存在和此方法一样的方法名使用

  • 不存在使用的字符串和方法名一致

  • 不是来自父类或Category的方法

  • 不是来自实现接口的方法

  • 不是属性 set/get 方法

可选的修复方式

  • 移除对应方法
未使用的分类方法

定义的ObjCCategory 方法并未被使用到。

扫描规则

  • 不存在和此方法一样的方法名使用

  • 不存在和方法名一致的字符串使用

  • 不是来自父类或Category的方法

  • 不是来自实现接口的方法

可选的修复方式

  • 移除未使用的方法

  • 如果是接口协议的方法,需要添加类实现此接口

未使用的资源文件

包含的文件资源并没有被使用到。这里的资源不包含Imageset/DataSet

扫描规则

  • 未检测到和文件名同样名字的字符串使用

可选的修复方式

  • 移除资源

注意事项

  • 某些Swift代码中使用的字符串不能被发现所以会被当做未使用

  • 使用字符串拼接的名字作为资源的名字

安全

动态反射调用ObjC类

存在类名和字符串一致,可能使用NSClassFromString()方法动态调用类。当字符串或类名变更时无法利用编译时检查发现问题,可能会导致功能异常。

扫描规则

  • 存在使用的字符串NSObject子类类名相同

可选的修复方式

  • 使用NSStringFromClass()获取类名字符串

  • 使用Framework外部的类应该使用方法封装,除了少部分功能不应该使用反射去调用

提示:包含继承NSObject的 swift 类。

ObjC属性内存申明错误

一些特殊的NSObject类型的属性内存类型申明错误,可能会导致功能异常或触发Crash

扫描规则

  • NSArray/NSSet/NSDictionary类型的属性使用strong申明

  • NSMutableArray/NSMutableSet/NSMutableDictionary类型的属性使用copy申明

可选的修复方式

  • 修改strong/copy申明
冲突的分类方法

ObjC同一个类的多个Category分类中存在多个相同的方法,由于运行时最终会加载方法可能是不确定的,可能会导致功能异常等未知的行为。

扫描规则

  • NSObject类的多个Category分类中存在多个相同的方法

修复方式

  • 移除多余的分类方法
重复的分类方法

ObjC原始类和类的Category分类中有相同的方法,分类中的方法会覆盖原始类的方法,可能会导致功能异常等未知的行为。

扫描规则

  • NSObject原始类和类的Category分类中有相同的方法

修复方式

  • 移除重复的分类方法
未实现的ObjC协议方法

类实现了某个ObjC协议,但是没有实现协议的非可选方法。可能会导致功能异常或触发Crash

扫描规则

  • 分类未实现NSObject协议的非可选方法

可选的修复方式

  • 对应的类实现缺失的非可选协议方法

  • 将对应的协议方法标识为optional可选方法

重复的ObjC类

多个动态库静态库之间存在同样的。不会导致编译失败,但是运行时只会使用其中一个类,可能会导致功能异常或触发Crash。同时会增加包体积

扫描规则

  • 多个动态库静态库之间存在同样的NSObject类符号

可能的修复方式

  • 移除重复的类

性能

使用动态库

使用动态库会增加启动耗时。

扫描规则

  • Macho为动态库

可选的修复方式

  • 使用静态库

  • 使用Mergeable Library

实现+load方法的类

APP启动后会执行所有+load方法,减少+load方法可以降低启动耗时。

扫描规则

  • 实现+load方法的NSObject

可选的修复方式

  • 移除+load方法

  • 使用+initialize替代

自定义配置

重要配置

systemFrameworkPaths

可以基于自身项目进行系统库目录的配置,解析工程时也会对系统库进行解析。配置系统库目录对于未使用方法的查找可以提供更多的信息避免误报。但是配置更多会导致执行的更慢,建议至少配置Foundation/UIKit

unusedObjCProperty-enable

unusedObjCProperty规则默认不开启。

  • 开启未使用属性检查以后,会扫描macho__TEXT段,会增加分析的耗时。
unusedClass-swiftEnable

unusedClass-swiftEnable默认不开启。

  • 开启Swift类检查以后,会扫描macho__TEXT段,会增加分析的耗时。

  • 未使用Swift类的项目建议不要开启,如果考虑执行性能的话Swift使用相对比较多的再开启。

提示:扫描macho__TEXT段需要使用XCodeRun编译出的包,不能直接使用用于上架APP Store构建出的包。主要是Debug会包含更多的信息用于扫描。

配置属性

/Users/Test/APPAnalyzeCommand -ipa /Users/Desktop/ipas/APPMobile/APPMobile.app -config /Users/Desktop/ipas/config.json --output /Users/Desktop/ipas/APPMobile

可基于自身项目需要,添加下列规则可配置参数。在使用APPAnalyzeCommand指令时添加--config配置文件地址。

{"systemFrameworkPaths": ["/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore", "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation","/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Foundation.framework/Foundation"], // 配置系统库。会极大增加未使用方法的误报"rules": {"dynamicCallObjCClass": { // 动态调`ObjC类"enable": false, // 是否启用"excludeClasslist": [ // 过滤类名"NSObject","param"]},"incorrectObjCPropertyDefine": { // 错误的 ObjC 属性定义"enable": false // 是否启动},"largeResource": { // 大资源"maxSize": 20480 // 配置大资源判定大小。默认 20480Byte=20KB},"unusedObjCProperty": { // 未使用的 ObjC 属性"enable": false, // 是否启用。默认不开启"excludeTypes": ["NSString", "NSArray", "NSDictionary", "NSNumber", "NSMutableArray", "NSMutableDictionary", "NSSet"] // 过滤掉部分类型的属性},"unusedClass": { // 未使用的类"swiftEnable": false, // 是否支持 Swift 类。默认不支持"excludeSuperClasslist": ["JDProtocolHandler", "JDProtocolScheme"],// 如果类继承了某些类就过滤"excludeProtocols": ["RCTBridgeModule"], // 如果类实现了某些协议就过滤"excludeClassRegex": ["^jd.*Module$", "^PodsDummy_", "^pg.*Module$", "^SF.*Module$"] // 过滤掉名字符合正则表达式的类},"unusedObjCMethod": { // 未使用的 ObjC 方法"excludeInstanceMethods": [""], // 过滤掉某些名字的对象方法"excludeClassMethods": [""], // 过滤掉某些名字的类方法"excludeInstanceMethodRegex": ["^jumpHandle_"], // 过滤掉名字符合正则表达式的对象方法"excludeClassMethodRegex": ["^routerHandle_"], // 过滤掉名字符合正则表达式的类方法"excludeProtocols": ["RCTBridgeModule"] // 如果类集成了某些协议就不再检查,例如 RN 方法},"loadObjCClass": { //  调用 ObjC + load 方法"excludeSuperClasslist": ["ProtocolHandler"], // 如果类继承了某些类就过滤"excludeProtocols": ["RCTBridgeModule"] // 如果类实现了某些协议就过滤,例如 RN 方法},"unusedImageset": { // 未使用 imageset"excludeNameRegex": [""] // 过滤掉名字符合正则表达式的imageset},"unusedResource": { // 未使用资源"excludeNameRegex": [""] // 过滤掉名字符合正则表达式的资源}}
}

组件化工程扫描

可以基于APPAnalyzeCore.framework定制实现自己的组件化工程扫描,或者添加基于自身组件化工程的检查规则。详情可以看Demo

基于组件化扫描方式有以下优势:

  • 细化数据粒度- 可以细化每个模块的包体积和包体积问题,更容易进行包体积优化。

  • 更多的检查- 例如检查不同组件同一个Bundle包含同名的文件,不同组件包含同一个category方法的的实现。

  • 检查结果更准确- 例如ObjC未使用方法的检查,只要存在一个和方法名同样的调用就表示方法有被使用到。但是整个ipa中可能存在很多一样的方法名但是只有一个方法有真正被调用到,如果细分到组件的粒度就可以发现更多问题。

提示:只有APP主工程无代码,全部通过子组件以framework的形式导入二进制库的方式的工程才适合这种模式。

其他

扫描质量如何

这套工具我们团队内部开发加逐步完善有一年的时间了。基于此工具修改了几十个组件的包体积问题,同时不断的修复误报问题。目前现有提供的这些规则检查误报率是很低的,只有极少数几个规则可能存在误报的可能性,总体扫描质量还是很高的。

和社区开源的工具有什么差异

我们在早期调研了社区的几个同类型的开源工具,主要存在以下几个问题:

  • 扩展性不够- 无法支持项目更好的扩展定制能力,例如添加扫描规则、支持不同类型扫描方式、生成更多的报告类型。

  • 功能不全- 只提供部分能力,例如只提供未使用资源或者未使用类

  • 无法生成包体积数据- 无法生成包体积完整的数据。

  • 检查质量不高- 扫描发现的错误数据多,或者有一些问题不能被发现。

开源计划

后续一定会开源。主要是希望调整一些内部结构再开源,开源后就不方便调整。顺便修复一些常见的问题。

开源带来的好处

开源带来的好处是,部分工程可以基于自身的业务需要,扩展定制自己的扫描工具。同时也可以将一些更好的想法实现添加进来。

  • 扩展解析方式- 目前只支持ipa模式扫描,很快会开放支持project组件化工程的扫描方式。基于组件化工程的扫描可以更加准确,但是不同的公司组件化工程的构建方式可能是不一样的,有需要可以在上层定制自身组件化工程的扫描解析。

  • 扩展扫描规则- 虽然现在已经添加了比较多的通用性的规则,同时提供了一定的灵活性配置能力。但是不同的项目可能需要定制一些其他的规则,这些规则没办法通过在现有规则上添加配置能力实现。

  • 扩展数据生成- 默认包里只包含两种数据生成,包体积数据还有包体积待修复问题数据。可以扩展更多的数据生成格式,例如我们自身的项目就有添加基于组件的依赖树格式。

后续规划

组件化工程支持

添加更多用于组件化工程的扫描

对于 Swift 更好的支持

对于Swift语言只要开启XCode编译优化以后就能在生成产物的时候支持无用代码的移除,包括未使用类型未使用方法的自动移除,但是依然有部分场景不会进行优化。所以这一块也是后续完善的重点:

  • 未使用属性- 编译器不会对于未使用属性进行移除,包括classstruct的属性。

  • 未使用方法- 对于class的方法,编译器并不会进行移除,即使没有申明[@objc](https://my.oschina.net/TnhqVdFXL8vnu)进行消息派发。

相关链接

  • Github地址

作者:京东零售 何骁

来源:京东云开发者社区 转载请注明来源

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/165778.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在VMware Workstation的Centos上实现KVM虚拟机的安装部署:详细安装部署过程(保姆级)

KVM概述 • 以色列qumranet公司研发&#xff0c;后被RedHad公司收购 &#xff08;1&#xff09;kvm只支持x86平台 &#xff08;2&#xff09;依赖于 HVM,inter VT AMD-v • KVM是&#xff08;Kernel-based Virtual Machine&#xff09;的简称&#xff0c;是一个开源的系统虚拟…

【Unity】 UGUI的PhysicsRaycaster (物理射线检测)组件的介绍及使用

1. 什么是PhysicsRaycaster组件&#xff1f; PhysicsRaycaster是Unity UGUI中的一个组件&#xff0c;用于在UI元素上进行物理射线检测。它可以检测鼠标或触摸事件是否发生在UI元素上&#xff0c;并将事件传递给相应的UI元素。 2. PhysicsRaycaster的工作原理 PhysicsRaycast…

【Proteus仿真】【51单片机】智能垃圾桶设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真51单片机控制器&#xff0c;使用报警模块、LCD1602液晶模块、按键模块、人体红外传感器、HCSR04超声波、有害气体传感器、SG90舵机等。 主要功能&#xff1a; 系统运行后&#xf…

基于GPRS的汽车碰撞自动报警系统(论文+源码)

1. 系统设计 本次基于GPRS的汽车碰撞自动报警系统的设计中&#xff0c;其主要的目标功能如下&#xff1a;1、实时检测当前的GPS精度和纬度坐标&#xff1b;2.当发生碰撞后系统自动将当前的信息通过GPRS数据发送到远端数据进行报警&#xff1b;3、系统在碰撞后一方面进行本地报警…

听GPT 讲Rust源代码--src/tools(2)

题图来自AI生成 File: rust/src/tools/rust-analyzer/crates/hir-def/src/src.rs rust-analyzer 是一个 Rust 语言的语法分析器和语义分析器&#xff0c;用于提供代码补全、导航、重构等开发工具。而 rust-analyzer 的代码实现存储在 rust/src/tools/rust-analyzer 这个文件夹中…

010 OpenCV中的4种平滑滤波

目录 一、环境 二、平滑滤波 2.1、均值滤波 2.2、高斯滤波 2.3、中值滤波 2.4、双边滤波 三、完整代码 一、环境 本文使用环境为&#xff1a; Windows10Python 3.9.17opencv-python 4.8.0.74 二、平滑滤波 2.1、均值滤波 在OpenCV库中&#xff0c;blur函数是一种简…

递归剪枝题

期中考终于考完了&#xff0c;整道题奖励下自己 我一北大同学问我的&#xff0c;说他递归超时了&#xff0c;叫我想一个办法 后面他说他加了个剪枝就过了&#xff0c;然后我自己尝试了一个方法&#xff1a; 就是先把城市按1到n排列&#xff0c;然后考虑互换&#xff0c;如果互…

考过了PMP,面试的时候应该怎么办?

近期喜番在后台收到了很多同学们的私信&#xff0c;表示自己已经过了8月份的PMP考试&#xff0c;开始着手往项目管理岗位转型&#xff0c;但是对于项目管理岗位的面试却一筹莫展。放轻松&#xff0c;大家的需求喜番都了解了&#xff0c;喜番给大家总结了一些项目经理在面试的时…

SpringCloud 微服务全栈体系(十七)

第十一章 分布式搜索引擎 elasticsearch 七、搜索结果处理 搜索的结果可以按照用户指定的方式去处理或展示。 1. 排序 elasticsearch 默认是根据相关度算分&#xff08;_score&#xff09;来排序&#xff0c;但是也支持自定义方式对搜索结果排序。可以排序字段类型有&#…

【Python】Fastapi swagger-ui.css 、swagger-ui-bundle.js 无法加载,docs无法加载,redocs无法使用

使用fastapi的时候&#xff0c;swagger-ui.css 、swagger-ui-bundle.js、redoc.standalone.js 有时候无法加载&#xff08;国内环境原因或者是局域网屏蔽&#xff09;&#xff0c;此时就需要自己用魔法下载好对应文件&#xff0c;然后替换到fastapi里面去。 fastapi里面依靠这…

java学习part07数组工具类

1比较内容 2输出信息 3值填充 4快速排序 5二分查找 负数没找到&#xff0c;其他表示下标

ES6 — ES14 新特性

一、ES6 新特性&#xff08;2015&#xff09; 1. let和const 在ES6中&#xff0c;新增了let和const关键字&#xff0c;其中 let 主要用来声明变量&#xff0c;而 const 通常用来声明常量。let、const相对于var关键字有以下特点&#xff1a; 特性varletconst变量提升✔️全局…

【漏洞复现】金蝶云星空管理中心 ScpSupRegHandler接口存在任意文件上传漏洞 附POC

漏洞描述 金蝶云星空是一款云端企业资源管理(ERP)软件,为企业提供财务管理、供应链管理以及业务流程管理等一体化解决方案。金蝶云星空聚焦多组织,多利润中心的大中型企业,以 “开放、标准、社交”三大特性为数字经济时代的企业提供开放的 ERP 云平台。服务涵盖:财务、供…

【MySQL】mysql中不推荐使用uuid或者雪花id作为主键的原因以及差异化对比

文章目录 前言什么是UUID?什么是雪花ID?什么是MySql自增ID?优缺点对比UUID:优点1.全球唯一性2.无需数据库支持 缺点1.存储空间大2.索引效率低3.查询效率低 雪花ID&#xff1a;优点1.分布式环境下唯一性 缺点1.依赖于机器时钟2.存储空间较大3.查询效率低 MYSQL自增:优点1.简单…

IP 代理的基础知识有哪些?

本文将介绍流冠IP代理的基础知识&#xff0c;帮助您了解IP代理的概念、类型、作用、设置方法和注意事项。 一、IP代理的概念 IP代理是一种网络代理服务&#xff0c;它通过代理服务器帮助用户访问互联网&#xff0c;并将用户的请求转发到目标网站&#xff0c;同时将目标网站的响…

手写工作流设计模式,针对常见的工作流步骤流转,减少过多的if/else,提升编程思维

需求 这一年下来&#xff0c;写两次工作流流转&#xff0c;总结下经验。 第一次写的时候&#xff0c;只找到用模版设计模式包裹一下&#xff0c;每个方法都做隔离&#xff0c;但是在具体分支实现的时候&#xff0c;if/else 满屏分&#xff0c;而且因为要针对不同情况&#xff…

微信小程序实现类似Vue中的computed、watch功能

微信小程序实现类似Vue中的computed、watch功能 构建npm使用 构建npm 创建包管理器 进入小程序后&#xff0c;打开终端&#xff0c;点击顶部“视图” - “终端” 新建终端 使用 npm init -y初始化包管理器&#xff0c;生成一个package.json文件 安装 npm 包 npm install --…

Java Web 实战 21 - 用 Servlet 实现一个Hello World

用 Servlet 来写一个 Hello World~ 一 . 基本部署方式1.1 创建 Servlet 项目1.2 引入依赖1.3 创建目录1.4 编写代码继承 HttpServlet重写 doGet 方法删除 super 方法加上 WebServlet 注解写业务逻辑 1.5 打包1.6 部署1.7 验证1.8 小结 二 . 更方便的部署方式2.1 Smart Tomcat 的…

本地部署 ComfyUI

本地部署 ComfyUI ComfyUI 介绍ComfyUI Github 地址部署 ComfyUI配置模型地址 or 下载模型启动 ComfyUI访问 ComfyUI ComfyUI 介绍 最强大、模块化的稳定扩散 GUI 和后端。 该用户界面将允许您使用基于图形/节点/流程图的界面设计和执行高级稳定扩散管道。 ComfyUI Github 地…

第五天 用Python批量处理Excel文件,实现自动化办公

用Python批量处理Excel文件&#xff0c;实现自动化办公 一、具体需求 有以下N个表&#xff0c;每个表的结构一样&#xff0c;如下&#xff1a; 需要把所有表数据汇总&#xff0c;把每个人的得分、积分分别加起来&#xff0c;然后按总积分排名&#xff0c;总积分一致时&#xff…