SpringBoot2.x 整合SpringDocJavadocknife4j实现无注解零入侵式接口文档

说明

基于 javadoc 无注解零入侵生成规范的 openapi 结构体。


文档工具使用

由于框架采用 openapi 行业规范 故市面上大部分的框架均支持 可自行选择
例如: apifox apipost postman torna knife4j 等 根据对应工具的文档接入即可


Swagger升级SpringDoc指南

常见功能如下 其他功能自行挖掘
注意: javadoc 只能替换基础功能 特殊功能还需要使用注解实现

swaggerspringdocjavadoc
@Api(name = “xxx”)@Tag(name = “xxx”)java类注释第一行
@Api(description= “xxx”)@Tag(description= “xxx”)java类注释
@ApiOperation@Operationjava方法注释
@ApiIgnore@Hidden
@ApiParam@Parameterjava方法@param参数注释
@ApiImplicitParam@Parameterjava方法@param参数注释
@ApiImplicitParams@Parameters多个@param参数注释
@ApiModel@Schemajava实体类注释
@ApiModelProperty@Schemajava属性注释
@ApiModelProperty(hidden = true)@Schema(accessMode = READ_ONLY)
@ApiResponse@ApiResponsejava方法@return返回值注释

Maven依赖

<dependency>
	<groupId>org.springdoc</groupId>
	<artifactId>springdoc-openapi-webmvc-core</artifactId>
	<version>1.6.14</version>
</dependency>

<dependency>
	<groupId>org.springdoc</groupId>
	<artifactId>springdoc-openapi-javadoc</artifactId>
	<version>1.6.14</version>
</dependency>

swagger 配置属性

package com.ruoyi.framework.config.properties;

import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.tags.Tag;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * swagger 配置属性
 *
 * @author Lion Li
 */
@Data
@Component
@ConfigurationProperties(prefix = "springdoc")
public class SpringDocProperties {

    /**
     * 文档基本信息
     */
    @NestedConfigurationProperty
    private InfoProperties info = new InfoProperties();

    /**
     * 扩展文档地址
     */
    @NestedConfigurationProperty
    private ExternalDocumentation externalDocs;

    /**
     * 标签
     */
    private List<Tag> tags = null;

    /**
     * 路径
     */
    @NestedConfigurationProperty
    private Paths paths = null;

    /**
     * 组件
     */
    @NestedConfigurationProperty
    private Components components = null;

    /**
     * <p>
     * 文档的基础属性信息
     * </p>
     *
     * @see io.swagger.v3.oas.models.info.Info
     *
     * 为了 springboot 自动生产配置提示信息,所以这里复制一个类出来
     */
    @Data
    public static class InfoProperties {

        /**
         * 标题
         */
        private String title = null;

        /**
         * 描述
         */
        private String description = null;

        /**
         * 联系人信息
         */
        @NestedConfigurationProperty
        private Contact contact = null;

        /**
         * 许可证
         */
        @NestedConfigurationProperty
        private License license = null;

        /**
         * 版本
         */
        private String version = null;

    }

}


Swagger 文档配置

package com.ruoyi.framework.config;

import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.config.properties.SpringDocProperties;
import com.ruoyi.framework.handler.OpenApiHandler;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.*;
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
import org.springdoc.core.customizers.OpenApiCustomiser;
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
import org.springdoc.core.providers.JavadocProvider;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;

/**
 * Swagger 文档配置
 *
 * @author Lion Li
 */
@RequiredArgsConstructor
@Configuration
@AutoConfigureBefore(SpringDocConfiguration.class)
@ConditionalOnProperty(name = "springdoc.api-docs.enabled", havingValue = "true", matchIfMissing = true)
public class SpringDocConfig {

    private final ServerProperties serverProperties;

