前言
上篇文章《总听说AGP,它到底做了什么?》和大家分析了 AGP(Android Gradle Plugin) 做了哪些事,了解到 AGP 就是为打包这个过程服务的。
那么,本篇文章就和大家聊一聊其中的 Transform,解决一下为什么在 AGP 3.x.x 的版本可以通过反射获取的 transformClassesWithDexBuilderForXXX
Task 在 4.0.0 的版本就不灵了?
源码走起!
一、Transform的流程
读本篇文章以前,相信同学们已经具备 Transform 的使用基础。
相信很多人都看过这张图:
正如上图中展示的,我么可以看到:
在一个项目中,我们可能既会有自定义的 Transform
,也会有系统的 Transform
在处理过程中,每一个 Transform 的接受流都是接收到上一个 Transform 的输出流,原始的文件流会经过很多 Transform 的处理
二、Transform源码分析
既然我们已经了解了整体的流程,再来看一下其中的细节吧。
第一步 Transform的起点
我们都知道,使用 Transform 的目的,是为了修改其中的字节码,那么,这些 Class 文件是哪里来的呢?
直接打开 AGP 的源码,直接跳到创建编译 Task 的时候,这个方法发生在 AGP 创建跟 Variant 相关的 Task 的时候,在 AbstractAppTaskManager
里:
private void createCompileTask(@NonNull VariantPropertiesImpl variantProperties) {
ApkCreationConfig apkCreationConfig = (ApkCreationConfig) variantProperties;
// 执行javac
TaskProvider javacTask = createJavacTask(variantProperties);
// 添加Class输入流
addJavacClassesStream(variantProperties);
setJavaCompilerTask(javacTask, variantProperties);
// 执行transform和dex相关的任务
createPostCompilationTasks(apkCreationConfig);
}
虽然只有几个方法,但是每个方法的作用还挺大,先看 javac。
第二步 执行javac
大家对 javac 的命令肯定很熟悉,它可以将 .java 文件转化成 .class 文件。这个方法确实也是这样:
public TaskProvider createJavacTask(
@NonNull ComponentPropertiesImpl componentProperties) {
// Java预编译任务,看了一下,主要是处理Java注解
taskFactory.register(new JavaPreCompileTask.CreationAction(componentProperties));
// Java编译任务
final TaskProvider javacTask =
taskFactory.register(new JavaCompileCreationAction(componentProperties));
postJavacCreation(componentProperties);
return javacTask;
}
它的方法注释:
Creates the task for creating *.class files using javac. These tasks are created regardless of whether Jack is used or not, but assemble will not depend on them if it is. They are always used when running unit tests.
很明显,就是为了创建 .class 文件。
这一步中,最重要的一步就是注册了一个名叫 JavaCompile
的任务,也就是将 Java 文件和 Java 注解转变成 .class 的 Task。
JavaCompile
的 Task
的代码比较绕,直接跟大家说结果了,最终是调用 JDK 下面的 JavaCompiler
类,动态将 .java
转化成 .class
文件。
当然,不仅仅只有 .class
文件,还有其他的诸如 .kt
和 .jar
等,都需要特定的 Task,才能转化成我们需要的输入源。
第三步 建立原始的输入流
回到第一步,进入 addJavacClassesStream
方法:
protected void addJavacClassesStream(@NonNull ComponentPropertiesImpl componentProperties) {
// create separate streams for the output of JAVAC and for the pre/post javac
// bytecode hooks
TransformManager transformManager = componentProperties.getTransformManager();
boolean needsJavaResStreams =
componentProperties.getVariantScope().getNeedsJavaResStreams();
transformManager.addStream(
OriginalStream.builder(project, "javac-output")
// Need both classes and resources because some annotation
// processors generate resources
.addContentTypes(
needsJavaResStreams
? TransformManager.CONTENT_JARS
: ImmutableSet.of(DefaultContentType.CLASSES))
.addScope(Scope.PROJECT)
.setFileCollection(project.getLayout().files(javaOutputs))
.build());
BaseVariantData variantData = componentProperties.getVariantData();
transformManager.addStream(
OriginalStream.builder(project, "pre-javac-generated-bytecode")
.addContentTypes(
needsJavaResStreams
? TransformManager.CONTENT_JARS
: ImmutableSet.of(DefaultContentType.CLASSES))
.addScope(Scope.PROJECT)
.setFileCollection(variantData.getAllPreJavacGeneratedBytecode())
.build());
transformManager.addStream(
OriginalStream.builder(project, "post-javac-generated-bytecode")
.addContentTypes(
needsJavaResStreams
? TransformManager.CONTENT_JARS
: ImmutableSet.of(DefaultContentType.CLASSES))
.addScope(Scope.PROJECT)
.setFileCollection(variantData.getAllPostJavacGeneratedBytecode())
.build());
}
这个 transformManager
就是处理 Transform
的,它在建立第一个 Transform
的原始数据流。
细心的同学可能发现了,第一个数据流的 contentType
至少也是 DefaultContentType.CLASSES
,scope
是 Scope.PROJECT
,自定义过 Transform
的同学肯定知道,这样设置我们自定义的 Transform
能够接收到原始数据流。
第四步 创建编译后的任务
回到第一步中的 createPostCompilationTasks
方法,它用来创建编译后的任务:
public void createPostCompilationTasks(@NonNull ApkCreationConfig creationConfig) {
//...
TransformManager transformManager = componentProperties.getTransformManager();
// ...
// java8脱糖
maybeCreateDesugarTask(
componentProperties,
componentProperties.getMinSdkVersion(),
transformManager,
isTestCoverageEnabled);
BaseExtension extension = componentProperties.getGlobalScope().getExtension();
// Merge Java Resources.
createMergeJavaResTask(componentProperties);
// ----- External Transforms -----
// apply all the external transforms.
List customTransforms = extension.getTransforms();
List> customTransformsDependencies = extension.getTransformsDependencies();
boolean registeredExternalTransform = false;
for (int i = 0, count = customTransforms.size(); i < count; i++) {
Transform transform = customTransforms.get(i);
List deps = customTransformsDependencies.get(i);
registeredExternalTransform |=
transformManager
.addTransform(
taskFactory,
componentProperties,
transform,
null,
task -> {
if (!deps.isEmpty()) {
task.dependsOn(deps);
}
},
taskProvider -> {
// if the task is a no-op then we make assemble task depend on it.
if (transform.getScopes().isEmpty()) {
TaskFactoryUtils.dependsOn(
componentProperties
.getTaskContainer()
.getAssembleTask(),
taskProvider);
}
})
.isPresent();
}
// Add a task to create merged runtime classes if this is a dynamic-feature,
// or a base module consuming feature jars. Merged runtime classes are needed if code
// minification is enabled in a project with features or dynamic-features.
if (componentProperties.getVariantType().isDynamicFeature()
|| variantScope.consumesFeatureJars()) {
taskFactory.register(new MergeClassesTask.CreationAction(componentProperties));
}
// ----- Minify next -----
// 混淆
// ----- Multi-Dex支持...
// 创建 dex
createDexTasks(
creationConfig, componentProperties, dexingType, registeredExternalTransform);
// ... 资源压缩等
}
在进行 Transform 之前,它还会进行一些 java8 的脱糖以及合并 java 资源的 Task,这些都是会被添加到原始的数据流中。
第五步 为Transfrom创建Task
首先,我们得明白,每一种 Transform 其实有两种类型:
消费型:需要将数据源输出给下一个 Transform
引用型:只需要读取,不需要输出
接下来就到了我们关心的处理 Transform 的逻辑了。
从上面的方法我们可以看出,系统会为我们找到所有已经在 BaseExtension
注册的 Transform 并遍历,使用 transformManager
通过 addTransform
做处理:
public Optional> addTransform(
@NonNull TaskFactory taskFactory,
@NonNull ComponentPropertiesImpl componentProperties,
@NonNull T transform,
@Nullable PreConfigAction preConfigAction,
@Nullable TaskConfigAction configAction,
@Nullable TaskProviderCallback providerCallback) {
//... 省略
List inputStreams = Lists.newArrayList();
String taskName = componentProperties.computeTaskName(getTaskNamePrefix(transform));
// get referenced-only streams
List referencedStreams = grabReferencedStreams(transform);
// find input streams, and compute output streams for the transform.
IntermediateStream outputStream =
findTransformStreams(
transform,
componentProperties,
inputStreams,
taskName,
componentProperties.getGlobalScope().getBuildDir());
// ... 检测工作
transforms.add(transform);
TaskConfigAction wrappedConfigAction =
t -> {
t.getEnableGradleWorkers()
.set(
componentProperties
.getGlobalScope()
.getProjectOptions()
.get(BooleanOption.ENABLE_GRADLE_WORKERS));
if (configAction != null) {
configAction.configure(t);
}
};
// create the task...
return Optional.of(
taskFactory.register(
new TransformTask.CreationAction<>(
componentProperties.getName(),
taskName,
transform,
inputStreams,
referencedStreams,
outputStream,
recorder),
preConfigAction,
wrappedConfigAction,
providerCallback));
}
这里呢,先定义了一个 taskName
,规则是:
transform${inputType}With${transformName}For${BuildType}
关于 taskName
规则先放这儿,后面我们会用到!
上面代码中的 referencedStreams
用来处理引用型的 Transform
,所以我们着重看 outputStream
,outputStream
是通过方法 findTransformStreams
方法生成的,关于数据流向的问题这个方法里面讲的特别明白:
private final List streams = Lists.newArrayList();
private IntermediateStream findTransformStreams(
@NonNull Transform transform,
@NonNull ComponentPropertiesImpl componentProperties,
@NonNull List inputStreams,
@NonNull String taskName,
@NonNull File buildDir) {
//...
// 消费数据流,inputStreams添加需要消费的数据流
// 1. inputStreams会消费掉streams可以消费的数据流
consumeStreams(requestedScopes, requestedTypes, inputStreams);
Set outputTypes = transform.getOutputTypes();
File outRootFolder =
FileUtils.join(
buildDir,
StringHelper.toStrings(
AndroidProject.FD_INTERMEDIATES,
FD_TRANSFORMS,
transform.getName(),
componentProperties.getVariantDslInfo().getDirectorySegments()));
// 创建输出流
IntermediateStream outputStream =
IntermediateStream.builder(
project,
transform.getName() + "-" + componentProperties.getName(),
taskName)
.addContentTypes(outputTypes)
.addScopes(requestedScopes)
.setRootLocation(outRootFolder)
.build();
// 2. 为下一个Transform添加生成的数据流
streams.add(outputStream);
return outputStream;
}
流程如图:
意思就是每一个 Transform 都要走一遍图中的流程,对于大部分 Transform 来说,每一个的输入源就是上一个Transform 的输出源。
所以对于开发者来说,如果我们定义 Transform
却不将生成的文件添加到输出目录,这就会导致后面的 Transform 找不到输入源,编译器就只能报错了。
这个错误我最近才犯过。
回到这一步的开始,taskFactory
最终为我们注册了一个 TransformTask
。
第六步 TransformTask做了什么
进入 TransformTask
这个类,里面有一个方法 transform
添加了 @TaskAction
注解,所以,一旦该 Task 执行了,这个方法就会被调用。
@TaskAction
void transform(final IncrementalTaskInputs incrementalTaskInputs)
throws IOException, TransformException, InterruptedException {
// 设置增量编译
isIncremental.setValue(transform.isIncremental() && incrementalTaskInputs.isIncremental());
// ...
recorder.record(
ExecutionType.TASK_TRANSFORM_PREPARATION,
preExecutionInfo,
getProjectPath().get(),
getVariantName(),
new Recorder.Block() {
@Override
public Void call() throws Exception {
// ... 针对增量编译对文件处理
return null;
}
});
GradleTransformExecution executionInfo =
preExecutionInfo.toBuilder().setIsIncremental(isIncremental.getValue()).build();
recorder.record(
ExecutionType.TASK_TRANSFORM,
executionInfo,
getProjectPath().get(),
getVariantName(),
new Recorder.Block() {
@Override
public Void call() throws Exception {
// ...
transform.transform(
new TransformInvocationBuilder(context)
.addInputs(consumedInputs.getValue())
.addReferencedInputs(referencedInputs.getValue())
.addSecondaryInputs(changedSecondaryInputs.getValue())
.addOutputProvider(
outputStream != null
? outputStream.asOutput()
: null)
.setIncrementalMode(isIncremental.getValue())
.build());
if (outputStream != null) {
outputStream.save();
}
return null;
}
});
}
recorder
不用管,它只是一个执行器,最终会执行 Block
中的代码。
如果是增量编译的 Task,它会处理文件,告诉我们哪些文件变化了。
之后,就执行 Transform
的 transform
方法,整个 Transform 就结束了。
第七步 DexBuild
回到第四步,AGP 会我们先后注册了混淆和多 Dex 支持的 Task,之后就到了创建 Dex 的 Task:
private void createDexTasks(
@NonNull ApkCreationConfig apkCreationConfig,
@NonNull ComponentPropertiesImpl componentProperties,
@NonNull DexingType dexingType,
boolean registeredExternalTransform) {
// ...
taskFactory.register(
new DexArchiveBuilderTask.CreationAction(
dexOptions,
enableDexingArtifactTransform,
componentProperties));
//...
}
DexArchiveBuilderTask
就是名为 dexBuilder
的任务,它的注释:
Task that converts CLASS files to dex archives
它就是创建 dex 文件的 Task。
如果想要对 Dex 有进一步的了解,可以阅读:
《浅谈 Android Dex 文件》
到了这一步,我们的源码分析就结束了。
三、解决问题
之前我一直说 AGP 3.x.x 的时候可以 hook 到 transformClassesWithDexBuilderForXXX
的 task
,到了 AGP 4.x.x 就不行了。
仔细看一下我上面提到 taskName
命名规则,就会发现,在 3.x.x 之前,transformClassesWithDexBuilderForXXX
其实是一个 Transform
,我记得对应的类 DexTransform
,它会帮助 AGP 生成 .dex 文件。
而在 4.1.1 的代码中,这个任务交给了 DexArchiveBuilderTask
,已经不是一个 Transform
了。
所以啊,经常看到安卓开发者骂骂咧咧的说:卧槽,AGP版本升级了,我的这个方法不能用了!
因此,得出结论,在 AGP 上,最好还是不要去 hook 源码,建议使用官方推荐的接口去处理。
总结
本篇文章的内容其实是对上面 Transform 流程的验证,相信大家已经对 Transform 流程有了整体的把握!
如有什么争议的内容,欢迎评论区留言,如果觉得本文不错,「点赞」是对本文最大的肯定!
文章引用:
《一起玩转Android项目中的字节码》
你可能感兴趣的:(谨慎hook,一个hook Transform源码导致的错误!)
【Python百日进阶-Web开发-FastAPI】Day801 - FastAPI是什么
岳涛@泰山医院
Dash python 前端 fastapi
文章目录一、官网二、FastAPI是什么三、FastAPI特性3.1基于开放标准3.2自动生成文档3.3更主流的Python3.4编辑器支持3.5简洁3.6验证3.7安全性及身份验证¶3.8依赖注入3.9无限制"插件"3.10测试四、Starlette特性五、Pydantic特性六、Python类型提示简介6.1动机6.1.1简单示例6.1.2修改示例6.1.3添加类型6.2更多动机6.3声明类型
算法篇1:二分查找
呀呀猴
算法 算法 经验分享 java 其他 python
数组篇算法一:二分查找详解零、问题描述给定一个n个元素有序的(升序)整型数组nums和一个目标值target,编写一个函数搜索nums中的target。若目标值存在返回下标,否则返回-1。示例:输入:nums=[-1,0,3,5,9,12],target=9输出:4一、算法适用条件有序性:数组必须按升序或降序排列(通常假设升序)。唯一性(非必须):若数组有重复元素,需明确查找目标(如第一个/最后一
Python自动化运维实战,怎么构建分布式质量监控平台
好知识传播者
Python实例开发实战 运维 python 自动化 分布式质量监控平台 linux
注意:本文的下载教程,与以下文章的思路有相同点,也有不同点,最终目标只是让读者从多维度去熟练掌握本知识点。下载教程:Python自动化运维项目开发实战_构建分布式质量监控平台_编程案例解析实例课程教程.pdf一、引言随着企业业务的不断扩展和复杂化,系统运维面临着越来越多的挑战。传统的运维方式已经无法满足现代企业的需求,因此,自动化运维成为了企业提升运维效率、保障系统稳定性的重要手段。在自动化运维中
吲哚菁绿标记牛血清白蛋白|ICG-BSA
星贝爱科
吲哚菁绿标记牛血清白蛋白 ICG-BSA
吲哚菁绿标记牛血清白蛋白(ICG-BSA)是一种将吲哚菁绿(ICG)与牛血清白蛋白(BSA)结合形成的复合物,具有以下特点和应用:特点高荧光亮度:ICG具有较高的荧光亮度,使得ICG-BSA在荧光成像和检测中具有较高的信噪比和灵敏度。良好的生物相容性:ICG-BSA在生物体内具有良好的生物相容性,对细胞和组织无毒副作用,适用于生物医学研究和生物技术应用。稳定性好:ICG-BSA复合物在水溶液中稳定
HTML中 video标签样式铺满全屏
小华0000
css 前端
video标签默认不是铺满的,即使手动设置宽高100%也不会生效,所以当需要video铺满div时,需要加上一个css样式关键是这个“object-fit:fill”,这样就可以解决了!object-fit属性指定元素的内容应该如何去适应指定容器的高度与宽度。object-fit一般用于img和video标签,一般可以对这些元素进行保留原始比例的剪切、缩放或者直接进行拉伸等。fill:默认,不保证
vue3中子组件调用父组件事件
小华0000
vue.js javascript 前端
在Vue3中,子组件调用父组件的事件(或方法)的方式与Vue2类似,但Vue3引入了CompositionAPI,这可能会改变你组织代码的方式。不过,基本的通信机制——通过自定义事件($emit)通知父组件——仍然保持不变。以下是如何在Vue3中使用OptionsAPI和CompositionAPI的示例:使用OptionsAPI父组件(ParentComponent.vue)父组件importC
js的some函数
小华0000
javascript 开发语言 ecmascript
在JavaScript中,some()是一个数组的方法,用于测试数组中是否至少有一个元素满足提供的函数。如果数组中有至少一个元素通过由提供的函数实现的测试,则它返回true;否则返回false。以下是some()函数的基本语法:javascript复制代码arr.some(function(currentValue,index,arr),thisArg);currentValue:必需。当前元素的
js的includes函数
小华0000
javascript 前端 开发语言
在JavaScript中,includes()是一个数组(Array)和字符串(String)对象的方法,用于确定一个数组是否包含一个特定的值,或者一个字符串是否包含一个特定的子串。如果找到该值或子串,则返回true;否则返回false。数组中的includes()对于数组,includes()方法用于判断一个数组是否包含一个指定的值,根据情况,如果需要,可以指定开始搜索的位置。语法:arr.in
跨境独立站搭建与运营全攻略
青舰海淘SaaS代购独立站
跨境独立站知识地图 科技
随着跨境电商的日益火爆,越来越多的商家开始寻求搭建自己的跨境独立站。然而,从选择商业模式到后端的收款结汇,每一个环节都充满了挑战。本文将为你详细解读跨境独立站搭建与运营的全流程,帮你在运营独立站的过程中少走弯路(文章还有详细的思维导图可供下载)。一、商业模式选择在搭建跨境独立站之前,首先需要明确自己的商业模式。无货源模式、有货源模式、虚拟产品、垂直细分领域以及仿牌与黑五类,每一种模式都有其独特的优
探秘 C++:从基础语法到复杂项目实践的全攻略(一)
小周不想卷
艾思科蓝学术会议投稿 java 开发语言
目录C++是什么搭建开发环境常见的IDE介绍安装步骤与简单配置创建和运行C++项目基础语法入门变量与数据类型运算符与表达式控制结构C++是什么C++是一种强大的编程语言,它的历史可以追溯到20世纪70年代末。当时,计算机科学家比雅尼・斯特劳斯特鲁普(BjarneStroustrup)在贝尔实验室工作,他希望扩展C语言以支持面向对象编程(OOP),最初的工作被称为“CwithClasses”,这是C
考研高数(洛必达法则的使用条件)
蓝桉802
考研
洛必达法则的使用条件主要包括以下几点:1.导函数存在且连续。这是使用洛必达法则的基本要求,只有在满足这一条件下,我们才能对分子和分母同时求导。2.洛必达仅适用于求解后极限存在的情形。如果在使用洛必达法则后得到的极限不存在,那么原极限可能存在也可能不存在,此时需要采用其他方法进行判断或计算。3.洛必达只能正用不能逆用。也就是说,我们只能由分子分母同时求导以后获得的极限来推断原极限的值,而不能反过来
运输层和应用层之间的接口和端口有什么关系
蓝桉802
计算机网络 考研
运输层和应用层之间的接口是通过端口实现的。运输层使用端口号作为应用层与运输层之间的接口,确保不同应用进程之间的数据传输和通信。运输层的主要功能包括复用和分用。复用是指多个应用层的进程可以共享同一个运输层的连接,而分用则是将运输层收到的数据正确地分发到正确的应用层进程。这些功能的实现都依赖于端口号。端口号的作用是为了解决不同操作系统中使用不同格式的进程标识符的问题。由于进程的创建和撤销是动态的,
当x趋于零时,零乘以无穷的极限等于多少
蓝桉802
考研
当x趋于零时,零乘以无穷的极限是未定义。在数学中,0乘以无穷大(0×∞)是一个未定义的表达式,因为它涉及到两个相互矛盾的概念:0乘以任何有限数都等于0,而无穷大乘以任何非零数都应该是无穷大。因此,我们不能确定0乘以无穷大应该是0还是无穷大,所以它被认为是未定义的。为了更好地理解这个问题,我们可以考虑一个极限的例子。假设我们有两个函数f(x)和g(x),其中f(x)趋于0,而g(x)趋于无穷大。
怎么设置腾讯云CDN缓存规则
幻影龙王
腾讯云 缓存 服务器 CDN缓存规则 CDN
CDN主要功能是在不同的地点缓存内容,通过负载均衡技术,将用户的请求定向到最合适的缓存服务器上去获取内容,比如说,是北京的用户,我们让他访问北京的节点,深圳的用户,我们让他访问深圳的节点。通过就近访问,加速用户对网站的访问。解决Internet网络拥堵状况,提高用户访问网络的响应速度。今天就把我设置的方法贡献出来,可能不是那么完美,但是的的确确起到了简单的防护和加速作用,而且新用户有免费半年的流量
分布式服务发现与注册中心 Consul
要加油呀
中间件 java-consul consul java
分布式服务发现与注册中心Consulgithub地址:https://github.com/consul/consul基础概念什么是注册中心随着微服务理论发展的成熟,越来越多互联网公司采用微服务架构来支持业务发展。各个微服务之间都需要通过注册中心来实现自动化的注册和发现。注册中心主要有三种角色:服务提供者(RPCServer):在启动时,向Registry注册自身服务,并向Registry定期发送
深入Android HandlerThread 使用及其源码完全解析
ThreadLocalForrest
android java ui
本篇我们将来给大家介绍HandlerThread这个类,以前我们在使用线程执行一个耗时任务时总会new一个Thread的线程去跑,当任务执行完后,线程就会自动被销毁掉,如果又由新的任务,我们又得新建线程.....我们假设这样的一个情景,我们通过listview去加载图文列表,当我们往下滑动时,这时需要不断去请求网络资源,也就是需要不断开线程去加载网络资源,如果每次都new一个Thread,这显然是
fastapi 大型应用_FastApi项目实战 - 爱投票管理系统(一)
赌徒梦
fastapi 大型应用
一、闲来无事,在工作之余自己研究了一下python的异步框架-fastapi,并写包括1、部门管理2、角色管理3、用户管理4、菜单管理5、登录日志6、操作日志7、添加反爬机制六个基础功能模块,演示链接:https://www.lovevote.cn,账号:admin,密码:admin由于我的电脑问题,下载的vue依赖包有几个发生了版本冲突,打包部署后偶尔会在点击菜单按钮时发生前端延时(这时表现为缓
python析构_【python 类的析构】
盐选科普
python析构
简介很多面向对象的语言都提供了new关键字,通过new可以创建类的实例。Python的方式更加简单,一旦定义了一个类,直接使用函数操作符,即可创建类的实例。本文主要结合一些实际的例子,介绍了Python类的构造,初始化和析构的原理。类的构造与初始化Python涉及类的构造与初始化,有两个重要的方法:__new__()和__init__()和方法。前者完成实例对象的创建,后者完成对创建的实例对象的初
ubuntu14.04 sqlite3 及可视化工具安装
Andre-S
linux工具 可视化工具 sqlite3 界面 数据库 ubuntu
1.安装sqlite3sudoapt-getinstallsqlite3检查版本:sqlite3-version2.使用方法查看帮助信息.help创建数据库sqlite3test.db查询数据库信息.database查看该数据库内的表信息.tables退出.exit3.安装图形化界面sudoapt-getinstallsqlitebrowser4.使用方法sqlitebrowsertest.db注
分布式系统架构设计原理与实战:理解分布式系统的基本概念
AI天才研究院
计算 大数据 人工智能 语言模型 AI LLM Java Python 架构设计 Agent RPA
1.背景介绍在当今的互联网时代,数据量的爆炸性增长和业务的快速发展,使得单一的计算机系统已经无法满足我们的需求。为了解决这个问题,分布式系统应运而生。分布式系统是一种能在多台计算机(也称为节点)上运行,并通过网络进行通信和协调的系统。它能够提供高可用性、高可靠性、高扩展性和高性能等特性,因此在云计算、大数据、微服务等领域得到了广泛的应用。然而,设计和实现一个分布式系统并不是一件容易的事情。它涉及到
RabbitMQ实战:构建可靠的异步消息系统
AI天才研究院
计算 DeepSeek R1 & 大数据AI人工智能大模型 计算科学 神经计算 深度学习 神经网络 大数据 人工智能 大型语言模型 AI AGI LLM Java Python 架构设计 Agent RPA
RabbitMQ实战:构建可靠的异步消息系统1.背景介绍1.1异步消息系统的重要性在现代软件系统中,各个组件和服务之间通常需要进行大量的通信和数据交换。同步通信会导致系统耦合度高,可扩展性差。异步消息系统应运而生,通过将消息发送者和接收者解耦,提高了系统的灵活性、可靠性和性能。1.2RabbitMQ的优势RabbitMQ是一个开源的消息队列系统,基于AMQP(AdvancedMessageQueu
通过vLLM部署LLM模型到生产环境中
MichaelIp
人工智能实验室 大语言模型 人工智能 python AIGC 自然语言处理 语言模型 prompt llama
文章目录1使用vLLM部署模型2部署过程2.1准备GPU环境2.2安装vLLM依赖项3使用vLLM部署模型并启动服务3.1部署开源模型3.2部署微调模型4测试服务是否正常运行5评估服务性能1使用vLLM部署模型本地部署模型主要包含下载模型、编写模型加载代码和发布为支持API访问的应用服务这三个步骤。这个过程通常伴随较高的人工部署成本,vLLM可以用来简化这一流程。它是一个专为大模型推理设计的开源框
阿里巴巴在线图床HTML源码
tupaopao123
程序源码 网站源码 AEIN 墨渊AE博客 建站源码 网络技术
介绍:阿里巴巴在线图片上传源码纯单页模板,CSS全部外链化,加快访问速度转载请注明出处AE博客|墨渊»阿里巴巴在线图床HTML源码网盘下载地址:http://kekewl.org/1CUeRcKpHXj图片:
Aspect执行时目标方法参数设置完成了吗?
阿湯哥
spring
在SpringAOP中,Aspect的通知(如@Before、@Around)确实是在方法入参设置完成之后执行的。以下是详细的解释:参数绑定的时机:当通过代理对象调用目标方法时,Spring会先解析方法的参数(例如,从请求、上下文或调用方传入的值),并将其正确绑定到方法的参数列表中。这一过程发生在Aspect的通知代码执行之前。不同通知类型的执行顺序:@Before通知:在目标方法执行前触发,但此
领域模型介绍
阿湯哥
架构
领域模型介绍领域模型(DomainModel)是软件系统中用于抽象和表达业务逻辑的核心结构,它将复杂的业务问题转化为代码中的对象、规则和交互关系,帮助开发者以业务语言构建系统。领域模型的核心目标是高内聚、低耦合,通过清晰的边界(BoundedContext)隔离不同业务模块,确保代码与业务需求高度一致。领域模型的核心元素及经典案例我们以电商系统的订单处理流程为例,说明领域模型的核心元素如何协作。1
Python 布尔类型深度剖析:与其他数据类型的对比及应用
tekin
Python 编程秘籍库 python 开发语言 Python 布尔类型深度剖析 数据类型的对比及应用
Python布尔类型深度剖析:与其他数据类型的对比及应用本文将深入探讨Python中的布尔类型,详细介绍布尔类型的定义、特性、运算规则以及使用场景。同时,将布尔类型与数字类型、字符串类型、列表等其他常见数据类型进行全面对比,帮助读者清晰地理解它们之间的差异和联系。此外,还会对相关联的知识点进行拓展深化,让读者能够更加灵活地运用布尔类型进行Python编程。文章目录Python布尔类型深度剖析:与其
对象存储
Mini' miss
python java c# php c++
对象存储(CloudObjectStorage,COS)是腾讯云提供的一种存储海量文件的分布式存储服务,具有高扩展性、低成本、可靠安全等优点。通过控制台、API、SDK和工具等多样化方式,用户可简单、快速地接入COS,进行多格式文件的上传、下载和管理,实现海量数据存储和管理。产品概述本页目录:对象存储(CloudObjectStorage,COS)是腾讯云提供的一种存储海量文件的分布式存储服务,用
Web开发架构--常见web后端框架介绍(python、java)
土豆尼尼酱
前端 架构 python
文章目录※Web开发介绍一、什么是Web框架?1.1核心流程1.2Web框架功能1.3Web框架的作用二、Python中的Web框架1.Django2.Flask3.FastAPI三、Java中的Web框架1.SpringBoot2.SpringMVC3.Struts24.PlayFramework四、Python与JavaWeb框架对比五、如何选择Web框架?※Web开发介绍Web开发流程需求分
IntersectionObserver API 使用,判断某个元素是否进入了“视口“
T-shmily
API 前端
一、APIIntersectionObserverAPI:可以自动"观察"元素是否可见,Chrome51+已经支持。其他支持Firefox:55+、Safari:12.1+、Edge:15+、Opera:44+由于可见(visible)的本质是,目标元素与视口产生一个交叉区,所以这个API叫做"交叉观察器"。用法:vario=newIntersectionObserver(callback,opt
Java语言特点及面试常考问题
土豆尼尼酱
java
未完待续…文章目录Java语言的特点面试常考问题及要点一、基础概念二、面向对象三、集合框架四、多线程五、JVM六、异常处理七、设计模式八、新特性(JDK8+)Java语言的特点跨平台性(一次编写,到处运行)通过JVM(Java虚拟机)实现,字节码可在任何安装JVM的设备上运行。面向对象(OOP)支持封装、继承、多态,所有代码必须定义在类中。自动内存管理(垃圾回收,GC)开发者无需手动释放内存,JV
Nginx负载均衡
510888780
nginx 应用服务器
Nginx负载均衡一些基础知识:
nginx 的 upstream目前支持 4 种方式的分配
1)、轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
2)、weight
指定轮询几率,weight和访问比率成正比
RedHat 6.4 安装 rabbitmq
bylijinnan
erlang rabbitmq redhat
在 linux 下安装软件就是折腾,首先是测试机不能上外网要找运维开通,开通后发现测试机的 yum 不能使用于是又要配置 yum 源,最后安装 rabbitmq 时也尝试了两种方法最后才安装成功
机器版本:
[root@redhat1 rabbitmq]# lsb_release
LSB Version: :base-4.0-amd64:base-4.0-noarch:core
FilenameUtils工具类
eksliang
FilenameUtils common-io
转载请出自出处:http://eksliang.iteye.com/blog/2217081 一、概述
这是一个Java操作文件的常用库,是Apache对java的IO包的封装,这里面有两个非常核心的类FilenameUtils跟FileUtils,其中FilenameUtils是对文件名操作的封装;FileUtils是文件封装,开发中对文件的操作,几乎都可以在这个框架里面找到。 非常的好用。
xml文件解析SAX
不懂事的小屁孩
xml
xml文件解析:xml文件解析有四种方式,
1.DOM生成和解析XML文档(SAX是基于事件流的解析)
2.SAX生成和解析XML文档(基于XML文档树结构的解析)
3.DOM4J生成和解析XML文档
4.JDOM生成和解析XML
本文章用第一种方法进行解析,使用android常用的DefaultHandler
import org.xml.sax.Attributes;
通过定时任务执行mysql的定期删除和新建分区,此处是按日分区
酷的飞上天空
mysql
使用python脚本作为命令脚本,linux的定时任务来每天定时执行
#!/usr/bin/python
# -*- coding: utf8 -*-
import pymysql
import datetime
import calendar
#要分区的表
table_name = 'my_table'
#连接数据库的信息
host,user,passwd,db =
如何搭建数据湖架构?听听专家的意见
蓝儿唯美
架构
Edo Interactive在几年前遇到一个大问题:公司使用交易数据来帮助零售商和餐馆进行个性化促销,但其数据仓库没有足够时间去处理所有的信用卡和借记卡交易数据
“我们要花费27小时来处理每日的数据量,”Edo主管基础设施和信息系统的高级副总裁Tim Garnto说道:“所以在2013年,我们放弃了现有的基于PostgreSQL的关系型数据库系统,使用了Hadoop集群作为公司的数
spring学习——控制反转与依赖注入
a-john
spring
控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。 控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。依赖注入应用比较广泛。
用spool+unixshell生成文本文件的方法
aijuans
xshell
例如我们把scott.dept表生成文本文件的语句写成dept.sql,内容如下:
set pages 50000;
set lines 200;
set trims on;
set heading off;
spool /oracle_backup/log/test/dept.lst;
select deptno||','||dname||','||loc
1、基础--名词解析(OOA/OOD/OOP)
asia007
学习基础知识
OOA:Object-Oriented Analysis(面向对象分析方法)
是在一个系统的开发过程中进行了系统业务调查以后,按照面向对象的思想来分析问题。OOA与结构化分析有较大的区别。OOA所强调的是在系统调查资料的基础上,针对OO方法所需要的素材进行的归类分析和整理,而不是对管理业务现状和方法的分析。
OOA(面向对象的分析)模型由5个层次(主题层、对象类层、结构层、属性层和服务层)
浅谈java转成json编码格式技术
百合不是茶
json编码 java转成json编码
json编码;是一个轻量级的数据存储和传输的语言
在java中需要引入json相关的包,引包方式在工程的lib下就可以了
JSON与JAVA数据的转换(JSON 即 JavaScript Object Natation,它是一种轻量级的数据交换格式,非
常适合于服务器与 JavaScript 之间的数据的交
web.xml之Spring配置(基于Spring+Struts+Ibatis)
bijian1013
java web.xml SSI spring配置
指定Spring配置文件位置
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-dao-bean.xml,/WEB-INF/spring-resources.xml,
/WEB-INF/
Installing SonarQube(Fail to download libraries from server)
sunjing
Install Sonar
1. Download and unzip the SonarQube distribution
2. Starting the Web Server
The default port is "9000" and the context path is "/". These values can be changed in &l
【MongoDB学习笔记十一】Mongo副本集基本的增删查
bit1129
mongodb
一、创建复本集
假设mongod,mongo已经配置在系统路径变量上,启动三个命令行窗口,分别执行如下命令:
mongod --port 27017 --dbpath data1 --replSet rs0
mongod --port 27018 --dbpath data2 --replSet rs0
mongod --port 27019 -
Anychart图表系列二之执行Flash和HTML5渲染
白糖_
Flash
今天介绍Anychart的Flash和HTML5渲染功能
HTML5
Anychart从6.0第一个版本起,已经逐渐开始支持各种图的HTML5渲染效果了,也就是说即使你没有安装Flash插件,只要浏览器支持HTML5,也能看到Anychart的图形(不过这些是需要做一些配置的)。
这里要提醒下大家,Anychart6.0版本对HTML5的支持还不算很成熟,目前还处于
Laravel版本更新异常4.2.8-> 4.2.9 Declaration of ... CompilerEngine ... should be compa
bozch
laravel
昨天在为了把laravel升级到最新的版本,突然之间就出现了如下错误:
ErrorException thrown with message "Declaration of Illuminate\View\Engines\CompilerEngine::handleViewException() should be compatible with Illuminate\View\Eng
编程之美-NIM游戏分析-石头总数为奇数时如何保证先动手者必胜
bylijinnan
编程之美
import java.util.Arrays;
import java.util.Random;
public class Nim {
/**编程之美 NIM游戏分析
问题:
有N块石头和两个玩家A和B,玩家A先将石头随机分成若干堆,然后按照BABA...的顺序不断轮流取石头,
能将剩下的石头一次取光的玩家获胜,每次取石头时,每个玩家只能从若干堆石头中任选一堆,
lunce创建索引及简单查询
chengxuyuancsdn
查询 创建索引 lunce
import java.io.File;
import java.io.IOException;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Docume
[IT与投资]坚持独立自主的研究核心技术
comsci
it
和别人合作开发某项产品....如果互相之间的技术水平不同,那么这种合作很难进行,一般都会成为强者控制弱者的方法和手段.....
所以弱者,在遇到技术难题的时候,最好不要一开始就去寻求强者的帮助,因为在我们这颗星球上,生物都有一种控制其
flashback transaction闪回事务查询
daizj
oracle sql 闪回事务
闪回事务查询有别于闪回查询的特点有以下3个:
(1)其正常工作不但需要利用撤销数据,还需要事先启用最小补充日志。
(2)返回的结果不是以前的“旧”数据,而是能够将当前数据修改为以前的样子的撤销SQL(Undo SQL)语句。
(3)集中地在名为flashback_transaction_query表上查询,而不是在各个表上通过“as of”或“vers
Java I/O之FilenameFilter类列举出指定路径下某个扩展名的文件
游其是你
FilenameFilter
这是一个FilenameFilter类用法的例子,实现的列举出“c:\\folder“路径下所有以“.jpg”扩展名的文件。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
C语言学习五函数,函数的前置声明以及如何在软件开发中合理的设计函数来解决实际问题
dcj3sjt126com
c
# include <stdio.h>
int f(void) //括号中的void表示该函数不能接受数据,int表示返回的类型为int类型
{
return 10; //向主调函数返回10
}
void g(void) //函数名前面的void表示该函数没有返回值
{
//return 10; //error 与第8行行首的void相矛盾
}
in
今天在测试环境使用yum安装,遇到一个问题: Error: Cannot retrieve metalink for repository: epel. Pl
dcj3sjt126com
centos
今天在测试环境使用yum安装,遇到一个问题:
Error: Cannot retrieve metalink for repository: epel. Please verify its path and try again
处理很简单,修改文件“/etc/yum.repos.d/epel.repo”, 将baseurl的注释取消, mirrorlist注释掉。即可。
&n
单例模式
shuizhaosi888
单例模式
单例模式 懒汉式
public class RunMain {
/**
* 私有构造
*/
private RunMain() {
}
/**
* 内部类,用于占位,只有
*/
private static class SingletonRunMain {
priv
Spring Security(09)——Filter
234390216
Spring Security
Filter
目录
1.1 Filter顺序
1.2 添加Filter到FilterChain
1.3 DelegatingFilterProxy
1.4 FilterChainProxy
1.5
公司项目NODEJS实践0.1
逐行分析JS源代码
mongodb nginx ubuntu nodejs
一、前言
前端如何独立用nodeJs实现一个简单的注册、登录功能,是不是只用nodejs+sql就可以了?其实是可以实现,但离实际应用还有距离,那要怎么做才是实际可用的。
网上有很多nod
java.lang.Math
liuhaibo_ljf
java Math lang
System.out.println(Math.PI);
System.out.println(Math.abs(1.2));
System.out.println(Math.abs(1.2));
System.out.println(Math.abs(1));
System.out.println(Math.abs(111111111));
System.out.println(Mat
linux下时间同步
nonobaba
ntp
今天在linux下做hbase集群的时候,发现hmaster启动成功了,但是用hbase命令进入shell的时候报了一个错误 PleaseHoldException: Master is initializing,查看了日志,大致意思是说master和slave时间不同步,没办法,只好找一种手动同步一下,后来发现一共部署了10来台机器,手动同步偏差又比较大,所以还是从网上找现成的解决方
ZooKeeper3.4.6的集群部署
roadrunners
zookeeper 集群 部署
ZooKeeper是Apache的一个开源项目,在分布式服务中应用比较广泛。它主要用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步、集群管理、配置文件管理、同步锁、队列等。这里主要讲集群中ZooKeeper的部署。
1、准备工作
我们准备3台机器做ZooKeeper集群,分别在3台机器上创建ZooKeeper需要的目录。
数据存储目录
Java高效读取大文件
tomcat_oracle
java
读取文件行的标准方式是在内存中读取,Guava 和Apache Commons IO都提供了如下所示快速读取文件行的方法: Files.readLines(new File(path), Charsets.UTF_8); FileUtils.readLines(new File(path)); 这种方法带来的问题是文件的所有行都被存放在内存中,当文件足够大时很快就会导致
微信支付api返回的xml转换为Map的方法
xu3508620
xml map 微信api
举例如下:
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><