Android Dagger2 框架依赖图构建模块深度剖析(三)
一、引言
在 Android 开发中,依赖注入(Dependency Injection,简称 DI)是一种重要的设计模式,它能够降低代码的耦合度,提高代码的可测试性和可维护性。Dagger 2 作为一款高效的依赖注入框架,在编译时生成依赖注入代码,避免了运行时反射带来的性能开销。其中,依赖图构建模块是 Dagger 2 的核心组成部分,它负责解析依赖关系,构建依赖图,为后续的依赖注入提供基础。本文将深入分析 Dagger 2 框架的依赖图构建模块,从源码级别详细介绍其工作原理和实现细节。
二、依赖图构建模块概述
2.1 依赖图的概念
依赖图是一种有向图,用于表示对象之间的依赖关系。在 Dagger 2 中,依赖图的节点表示依赖对象,边表示依赖关系。通过构建依赖图,可以清晰地了解各个依赖对象之间的依赖关系,从而实现正确的依赖注入。
2.2 依赖图构建模块的作用
依赖图构建模块的主要作用是解析 Dagger 2 注解(如@Inject、@Module、@Provides、@Component等),并根据注解信息构建依赖图。在构建过程中,会检查依赖关系的合法性,处理循环依赖等问题,确保依赖图的正确性。
2.3 依赖图构建模块的工作流程
依赖图构建模块的工作流程主要包括以下几个步骤:
- 注解扫描:扫描源代码中的 Dagger 2 注解,收集依赖信息。
- 依赖解析:根据注解信息,解析各个依赖对象之间的依赖关系。
- 图构建:将解析得到的依赖关系转化为依赖图。
- 图验证:检查依赖图的合法性,处理循环依赖等问题。
- 代码生成:根据依赖图生成依赖注入代码。
三、注解扫描
3.1 注解处理器
Dagger 2 使用注解处理器在编译时处理注解。注解处理器是一个实现了javax.annotation.processing.AbstractProcessor接口的类,它会在编译过程中扫描源代码中的注解,并根据注解信息进行相应的处理。以下是一个简化的注解处理器示例:
java
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.Set;// 定义支持的注解类型
@SupportedAnnotationTypes({"javax.inject.Inject","dagger.Module","dagger.Provides","dagger.Component"
})
// 定义支持的源代码版本
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class DaggerProcessor extends AbstractProcessor {@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {// 遍历所有支持的注解类型for (TypeElement annotation : annotations) {// 获取被该注解标记的所有元素Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation);for (Element element : annotatedElements) {// 处理注解元素processAnnotatedElement(element, annotation);}}return true;}private void processAnnotatedElement(Element element, TypeElement annotation) {// 根据注解类型进行不同的处理if (annotation.getQualifiedName().contentEquals("javax.inject.Inject")) {// 处理 @Inject 注解processInjectAnnotation(element);} else if (annotation.getQualifiedName().contentEquals("dagger.Module")) {// 处理 @Module 注解processModuleAnnotation(element);} else if (annotation.getQualifiedName().contentEquals("dagger.Provides")) {// 处理 @Provides 注解processProvidesAnnotation(element);} else if (annotation.getQualifiedName().contentEquals("dagger.Component")) {// 处理 @Component 注解processComponentAnnotation(element);}}private void processInjectAnnotation(Element element) {// 处理 @Inject 注解的具体逻辑// 例如,记录需要注入的字段或构造函数}private void processModuleAnnotation(Element element) {// 处理 @Module 注解的具体逻辑// 例如,记录模块类和提供依赖的方法}private void processProvidesAnnotation(Element element) {// 处理 @Provides 注解的具体逻辑// 例如,记录提供依赖的方法和返回类型}private void processComponentAnnotation(Element element) {// 处理 @Component 注解的具体逻辑// 例如,记录组件类和依赖的模块}
}
3.2 注解信息收集
在注解处理器中,会收集各个注解的信息,包括注解标记的元素、注解的属性等。例如,对于@Inject注解,会记录需要注入的字段或构造函数;对于@Module注解,会记录模块类和提供依赖的方法;对于@Provides注解,会记录提供依赖的方法和返回类型;对于@Component注解,会记录组件类和依赖的模块。以下是一个简化的注解信息收集示例:
java
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import java.util.ArrayList;
import java.util.List;// 注解信息收集器
class AnnotationInfoCollector {private List<Element> injectElements = new ArrayList<>();private List<TypeElement> moduleElements = new ArrayList<>();private List<ExecutableElement> providesElements = new ArrayList<>();private List<TypeElement> componentElements = new ArrayList<>();public void collectInjectElement(Element element) {injectElements.add(element);}public void collectModuleElement(TypeElement element) {moduleElements.add(element);}public void collectProvidesElement(ExecutableElement element) {providesElements.add(element);}public void collectComponentElement(TypeElement element) {componentElements.add(element);}public List<Element> getInjectElements() {return injectElements;}public List<TypeElement> getModuleElements() {return moduleElements;}public List<ExecutableElement> getProvidesElements() {return providesElements;}public List<TypeElement> getComponentElements() {return componentElements;}
}
3.3 注解扫描的实现细节
在注解处理器的process方法中,会遍历所有支持的注解类型,并获取被该注解标记的所有元素。然后,调用processAnnotatedElement方法对每个注解元素进行处理。在processAnnotatedElement方法中,根据注解类型调用不同的处理方法,如processInjectAnnotation、processModuleAnnotation等。在这些处理方法中,会将注解信息收集到AnnotationInfoCollector中。以下是一个完整的注解扫描示例:
java
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import java.util.Set;// 定义支持的注解类型
@SupportedAnnotationTypes({"javax.inject.Inject","dagger.Module","dagger.Provides","dagger.Component"
})
// 定义支持的源代码版本
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class DaggerProcessor extends AbstractProcessor {private AnnotationInfoCollector infoCollector = new AnnotationInfoCollector();@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {// 遍历所有支持的注解类型for (TypeElement annotation : annotations) {// 获取被该注解标记的所有元素Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation);for (Element element : annotatedElements) {// 处理注解元素processAnnotatedElement(element, annotation);}}return true;}private void processAnnotatedElement(Element element, TypeElement annotation) {// 根据注解类型进行不同的处理if (annotation.getQualifiedName().contentEquals("javax.inject.Inject")) {// 处理 @Inject 注解infoCollector.collectInjectElement(element);} else if (annotation.getQualifiedName().contentEquals("dagger.Module")) {// 处理 @Module 注解infoCollector.collectModuleElement((TypeElement) element);} else if (annotation.getQualifiedName().contentEquals("dagger.Provides")) {// 处理 @Provides 注解infoCollector.collectProvidesElement((ExecutableElement) element);} else if (annotation.getQualifiedName().contentEquals("dagger.Component")) {// 处理 @Component 注解infoCollector.collectComponentElement((TypeElement) element);}}public AnnotationInfoCollector getInfoCollector() {return infoCollector;}
}
四、依赖解析
4.1 依赖关系的表示
在 Dagger 2 中,依赖关系可以通过Dependency类来表示。Dependency类包含了依赖对象的类型和提供依赖的方式。以下是一个简化的Dependency类示例:
java
import javax.lang.model.type.TypeMirror;// 依赖关系类
class Dependency {private TypeMirror type; // 依赖对象的类型private boolean isProvidedByModule; // 是否由模块提供依赖public Dependency(TypeMirror type, boolean isProvidedByModule) {this.type = type;this.isProvidedByModule = isProvidedByModule;}public TypeMirror getType() {return type;}public boolean isProvidedByModule() {return isProvidedByModule;}
}
4.2 依赖解析的过程
依赖解析的过程主要是根据注解信息,确定各个依赖对象之间的依赖关系。具体步骤如下:
-
解析
@Inject注解:对于被@Inject注解标记的字段或构造函数,确定其依赖的对象类型。 -
解析
@Module和@Provides注解:对于被@Module注解标记的模块类,解析其中被@Provides注解标记的方法,确定这些方法提供的依赖对象类型。 -
建立依赖关系:根据解析得到的信息,建立各个依赖对象之间的依赖关系。以下是一个简化的依赖解析示例:
java
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;// 依赖解析器
class DependencyResolver {private AnnotationInfoCollector infoCollector;private Map<TypeMirror, List<Dependency>> dependencyMap = new HashMap<>();public DependencyResolver(AnnotationInfoCollector infoCollector) {this.infoCollector = infoCollector;}public void resolveDependencies() {// 解析 @Inject 注解resolveInjectAnnotations();// 解析 @Module 和 @Provides 注解resolveModuleAndProvidesAnnotations();}private void resolveInjectAnnotations() {List<Element> injectElements = infoCollector.getInjectElements();for (Element element : injectElements) {if (element.getKind().isField()) {// 处理字段注入TypeMirror fieldType = element.asType();Dependency dependency = new Dependency(fieldType, false);addDependency(fieldType, dependency);} else if (element.getKind().isConstructor()) {// 处理构造函数注入ExecutableElement constructor = (ExecutableElement) element;for (Element parameter : constructor.getParameters()) {TypeMirror parameterType = parameter.asType();Dependency dependency = new Dependency(parameterType, false);addDependency(parameterType, dependency);}}}}private void resolveModuleAndProvidesAnnotations() {List<ExecutableElement> providesElements = infoCollector.getProvidesElements();for (ExecutableElement providesElement : providesElements) {TypeMirror returnType = providesElement.getReturnType();Dependency dependency = new Dependency(returnType, true);addDependency(returnType, dependency);}}private void addDependency(TypeMirror type, Dependency dependency) {List<Dependency> dependencies = dependencyMap.computeIfAbsent(type, k -> new ArrayList<>());dependencies.add(dependency);}public Map<TypeMirror, List<Dependency>> getDependencyMap() {return dependencyMap;}
}
4.3 依赖解析的实现细节
在DependencyResolver类中,resolveDependencies方法是依赖解析的入口。它会依次调用resolveInjectAnnotations和resolveModuleAndProvidesAnnotations方法,分别解析@Inject注解和@Module、@Provides注解。在resolveInjectAnnotations方法中,会处理被@Inject注解标记的字段和构造函数,确定其依赖的对象类型,并将依赖信息添加到dependencyMap中。在resolveModuleAndProvidesAnnotations方法中,会处理被@Provides注解标记的方法,确定这些方法提供的依赖对象类型,并将依赖信息添加到dependencyMap中。
五、图构建
5.1 图的表示
在 Dagger 2 中,依赖图可以通过DependencyGraph类来表示。DependencyGraph类包含了图的节点和边的信息。以下是一个简化的DependencyGraph类示例:
java
import javax.lang.model.type.TypeMirror;
import java.util.*;// 依赖图类
class DependencyGraph {private Map<TypeMirror, List<TypeMirror>> adjacencyList = new HashMap<>(); // 邻接表表示图public void addNode(TypeMirror node) {adjacencyList.putIfAbsent(node, new ArrayList<>());}public void addEdge(TypeMirror from, TypeMirror to) {adjacencyList.computeIfAbsent(from, k -> new ArrayList<>()).add(to);}public List<TypeMirror> getNeighbors(TypeMirror node) {return adjacencyList.getOrDefault(node, Collections.emptyList());}public Set<TypeMirror> getNodes() {return adjacencyList.keySet();}
}
5.2 图构建的过程
图构建的过程主要是根据依赖解析得到的依赖关系,将其转化为依赖图。具体步骤如下:
-
添加节点:将所有依赖对象的类型作为节点添加到图中。
-
添加边:根据依赖关系,在图中添加边,表示依赖关系。以下是一个简化的图构建示例:
java
import javax.lang.model.type.TypeMirror;
import java.util.Map;
import java.util.List;// 图构建器
class GraphBuilder {private DependencyResolver resolver;private DependencyGraph graph = new DependencyGraph();public GraphBuilder(DependencyResolver resolver) {this.resolver = resolver;}public void buildGraph() {Map<TypeMirror, List<Dependency>> dependencyMap = resolver.getDependencyMap();// 添加节点for (TypeMirror type : dependencyMap.keySet()) {graph.addNode(type);}// 添加边for (Map.Entry<TypeMirror, List<Dependency>> entry : dependencyMap.entrySet()) {TypeMirror from = entry.getKey();List<Dependency> dependencies = entry.getValue();for (Dependency dependency : dependencies) {TypeMirror to = dependency.getType();graph.addEdge(from, to);}}}public DependencyGraph getGraph() {return graph;}
}
5.3 图构建的实现细节
在GraphBuilder类中,buildGraph方法是图构建的入口。它会首先获取依赖解析得到的dependencyMap,然后遍历dependencyMap的键,将所有依赖对象的类型作为节点添加到图中。接着,遍历dependencyMap的每个条目,根据依赖关系在图中添加边。
六、图验证
6.1 循环依赖的检测
循环依赖是指依赖关系中存在环路,即 A 依赖 B,B 又依赖 A。循环依赖会导致依赖注入无法正常进行,因此需要在图构建完成后进行循环依赖的检测。可以使用深度优先搜索(DFS)算法来检测循环依赖。以下是一个简化的循环依赖检测示例:
java
import javax.lang.model.type.TypeMirror;
import java.util.*;// 循环依赖检测器
class CycleDetector {private DependencyGraph graph;private Set<TypeMirror> visited = new HashSet<>();private Set<TypeMirror> recursionStack = new HashSet<>();public CycleDetector(DependencyGraph graph) {this.graph = graph;}public boolean hasCycle() {for (TypeMirror node : graph.getNodes()) {if (!visited.contains(node)) {if (dfs(node)) {return true;}}}return false;}private boolean dfs(TypeMirror node) {visited.add(node);recursionStack.add(node);for (TypeMirror neighbor : graph.getNeighbors(node)) {if (!visited.contains(neighbor)) {if (dfs(neighbor)) {return true;}} else if (recursionStack.contains(neighbor)) {return true;}}recursionStack.remove(node);return false;}
}
6.2 图验证的过程
图验证的过程主要是检测依赖图中是否存在循环依赖。如果存在循环依赖,会抛出异常,提示开发者解决循环依赖问题。以下是一个简化的图验证示例:
java
// 图验证器
class GraphValidator {private DependencyGraph graph;public GraphValidator(DependencyGraph graph) {this.graph = graph;}public void validateGraph() {CycleDetector detector = new CycleDetector(graph);if (detector.hasCycle()) {throw new IllegalStateException("Dependency graph contains a cycle!");}}
}
6.3 图验证的实现细节
在GraphValidator类中,validateGraph方法是图验证的入口。它会创建一个CycleDetector对象,并调用其hasCycle方法检测依赖图中是否存在循环依赖。如果存在循环依赖,会抛出IllegalStateException异常。
七、代码生成
7.1 代码生成的目标
代码生成的目标是根据依赖图生成依赖注入代码,实现依赖对象的创建和注入。生成的代码通常包括组件类的实现、模块类的包装类、依赖提供者类等。
7.2 代码生成的过程
代码生成的过程主要包括以下几个步骤:
-
确定生成的代码结构:根据依赖图和注解信息,确定生成的代码结构,如组件类的接口和实现、模块类的包装类、依赖提供者类等。
-
生成代码模板:根据代码结构,生成代码模板,包括类的定义、方法的定义、字段的定义等。
-
填充代码模板:根据依赖图和注解信息,填充代码模板,生成具体的代码。以下是一个简化的代码生成示例:
java
import javax.lang.model.type.TypeMirror;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.List;// 代码生成器
class CodeGenerator {private DependencyGraph graph;private Map<TypeMirror, List<Dependency>> dependencyMap;public CodeGenerator(DependencyGraph graph, Map<TypeMirror, List<Dependency>> dependencyMap) {this.graph = graph;this.dependencyMap = dependencyMap;}public void generateCode() {// 生成组件类的实现generateComponentImplementation();// 生成模块类的包装类generateModuleWrappers();// 生成依赖提供者类generateDependencyProviders();}private void generateComponentImplementation() {// 生成组件类的实现代码try (FileWriter writer = new FileWriter("ComponentImpl.java")) {writer.write("public class ComponentImpl {\n");// 生成组件类的字段和方法for (TypeMirror node : graph.getNodes()) {writer.write(" private " + node.toString() + " " + node.toString().toLowerCase() + ";\n");}writer.write(" public ComponentImpl() {\n");// 生成组件类的构造函数代码for (TypeMirror node : graph.getNodes()) {writer.write(" this." + node.toString().toLowerCase() + " = new " + node.toString() + "();\n");}writer.write(" }\n");writer.write("}\n");} catch (IOException e) {e.printStackTrace();}}private void generateModuleWrappers() {// 生成模块类的包装类代码// 具体实现省略}private void generateDependencyProviders() {// 生成依赖提供者类代码// 具体实现省略}
}
7.3 代码生成的实现细节
在CodeGenerator类中,generateCode方法是代码生成的入口。它会依次调用generateComponentImplementation、generateModuleWrappers和generateDependencyProviders方法,分别生成组件类的实现、模块类的包装类和依赖提供者类的代码。在generateComponentImplementation方法中,会创建一个FileWriter对象,将生成的组件类实现代码写入文件。
八、依赖图构建模块的整体流程
8.1 整体流程概述
依赖图构建模块的整体流程包括注解扫描、依赖解析、图构建、图验证和代码生成。以下是一个简化的整体流程示例:
java
// 依赖图构建模块的整体流程示例
public class DependencyGraphBuilder {public static void main(String[] args) {// 创建注解处理器DaggerProcessor processor = new DaggerProcessor();// 模拟注解扫描过程// 这里省略具体的注解扫描代码,假设已经完成注解扫描// 获取注解信息收集器AnnotationInfoCollector infoCollector = processor.getInfoCollector();// 创建依赖解析器DependencyResolver resolver = new DependencyResolver(infoCollector);// 解析依赖关系resolver.resolveDependencies();// 创建图构建器GraphBuilder graphBuilder = new GraphBuilder(resolver);// 构建依赖图graphBuilder.buildGraph();// 获取依赖图DependencyGraph graph = graphBuilder.getGraph();// 创建图验证器GraphValidator validator = new GraphValidator(graph);try {// 验证依赖图validator.validateGraph();// 创建代码生成器CodeGenerator generator = new CodeGenerator(graph, resolver.getDependencyMap());// 生成代码generator.generateCode();} catch (IllegalStateException e) {System.err.println("Error: " + e.getMessage());}}
}
8.2 整体流程的实现细节
在DependencyGraphBuilder类的main方法中,首先创建了一个DaggerProcessor对象,模拟注解扫描过程。然后获取注解信息收集器,创建DependencyResolver对象,解析依赖关系。接着创建GraphBuilder对象,构建依赖图。再创建GraphValidator对象,验证依赖图。如果依赖图验证通过,创建CodeGenerator对象,生成代码。如果依赖图存在循环依赖,会捕获IllegalStateException异常,并输出错误信息。
九、依赖图构建模块的优化
9.1 性能优化
- 缓存机制:在依赖解析和图构建过程中,可以使用缓存机制来避免重复计算。例如,对于已经解析过的依赖关系,可以将其缓存起来,下次需要时直接从缓存中获取。
- 并行处理:对于大规模的项目,可以考虑使用并行处理来提高依赖图构建的性能。例如,将注解扫描、依赖解析、图构建等步骤并行执行。
9.2 错误处理优化
- 详细的错误信息:在图验证过程中,如果发现循环依赖或其他错误,应该提供详细的错误信息,帮助开发者快速定位和解决问题。
- 错误恢复机制:在代码生成过程中,如果出现错误,应该有错误恢复机制,避免程序崩溃。例如,可以捕获异常,记录错误信息,并尝试继续生成其他部分的代码。
9.3 代码生成优化
- 代码模板优化:可以使用更灵活的代码模板,根据不同的依赖关系和注解信息生成更优化的代码。例如,对于单例对象,可以生成单例模式的代码。
- 代码压缩:生成的代码可能会比较冗长,可以使用代码压缩工具对生成的代码进行压缩,减少代码体积。
十、总结
本文深入分析了 Android Dagger 2 框架的依赖图构建模块,从源码级别详细介绍了其工作原理和实现细节。依赖图构建模块是 Dagger 2 的核心组成部分,它通过注解扫描、依赖解析、图构建、图验证和代码生成等步骤,实现了依赖关系的解析和依赖注入代码的生成。在实际开发中,掌握依赖图构建模块的原理和实现细节,有助于更好地使用 Dagger 2 进行依赖注入,提高代码的可测试性和可维护性。同时,通过对依赖图构建模块的优化,可以进一步提高其性能和可靠性。
以上是一篇关于 Android Dagger 2 框架依赖图构建模块的技术博客,涵盖了从注解扫描到代码生成的整个流程,以及相关的优化建议。希望对你有所帮助!
相关文章:
Android Dagger2 框架依赖图构建模块深度剖析(三)
一、引言 在 Android 开发中,依赖注入(Dependency Injection,简称 DI)是一种重要的设计模式,它能够降低代码的耦合度,提高代码的可测试性和可维护性。Dagger 2 作为一款高效的依赖注入框架,在编…...
(一)微服务初见之 Spring Cloud 介绍
微服务架构简介 从单体应用架构发展到SOA架构,再到微服务架构,应用架构经历了多年的不断演进。微服务架构不是凭空产生的,而是技术发展的必然结果,分布式云平台的应用环境使得微服务代替单体应用成为互联网大型系统的架构选择。目…...
python--面试题--基础题
join() 和 split() 函数 join() 函数可以将指定的字符添加到字符串中。 a[my, name, shi, wzngz] print(..join(a)) 输出结果:my.name.shi.wzngz split() 函数可以用指定的字符分割字符串 a"my name shi wzngz " print(a.split()) 输出结果ÿ…...
架构思维:软件建模与架构设计的关键要点
文章目录 1. 软件建模的核心概念2. 七种常用UML图及其应用场景类图时序图组件图部署图用例图状态图活动图 3. 软件设计文档的三阶段结构4. 架构设计的关键实践1. 用例图:核心功能模块2. 部署图:架构演进阶段3. 技术挑战与解决方案4. 关键架构图示例5. 架…...
【RNN神经网络】序列模型与RNN神经网络
前言 清库存。正式切入大模型后,打算把基础知识都梳理一遍,然后写了两篇就发现写不动了,后面就捡重要的记录。RNN知识仅此一篇记录,扫盲记录。 【自然语言处理】 (Natural Language Processing,NLP…...
Python文件管理
目录 一、文本文件读写 1、相关函数 2、读写文件 3、使用readline读取一行 4、读写文件的异常处理 5、添加内容 二、文本文件的编码 1、常见的编码 2、Python程序的编码 3、指定编码 三、文件的路径 1、相对路径 2、绝对路径 3、路径的改变 四、文件夹操作 五、…...
vue3 前端路由权限控制与字典数据缓存实践(附Demo)
目录 前言1. 基本知识2. Demo3. 实战 前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 从实战中出发: 1. 基本知识 Vue3 和 Java 通信时如何进行字典数据管理 需要了解字典数据的结构。通常&#x…...
基于javaweb的SpringBoot精美物流管理系统设计与实现(源码+文档+部署讲解)
技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…...
【极光 Orbit·STC8x】05. GPIO库函数驱动LED流动
【极光 OrbitSTC8】05. GPIO库函数驱动LED流动 七律 逐光流转 八灯列阵若星河,状态为舟渡长波。 寄存器中藏玄机,Switch语句定山河。 循环往复如潮涌,步骤变量掌沉浮。 单片机前展锋芒,代码织就光之舞。 摘要 本文基于STC8H8K6…...
DeepSeek进阶应用(二):结合Kimi制作PPT(双AI协作教程)
🌟引言: DeepSeek作为国产AI大模型,以强大的逻辑推理和结构化内容生成能力著称,擅长根据用户需求生成PPT大纲或Markdown文本;Kimi的PPT助手则能解析结构化内容并套用模板快速生成美观的PPT,两者结合实现“内…...
【Aioredis实战总结】Aioredis简介
一、Aioredis简介 Aioredis 是一个基于Python asyncio框架的异步Redis客户端库,专为高并发场景设计。它允许开发者在不阻塞主线程的情况下执行Redis操作,显著提升I/O密集型任务(如Web应用的缓存、实时消息队列等)的性能。自4.2.0…...
SpringBoot——Maven篇
Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的工具。它具有许多特性,其中一些重要的特性包括: 1. 自动配置:Spring Boot 提供了自动配置的机制,可以根据应用程序的依赖和环境自动配置应用程序的各种组件ÿ…...
Python中的多态与Java、C#、C++中的多态的区别有哪些?
Python中的多态与Java、C#、C等静态类型语言的主要区别体现在以下几个方面: 1. 类型系统与多态实现方式 Python(动态类型,鸭子类型) 多态基于对象的行为(方法的存在性),而非继承或接口。只要对…...
卷积神经网络(知识点)
一、为了使特征图变小: 由两种方法:1.增大步长:卷积的时候不是一次一步,而是一次多步,类似一张图片,在原来的像素基础上,每隔一个取一个像素点。 其中S就是步长 注意:扩大步长不经…...
Vision Transformer (ViT):将Transformer带入计算机视觉的革命性尝试(代码实现)
Vision Transformer (ViT):将Transformer带入计算机视觉的革命性尝试 作为一名深度学习研究者,如果你对自然语言处理(NLP)领域的Transformer架构了如指掌,那么你一定不会对它在序列建模中的强大能力感到陌生。然而&am…...
特殊 IP 地址
文章目录 特殊IP地址概述受限广播地址(Limited Broadcast Address)直接广播地址(Directed Broadcast Address)多播地址(Multicast Address)环回地址(Loopback Address)本网络本主机&…...
数学——A. K-divisible Sum + D. Exam in MAC
A. K-divisible Sum 题目: 思路: 以下 “[xxx]” 符号均代表向上取整 我们假设总和是sum,那么就有sum k * cnt 要想最大值最小,肯定是要让sum尽可能小,这样每个元素都能变小 最小情况是 sum 恰好等于 n 时&#…...
30天学习Java第五天——数组 字符串
数组 一维数组 定义 int[] anArray;int anOtherArray[];初始化int anOtherArray[] new int[] {1, 2, 3, 4, 5}; 访问 anArray[0] 10;可变数组:void varargsMethod(String... varargs) {} 该方法可以接收任意数量的字符串参数,可以是 0 个或者 N 个…...
【DeepSeek应用】本地部署deepseek模型后,如何在vscode中调用该模型进行代码撰写,检视和优化?
若已成功在本地部署了 DeepSeek 模型(例如通过 vscode-llm、ollama 或私有 API 服务),在 VS Code 中调用本地模型进行代码撰写、检视和优化的完整流程如下: 1. 准备工作:确认本地模型服务状态 模型服务类型: 若使用 HTTP API 服务(如 FastAPI/Flask 封装),假设服务地址…...
2025年【广东省安全员C证第四批(专职安全生产管理人员)】考试及广东省安全员C证第四批(专职安全生产管理人员)模拟试题
安全生产是各行各业不可忽视的重要环节,特别是在广东省这样的经济大省,安全生产的重要性更是不言而喻。为了确保安全生产管理人员具备足够的专业知识和实际操作能力,广东省定期举办安全员C证考试。本文将详细介绍2025年广东省安全员C证第四批…...
网络编程、URI和URL的区别、TCP/IP协议、IP和端口、URLConnection
DAY12.1 Java核心基础 网络编程 在互联网时代,网络在生活中处处可见,javaWeb占据了很大一部分 那如何实现javaWeb编程呢? Web编程就是运行在同一个网络下面的终端,使得它们之间可以进行数据传输 计算机网络基本知识 计算机网络…...
JAVA面试_进阶部分_Java JVM:垃圾回收(GC 在什么时候,对什么东西,做了什么事情)
在什么时候: 首先需要知道,GC又分为minor GC 和 Full GC(major GC)。Java堆内存分为新生代和老年代,新生代 中又分为1个eden区和两个Survior区域。 一般情况下,新创建的对象都会被分配到eden区ÿ…...
自探索大语言模型微调(一)
一、数据 1.1、失败案例 Hugging Face: 根据B站上搜索到的资料,datasets这个库可以直接下载丰富的数据集合和与训练模型,调用也非常的简单,唯一的缺点就是,需要外网(翻墙),用国内的…...
Unity 和 Python 的连接(通过SocketIO)附源码
在游戏或者项目开发中,Unity 通常用于创建前端,而 Python 则因其强大的数据处理能力常被用作后端。通过 Socket.IO,我们可以轻松地实现 Unity 和 Python 的实时通信。本文将介绍如何通过 Socket.IO 连接 Unity 和 Python,并附上完…...
89.HarmonyOS NEXT 应用安全与隐私保护指南:构建安全可靠的应用
温馨提示:本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦! HarmonyOS NEXT 应用安全与隐私保护指南:构建安全可靠的应用 文章目录 HarmonyOS NEXT 应用安全与隐私保护指南:构建安全可…...
浏览器对一个资源设置了缓存,如何清除缓存,且后续请求不命中缓存
方式1、浏览器端强制刷新 方式2、修改资源url eg:如下图,添加了查询参数 <link rel"stylesheet" href"style.css?v1.2.1"> <script src"app.js?t20231010"></script> 原理:1、在资源的…...
spring boot 发送邮件验证码
一、前置需求 1、准备邮箱 2、登录授权码 qq邮箱在–>设置–>账号POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 开启服务 二、发送邮件 1、简单邮件 包含邮件标题、邮件正文 2、引入mail启动器 <dependency><groupId>org.springframework.boot</groupI…...
MySQL连接较慢原因分析及解决措施
文章目录 整体说明一、问题现象二、问题分析2.1、DNS反向解析问题2.2、网络问题2.3、SSL/TLS协商问题2.4、自动补全的延迟 三、问题解决 摘要: MySQL连接较慢原因分析及解决措施 关键词: MySQL、连接缓慢、客户端、参数设置 整体说明 在使用MySQL的时候…...
IMA+DeepSeekR1+本地知识库撰写NOIP2008普及组T3【传球游戏】题解
目录 一、提问词 二、DeepSeekR1回复 题目描述 解题思路 实现代码 代码说明 三、说明 【IMADeepSeekR1本地知识库】撰写NOIP2008普及组复赛题解系列 1、IMADeepSeekR1本地知识库撰写NOIP2008普及组T1【ISBN 号码】题解-CSDN博客 2、IMADeepSeekR1本地知识库撰写NOIP200…...
【大模型基础_毛玉仁】2.5 基于 Decoder-only 架构的大语言模型 -- GPT和LLaMa模型介绍
更多内容:XiaoJ的知识星球 目录 2.5 基于 Decoder-only 架构的大语言模型2.5.1 Decoder-only 架构2.5.2 GPT 系列语言模型1)初出茅庐:GPT-1 模型2)小有所成:GPT-2 模型3)崭露头角:GPT-3 模型4&a…...