    @Bean
    @ConditionalOnMissingBean(OpenAPI.class)
    public OpenAPI openApi(SpringDocProperties properties) {
        OpenAPI openApi = new OpenAPI();
        // 文档基本信息
        SpringDocProperties.InfoProperties infoProperties = properties.getInfo();
        Info info = convertInfo(infoProperties);
        openApi.info(info);
        // 扩展文档信息
        openApi.externalDocs(properties.getExternalDocs());
        openApi.tags(properties.getTags());
        openApi.paths(properties.getPaths());
        openApi.components(properties.getComponents());
        Set<String> keySet = properties.getComponents().getSecuritySchemes().keySet();
        List<SecurityRequirement> list = new ArrayList<>();
        SecurityRequirement securityRequirement = new SecurityRequirement();
        keySet.forEach(securityRequirement::addList);
        list.add(securityRequirement);
        openApi.security(list);

        return openApi;
    }

    private Info convertInfo(SpringDocProperties.InfoProperties infoProperties) {
        Info info = new Info();
        info.setTitle(infoProperties.getTitle());
        info.setDescription(infoProperties.getDescription());
        info.setContact(infoProperties.getContact());
        info.setLicense(infoProperties.getLicense());
        info.setVersion(infoProperties.getVersion());
        return info;
    }

    /**
     * 自定义 openapi 处理器
     */
    @Bean
    public OpenAPIService openApiBuilder(Optional<OpenAPI> openAPI,
                                         SecurityService securityParser,
                                         SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
                                         Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers,
                                         Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers, Optional<JavadocProvider> javadocProvider) {
        return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider);
    }

    /**
     * 对已经生成好的 OpenApi 进行自定义操作
     */
    @Bean
    public OpenApiCustomiser openApiCustomiser() {
        String contextPath = serverProperties.getServlet().getContextPath();
        String finalContextPath;
        if (StringUtils.isBlank(contextPath) || "/".equals(contextPath)) {
            finalContextPath = "";
        } else {
            finalContextPath = contextPath;
        }
        // 对所有路径增加前置上下文路径
        return openApi -> {
            Paths oldPaths = openApi.getPaths();
            if (oldPaths instanceof PlusPaths) {
                return;
            }
            PlusPaths newPaths = new PlusPaths();
            oldPaths.forEach((k,v) -> newPaths.addPathItem(finalContextPath + k, v));
            openApi.setPaths(newPaths);
        };
    }

    /**
     * 单独使用一个类便于判断 解决springdoc路径拼接重复问题
     *
     * @author Lion Li
     */
    static class PlusPaths extends Paths {

        public PlusPaths() {
            super();
        }
    }

}


自定义 openapi 处理器

package com.ruoyi.framework.handler;

import cn.hutool.core.io.IoUtil;
import io.swagger.v3.core.jackson.TypeNameResolver;
import io.swagger.v3.core.util.AnnotationsUtils;
import io.swagger.v3.oas.annotations.tags.Tags;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springdoc.core.OpenAPIService;
import org.springdoc.core.PropertyResolverUtils;
import org.springdoc.core.SecurityService;
import org.springdoc.core.SpringDocConfigProperties;
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
import org.springdoc.core.providers.JavadocProvider;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.web.method.HandlerMethod;

import java.io.StringReader;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 自定义 openapi 处理器
 * 对源码功能进行修改 增强使用
 */
@SuppressWarnings("all")
public class OpenApiHandler extends OpenAPIService {

    /**
     * The constant LOGGER.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(OpenAPIService.class);

    /**
     * The Context.
     */
    private ApplicationContext context;

    /**
     * The Security parser.
     */
    private final SecurityService securityParser;

    /**
     * The Mappings map.
     */
    private final Map<String, Object> mappingsMap = new HashMap<>();

    /**
     * The Springdoc tags.
     */
    private final Map<HandlerMethod, io.swagger.v3.oas.models.tags.Tag> springdocTags = new HashMap<>();

    /**
     * The Open api builder customisers.
     */
    private final Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers;

    /**
     * The server base URL customisers.
     */
    private final Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers;

    /**
     * The Spring doc config properties.
     */
    private final SpringDocConfigProperties springDocConfigProperties;

    /**
     * The Open api.
     */
    private OpenAPI openAPI;

    /**
     * The Cached open api map.
     */
    private final Map<String, OpenAPI> cachedOpenAPI = new HashMap<>();

    /**
     * The Is servers present.
     */
    private boolean isServersPresent;

    /**
     * The Server base url.
     */
    private String serverBaseUrl;

    /**
     * The Property resolver utils.
     */
    private final PropertyResolverUtils propertyResolverUtils;

    /**
     * The javadoc provider.
     */
    private final Optional<JavadocProvider> javadocProvider;

    /**
     * The Basic error controller.
     */
    private static Class<?> basicErrorController;

    static {
        try {
            //spring-boot 2
            basicErrorController = Class.forName("org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController");
        } catch (ClassNotFoundException e) {
            //spring-boot 1
            try {
                basicErrorController = Class.forName("org.springframework.boot.autoconfigure.web.BasicErrorController");
            } catch (ClassNotFoundException classNotFoundException) {
                //Basic error controller class not found
                LOGGER.trace(classNotFoundException.getMessage());
            }
        }
    }

    /**
     * Instantiates a new Open api builder.
     *
     * @param openAPI                   the open api
     * @param securityParser            the security parser
     * @param springDocConfigProperties the spring doc config properties
     * @param propertyResolverUtils     the property resolver utils
     * @param openApiBuilderCustomizers the open api builder customisers
     * @param serverBaseUrlCustomizers  the server base url customizers
     * @param javadocProvider           the javadoc provider
     */
    public OpenApiHandler(Optional<OpenAPI> openAPI, SecurityService securityParser,
                          SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
                          Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
                          Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
                          Optional<JavadocProvider> javadocProvider) {
        super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
        if (openAPI.isPresent()) {
            this.openAPI = openAPI.get();
            if (this.openAPI.getComponents() == null)
                this.openAPI.setComponents(new Components());
            if (this.openAPI.getPaths() == null)
                this.openAPI.setPaths(new Paths());
            if (!CollectionUtils.isEmpty(this.openAPI.getServers()))
                this.isServersPresent = true;
        }
        this.propertyResolverUtils = propertyResolverUtils;
        this.securityParser = securityParser;
        this.springDocConfigProperties = springDocConfigProperties;
        this.openApiBuilderCustomisers = openApiBuilderCustomizers;
        this.serverBaseUrlCustomizers = serverBaseUrlCustomizers;
        this.javadocProvider = javadocProvider;
        if (springDocConfigProperties.isUseFqn())
            TypeNameResolver.std.setUseFqn(true);
    }

    @Override
    public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI, Locale locale) {

        Set<Tag> tags = new HashSet<>();
        Set<String> tagsStr = new HashSet<>();

        buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr, locale);
        buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr, locale);

        if (!CollectionUtils.isEmpty(tagsStr))
            tagsStr = tagsStr.stream()
                .map(str -> propertyResolverUtils.resolve(str, locale))
                .collect(Collectors.toSet());

        if (springdocTags.containsKey(handlerMethod)) {
            io.swagger.v3.oas.models.tags.Tag tag = springdocTags.get(handlerMethod);
            tagsStr.add(tag.getName());
            if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) {
                openAPI.addTagsItem(tag);
            }
        }

        if (!CollectionUtils.isEmpty(tagsStr)) {
            if (CollectionUtils.isEmpty(operation.getTags()))
                operation.setTags(new ArrayList<>(tagsStr));
            else {
                Set<String> operationTagsSet = new HashSet<>(operation.getTags());
                operationTagsSet.addAll(tagsStr);
                operation.getTags().clear();
                operation.getTags().addAll(operationTagsSet);
            }
        }

        if (isAutoTagClasses(operation)) {


            if (javadocProvider.isPresent()) {
                String description = javadocProvider.get().getClassJavadoc(handlerMethod.getBeanType());
                if (StringUtils.isNotBlank(description)) {
                    io.swagger.v3.oas.models.tags.Tag tag = new io.swagger.v3.oas.models.tags.Tag();

                    // 自定义部分 修改使用java注释当tag名
                    List<String> list = IoUtil.readLines(new StringReader(description), new ArrayList<>());
                    // tag.setName(tagAutoName);
                    tag.setName(list.get(0));
                    operation.addTagsItem(list.get(0));

                    tag.setDescription(description);
                    if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) {
                        openAPI.addTagsItem(tag);
                    }
                }
            } else {
                String tagAutoName = splitCamelCase(handlerMethod.getBeanType().getSimpleName());
                operation.addTagsItem(tagAutoName);
            }
        }

        if (!CollectionUtils.isEmpty(tags)) {
            // Existing tags
            List<io.swagger.v3.oas.models.tags.Tag> openApiTags = openAPI.getTags();
            if (!CollectionUtils.isEmpty(openApiTags))
                tags.addAll(openApiTags);
            openAPI.setTags(new ArrayList<>(tags));
        }

        // Handle SecurityRequirement at operation level
        io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirements = securityParser
            .getSecurityRequirements(handlerMethod);
        if (securityRequirements != null) {
            if (securityRequirements.length == 0)
                operation.setSecurity(Collections.emptyList());
            else
                securityParser.buildSecurityRequirement(securityRequirements, operation);
        }

        return operation;
    }

    private void buildTagsFromMethod(Method method, Set<io.swagger.v3.oas.models.tags.Tag> tags, Set<String> tagsStr, Locale locale) {
        // method tags
        Set<Tags> tagsSet = AnnotatedElementUtils
            .findAllMergedAnnotations(method, Tags.class);
        Set<io.swagger.v3.oas.annotations.tags.Tag> methodTags = tagsSet.stream()
            .flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet());
        methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class));
        if (!CollectionUtils.isEmpty(methodTags)) {
            tagsStr.addAll(methodTags.stream().map(tag -> propertyResolverUtils.resolve(tag.name(), locale)).collect(Collectors.toSet()));
            List<io.swagger.v3.oas.annotations.tags.Tag> allTags = new ArrayList<>(methodTags);
            addTags(allTags, tags, locale);
        }
    }

    private void addTags(List<io.swagger.v3.oas.annotations.tags.Tag> sourceTags, Set<io.swagger.v3.oas.models.tags.Tag> tags, Locale locale) {
        Optional<Set<io.swagger.v3.oas.models.tags.Tag>> optionalTagSet = AnnotationsUtils
            .getTags(sourceTags.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true);
        optionalTagSet.ifPresent(tagsSet -> {
            tagsSet.forEach(tag -> {
                tag.name(propertyResolverUtils.resolve(tag.getName(), locale));
                tag.description(propertyResolverUtils.resolve(tag.getDescription(), locale));
                if (tags.stream().noneMatch(t -> t.getName().equals(tag.getName())))
                    tags.add(tag);
            });
        });
    }

}


配置application.yml 属性

springdoc-openai 配置:

# springdoc-openai 配置
springdoc:
  api-docs:
    # 是否开启接口文档
    enabled: true
    # OpenAPI文档的路径
    path: /v3/api-docs
  swagger-ui:
    # swagger-ui路径
    path: /swagger-ui.html
    # 持久化认证数据
    persistAuthorization: true
  info:
    # 标题
    title: '标题:SpringDoc后台管理系统_接口文档'
    # 描述
    description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
    # 版本
    version: '版本号: 1.0.0'
    # 作者信息
    contact:
      name: lcz
      email: 1926585708@qq.com
      url: https://gitee.com/hsqyz
  components:
    # 鉴权方式配置
    security-schemes:
      apiKey:
        type: APIKEY
        in: HEADER
        name: Authorization
  # 分组配置
  group-configs:
    - group: SpringDoc项目模块接口文档
      packages-to-scan: com.hsqyz.spring_doc

配置Java编译器插件

主要看configuration配置这块内容:

			<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.9.0</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>com.github.therapi</groupId>
                            <artifactId>therapi-runtime-javadoc-scribe</artifactId>
                            <version>0.15.0</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                        <path>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-configuration-processor</artifactId>
                            <version>${springboot.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>

详细解释如下:

  1. <source>${java.version}</source> 和 <target>${java.version}</target>:设置Java源代码和目标字节码的版本。${java.version} 是一个占位符,表示从项目的属性中读取Java版本号。这意味着项目源代码使用的语言特性和编译后的class文件所对应的JVM版本是一致的。
  2. <encoding>${project.build.sourceEncoding}</encoding>:设置源代码文件的编码格式,${project.build.sourceEncoding} 表示从项目全局配置中读取编码格式。
  3. <annotationProcessorPaths>:这部分配置了注解处理器的路径,注解处理器是一种在编译阶段运行的工具,能够处理源代码中的特定注解,生成额外的类、资源文件或者其他辅助信息。
    • <path> 内的 、 和 分别指定了注解处理器的Maven坐标,即在Maven仓库中该注解处理器的组织ID、项目ID和版本号。
    • com.github.therapi:therapi-runtime-javadoc-scribe:这是一个用于提取Java方法上的Javadoc注解并在运行时使用的注解处理器。
    • org.projectlombok:lombok:Lombok是一个简化Java开发的工具,提供了众多注解如@Data@NoArgsConstructor等,注解处理器会在编译时自动插入getter/setter、equals/hashCode/toString等方法。
    • org.springframework.boot:spring-boot-configuration-processor:Spring Boot的配置处理器,用于在编译时生成关于@ConfigurationProperties注解类的元数据,使得IDE能提供更好的代码提示和验证。

总之,这段配置确保了Java源代码按指定的版本和编码进行编译,并在编译过程中利用指定的注解处理器生成额外有用的代码和元数据,提升开发效率和应用的功能性。


JavaDoc 元数据

配置好之后再次运行项目,查看target目录下会生成了JavaDoc的元数据


示例接口

/**
 * 用户
 */
@Slf4j
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class TestController {

    /**
     * 添加用户
     *
     * @param user 用户信息对象
     * @return 结果集
     */
    @PostMapping("/insert")
    public R insert(@RequestBody User user) {
        System.out.println("添加用户:" + user);
        return R.ok();
    }

    /**
     * 更新用户
     *
     * @param user 用户信息对象
     * @return 结果集
     */
    @PostMapping("/update")
    public R update(@RequestBody User user) {
        System.out.println("更新用户:" + user);
        return R.ok();
    }

}

准备好接口之后就可以开始准备启动了。

查看 OpenApi 接口

根据项目内所有文档组完成所有数据源创建(拉取后端openapi结构体)
数据源URL格式 http://后端ip:端口/v3/api-docs/组名
项目内所需:

  • http://localhost:8080/v3/api-docs/1.演示模块
  • http://localhost:8080/v3/api-docs/2.通用模块
  • http://localhost:8080/v3/api-docs/3.系统模块
  • http://localhost:8080/v3/api-docs/4.代码生成模块

也可不分组统一导入: http://localhost:8080/v3/api-docs

导入Apifox

打开Apifox选择导入项目,粘贴接口文档地址

点击提交,设置项目名称

导入成功

点击确定导入

查看接口


整合 knife4j

如果想让项目自带可视化接口调试界面,可以选择引入 knife4j 基于openapi3的依赖

<dependency>
	<groupId>com.github.xiaoymin</groupId>
	<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
	<version>4.4.0</version>
</dependency>

然后刷新依赖重启项目,访问:http://127.0.0.1:8080/doc.html

查看接口列表

查看数据模型

参考资料

  • 本教程参考自疯狂的狮子大佬开发的若依Plus版教程

教程结束!

散会!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RHEL9部署Docker环境

华子目录 Docker引擎架构docker引擎架构示意图执行过程示例 RHEL9上安装Docker1.系统要求2.安装yum-utils工具包3.yum安装docker-ce4.配置docker镜像加速docker拉取镜像的过程配置阿里云镜像仓库重新加载守护进程重启Docker服务 5.拉取并运行hello-world镜像6.测试是否安装成功…

【Godot4.2】基础知识 - Godot中的2D向量

概述 在Godot中&#xff0c;乃至一切游戏编程中&#xff0c;你应该都躲不开向量。这是每一个初学者都应该知道和掌握的内容&#xff0c;否则你将很难理解和实现某些其实原理非常简单的东西。 估计很多刚入坑Godot的小伙伴和我一样&#xff0c;不一定是计算机专业或编程相关专…

ROS机器人入门第二课:ROS集成开发环境搭建

文章目录 ROS机器人入门第二课&#xff1a;ROS集成开发环境搭建一、安装终端&#xff08;一&#xff09;安装Terminator&#xff08;二&#xff09;添加到收藏夹&#xff08;三&#xff09;Terminator 常用快捷键第一部份&#xff1a;关于在同一个标签内的操作第二部份&#xf…

【开发环境搭建篇】Nacos的安装和配置

作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过大学刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0…

01-机器学习概述

机器学习的定义 机器学习是一门从数据中研究算法的科学学科。 机器学习直白来讲&#xff0c; 就是根据已有的数据&#xff0c;进行算法选择&#xff0c;并基于算法和数据 构建模型&#xff0c;最终对未来进行预测。 机器学习就是一个模拟人决策过程的一种程序结构。 机器学…

系统大屏可视化展示平台解决方案(原件)

1.系统概述 1.1.需求分析 1.2.重难点分析 1.3.重难点解决措施 2.系统架构设计 2.1.系统架构图 2.2.关键技术 2.3.接口及要求 3.系统功能设计 3.1.功能清单列表 3.2.数据源管理 3.3.数据集管理 3.4.视图管理 3.5.仪表盘管理 3.6.移动端设计 3.7.系统权限设计 3.8.数据查询过程设…

隐私计算实训营学习三:隐私计算框架的架构和技术要点

文章目录 一、隐语架构二、产品层三、算法层3.1 PSI与PIR3.2 Data Analysis-SCQL3.3 Federated Learning 四、计算层4.1 混合调度编译-RayFed4.2 密态引擎4.3 密码原语YACL 五、资源管理层六、互联互通七、跨域管控 一、隐语架构 1、完备性&#xff1a;支持多种技术&#xff0…

QT:QTableWidget表格中加入勾选框

1.新建QTableWidget控件&#xff1a;tableWidget_TestResult 2.举例&#xff1a;在第一行第一列添加一个勾选框 //添加选择框QTableWidgetItem* check0 new QTableWidgetItem();check0->setCheckState(Qt::Checked);ui->tableWidget_TestResult->setItem(0, 0, chec…

Redis如何删除大key

参考阿里云Redis规范 查找大key&#xff1a; redis-cli --bigkeys 1、String类型&#xff1a; Redis 4.0及以后版本提供了UNLINK命令&#xff0c;该命令与DEL命令类似&#xff0c;但它会在后台异步删除key&#xff0c;不会阻塞当前客户端&#xff0c;也不会阻塞Redis服务器的…

C语言函数和数组

目录 一.数组 一.一维数组&#xff1a; 1.一维数组的创建: 2.一维数组的初始化&#xff1a; 3.一维数组的使用 4.一维数组在内存中的存储&#xff1a; 二.二维数组&#xff1a; 三.数组越界&#xff1a; 四.数组作为函数参数&#xff1a; 二.函数 一.函数是什么&…

NVIDIA最新 Blackwell架构简介

NVIDIA Blackwell架构简介 在AI和大型语言模型&#xff08;LLMs&#xff09;迅速发展的领域中&#xff0c;追求实时性能和可扩展性至关重要。从医疗保健到汽车行业&#xff0c;组织正深入探索生成性AI和加速计算解决方案的领域。对生成性AI解决方案的需求激增&#xff0c;促使企…

代码随想录算法训练营第二十八天|● 93.复原IP地址 ● 78.子集 ● 90.子集II (JS写法)

93 复原IP地址 题目链接/文章讲解&#xff1a;https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html 视频讲解&#xff1a;https://www.bilibili.com/video/BV1XP4y1U73i/ 思路&#xff1a; /*** param {string} s* return {string[]}*/ var resto…

Java数据结构-顺序表

目录 1. 顺序表的相关概念1.1 线性表1.2 顺序表2. 功能实现2.1 整体框架2.2 乱七八糟的功能(bushi)2.2.1 判断容量是否满2.2.2 返回顺序表当前长度2.2.3 扩容2.2.4 清空整个顺序表 2.3 插入数据2.3.1 头插数据2.3.2 尾插数据2.3.3 指定位置插入 2.4 删除数据2.4.1 删除第一次出…

中国贸易金融跨行交易区块链平台CTFU、区块链福费廷交易平台BCFT、中国人民银行贸易金融区块链平台CTFP、银行函证区块链服务平台BPBC

中国人民银行贸易金融区块链平台CTFP介绍 贸易金融的发展概况及存在的问题 1.1 贸易金融的概况 贸易金融是指商业银行在贸易双方债权债务关系的基础上&#xff0c;为国内或跨国的商品和服务贸易提供的贯穿贸易活动整个价值链、全程全面性的综合金融服务。伴随全球化的进程&a…

基于java+springboot+vue实现的宿舍管理系统(文末源码+Lw+ppt)23-597

摘 要 随着信息时代的来临&#xff0c;过去的传统管理方式缺点逐渐暴露&#xff0c;对过去的传统管理方式的缺点进行分析&#xff0c;采取计算机方式构建宿舍管理系统。本文通过课题背景、课题目的及意义相关技术&#xff0c;提出了一种楼宇信息、宿舍信息、宿舍安排、缺勤信…

使用Python进行自动化测试Selenium与PyTest的结合【第150篇—自动化测试】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 使用Python进行自动化测试&#xff1a;Selenium与PyTest的结合 在软件开发中&#xff0c;自…

数据结构:图的最短路径

目录 一、最短路径的基本概念 二、无权图单源最短路径 三、Dijkstra算法&#xff08;正权图单源&#xff09; 3.1、算法的基本步骤 3.2、算法的实现 3.3、习题思考 3.3.1、网络延迟时间 四、A*算法&#xff08;正权图单源单目标点&#xff09; 4.1、算法的基本概念 4…

web自动化--元素定位之xpath和css

元素定位 xpath绝对路径相对路径案例xpath策略&#xff08;路径&#xff09;案例xpath策略&#xff08;层级、扩展&#xff09;属性层级与属性层级与属性拓展层级与属性综合 csscss选择器&#xff08;id、类、标签、属性&#xff09;id选择器类选择器标签选择器属性选择器案例-…

黄衣老太怒骂,难阻年轻人的热情,苹果CEO库克笑开怀

苹果在中国最大的专卖店在上海静安开业&#xff0c;壮观的排队场景--特别的是排队的大多都是年轻人&#xff0c;凸显出国内消费者对苹果的热情&#xff0c;苹果CEO库克亲自在专门店门口迎客&#xff0c;还与消费者热情拥抱。 不过在苹果静安店开业的当天出现了一个插曲&#xf…

E3S独立出版 | 2024年第二届绿色建筑国际会议(ICoGB 2024)

会议简介 Brief Introduction 2024年第二届绿色建筑国际会议(ICoGB 2024) 会议时间&#xff1a;2024年5月22日-24日 召开地点&#xff1a;意大利米兰 大会官网&#xff1a;www.icogb.org ICoGB 2024由意大利米兰理工大学主办&#xff0c;西安交通大学&#xff0c;葡萄牙米尼奥大…