springboot2集成knife4j(swagger3)

2023-11-02

springboot2集成knife4j(swagger3)

环境说明

  • springboot:2.6.4
  • knife4j-spring-boot-starter:3.0.3

集成knife4j

第一步:引入依赖

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

第二步:编写配置类

提示:可以借助配置文件,进一步改造

import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.github.xiaoymin.knife4j.core.model.MarkdownProperty;
import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
import com.ideaaedi.demo.support.EnumDescriptor;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ModelSpecificationBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.PropertySpecificationBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.RequestParameterBuilder;
import springfox.documentation.schema.ScalarType;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelContext;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterContext;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Knife4j配置
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 1.0.0
 */
@Slf4j
@Configuration
@EnableSwagger2
public class Knife4jConfig implements WebMvcConfigurer {
    
    /** 对于管控了权限的应用,应放行以下资源 */
    public static String[] RESOURCE_URLS = new String[]{"/webjars/**", "/swagger**", "/v3/api-docs", "/doc.html"};
    
    @Value("${spring.application.name:default}")
    private String applicationName;
    
    @Resource
    private OpenApiExtensionResolver openApiExtensionResolver;
    
    @Bean
    public Docket docket() {
        // 指定使用Swagger2规范
        Docket docket = new Docket(DocumentationType.OAS_30)
                .apiInfo(new ApiInfoBuilder()
                        // 简介(支持Markdown语法)
                        .description("# 我是API简介")
                        // 服务地址
                        .termsOfServiceUrl("http://local.idea-aedi.com/")
                        // 作者及联系信息
                        .contact(new Contact("JustryDeng", "https://gitee.com/JustryDeng", "13548417409@163.com"))
                        // api版本
                        .version("1.0.0")
                        .build())
                //分组名称(微服务项目可以用微服务名分组)
                .groupName(applicationName)
                .select()
                // 定位api
                .apis(
                        RequestHandlerSelectors.basePackage(getProjectBasePackage())
                                .and(RequestHandlerSelectors.withClassAnnotation(RestController.class)
                                        .or(RequestHandlerSelectors.withClassAnnotation(Controller.class))
                                )
                )
                .paths(PathSelectors.any())
                .build();
    
        docket.securitySchemes(securitySchemes()).securityContexts(securityContexts());
        
        // 自定义文档解析
        try {
            Field markdownPropertiesField = FieldUtils.getDeclaredField(OpenApiExtensionResolver.class,
                    "markdownProperties", true);
            List<MarkdownProperty> markdownProperties = (List<MarkdownProperty>)markdownPropertiesField.get(openApiExtensionResolver);
            if (!CollectionUtils.isEmpty(markdownProperties)) {
                for (MarkdownProperty markdownProperty : markdownProperties) {
                    docket.extensions(openApiExtensionResolver.buildExtensions(markdownProperty.getGroup()));
                }
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        
        return docket;
    }
    
    private List<SecurityScheme> securitySchemes() {
        // 设置请求头信息
        List<SecurityScheme> result = new ArrayList<>();
        // 第一个参数name,自定义即可。 在配置securityContexts时,通过此name对应到apiKey即可
        // 第二个参数,header name自定义即可。 如:JWT_TOKEN_KEY=Auth-Token,然后在代码里request.getHeader(JWT_TOKEN_KEY)取值
        String JWT_TOKEN_KEY="Auth-Token";
        /// swagger 2
        /// ApiKey apiKey = new ApiKey("JustryDengApiKey", JWT_TOKEN_KEY, "header");
        // swagger3这里应该是个bug:页面上(参数key、参数名)实际展示及生效的都是第一个参数. 所以这里都直接用
        ApiKey apiKey = new ApiKey(JWT_TOKEN_KEY, JWT_TOKEN_KEY, "header");
        result.add(apiKey);
        return result;
    }
    
    private List<SecurityContext> securityContexts() {
        // 设置需要登录认证的路径
        List<SecurityContext> result = new ArrayList<>();
        List<SecurityReference> securityReferences = defaultAuth();
        result.add(
                SecurityContext.builder().securityReferences(securityReferences).forPaths(
                        // 当直接使用swagger文档发送请求时,这些api需要满足securityReferences认证要求
                        PathSelectors.regex("/.*")
                                .and(
                                        // 当直接使用swagger文档发送请求时,这些api不需要满足securityReferences认证要求. '.*'表示匹配所有
                                        PathSelectors.regex("/hello.*").or(PathSelectors.regex("/hi.*"))
                                                .negate()
                                )
                ).build()
        );
        return result;
    }
    
    private List<SecurityReference> defaultAuth() {
        List<SecurityReference> result = new ArrayList<>();
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        // 这里指定使用哪个apiKey进行认证鉴权. 这里指定使用上面名为Auth-Token的apiKey
        result.add(new SecurityReference("Auth-Token", authorizationScopes));
        return result;
    }
    
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
    
    /**
     * 获取项目包前缀
     */
    private String getProjectBasePackage() {
        String projectBasePackage;
        String currPackageName = this.getClass().getPackage().getName();
        String[] packageItemArr = currPackageName.split("\\.");
        if (packageItemArr.length > 3) {
            projectBasePackage = String.join(".", packageItemArr[0], packageItemArr[1], packageItemArr[2]);
        } else {
            projectBasePackage = currPackageName;
        }
        log.info("Base package to scan api is -> {}", projectBasePackage);
        return projectBasePackage;
    }
    
    /**
     * 显示自定义枚举类型注释
     * <p>
     * <br/> 参考<a
     * href="https://blog.gelu.me/2021/Knife4j-Swagger%E8%87%AA%E5%AE%9A%E4%B9%89%E6%9E%9A%E4%B8%BE%E7%B1%BB%E5%9E%8B
     * /">here</a>
     */
    @Component
    @SuppressWarnings("unchecked")
    public static class Knife4jSwaggerEnumPlugin implements ModelPropertyBuilderPlugin, ParameterBuilderPlugin {
        
        private static final Field parameterDescriptionField;
        
        private static final Field modelPropertyBuilderDescriptionField;
        
        
        static {
            parameterDescriptionField = ReflectionUtils.findField(RequestParameterBuilder.class, "description");
            Objects.requireNonNull(parameterDescriptionField, "parameterDescriptionField should noe be null.");
            ReflectionUtils.makeAccessible(parameterDescriptionField);
            
            modelPropertyBuilderDescriptionField = ReflectionUtils.findField(PropertySpecificationBuilder.class, "description");
            Objects.requireNonNull(modelPropertyBuilderDescriptionField, "ModelPropertyBuilder_descriptionField should noe be null.");
            ReflectionUtils.makeAccessible(modelPropertyBuilderDescriptionField);
        }
        
        /**
         * {@link ApiModelProperty}相关
         * <p>
         * 主要处理枚举对象直接作为方法参数的内部字段的情况. 如:
         * <pre>
         *  &nbsp;  @Data
         *  &nbsp;  public class LoginTokenRespVO {
         *  &nbsp;
         *  &nbsp;      @ApiModelProperty("用户类型")
         *  &nbsp;      private UserTypeEnum userType;
         *  &nbsp;  }
         * </pre>
         */
        @Override
        public void apply(ModelPropertyContext context) {
            Optional<BeanPropertyDefinition> optional = context.getBeanPropertyDefinition();
            if (!optional.isPresent()) {
                return;
            }
            // 对应被@ApiModelProperty标注的字段
            BeanPropertyDefinition beanPropertyDefinition = optional.get();
            AnnotatedField field = beanPropertyDefinition.getField();
            Class<?> fieldType = null;
            if (field != null) {
                fieldType = field.getRawType();
            } else {
                String fieldNameIgnoreCase = beanPropertyDefinition.getName();
                Field determinedField = Optional.ofNullable(context.getOwner())
                        .map(ModelContext::getType)
                        .map(ResolvedType::getErasedType)
                        .map(Class::getDeclaredFields)
                        .flatMap(fields -> Arrays.stream(fields).filter(x -> x.getName().equalsIgnoreCase(fieldNameIgnoreCase)).findFirst())
                        .orElse(null);
                if (determinedField != null) {
                    fieldType = determinedField.getType();
                }
            }
            if (fieldType == null || !Enum.class.isAssignableFrom(fieldType)) {
                return;
            }
            Class<Enum<?>> enumType = (Class<Enum<?>>) fieldType;
            Enum<?>[] enumConstants = enumType.getEnumConstants();
            PropertySpecificationBuilder modelPropertyBuilder = context.getSpecificationBuilder();
            Object oldValue = ReflectionUtils.getField(modelPropertyBuilderDescriptionField, modelPropertyBuilder);
            // 解析枚举
            List<String> enumDescList =
                    Arrays.stream(enumConstants).map(this::obtainEnumDescription).collect(Collectors.toList());
            modelPropertyBuilder.description((oldValue == null ? "" : oldValue) + buildHtmlUnOrderList(enumDescList))
                    .type(new ModelSpecificationBuilder().scalarModel(ScalarType.UUID).build());
        }
        
        /**
         * {@link ApiParam}、{@link io.swagger.v3.oas.annotations.Parameter}相关.
         * <p> 主要处理:枚举对象直接作为方法参数的情况. 如:
         * <pre>
         *  &nbsp;  @PostMapping("/test1")
         *  &nbsp;  @ApiOperation(value = "测试1")
         *  &nbsp;  public void test1(@ApiParam(value = "用户类型", required = true) UserTypeEnum userTypeEnum)
         * </pre>
         */
        @Override
        public void apply(ParameterContext context) {
            Class<?> type = context.resolvedMethodParameter().getParameterType().getErasedType();
            RequestParameterBuilder parameterBuilder = context.requestParameterBuilder();
            if (!Enum.class.isAssignableFrom(type)) {
                return;
            }
            Class<Enum<?>> enumType = (Class<Enum<?>>) type;
            Enum<?>[] enumConstants = enumType.getEnumConstants();
            // 解析枚举
            List<String> enumDescList = Arrays.stream(enumConstants).map(this::obtainEnumDescription).collect(Collectors.toList());
            Object oldValue = ReflectionUtils.getField(parameterDescriptionField, parameterBuilder);
            parameterBuilder.description((oldValue == null ? "" : oldValue) + buildHtmlUnOrderList(enumDescList));
        }
        
        /**
         * 此插件是否支持处理该DocumentationType
         */
        @Override
        public boolean supports(@NonNull DocumentationType documentationType) {
            return true;
        }
        
        /**
         * 获取枚举描述
         *
         * @param enumObj 枚举对象
         *
         * @return 枚举描述
         */
        private String obtainEnumDescription(@NonNull Enum<?> enumObj) {
            String name = enumObj.name();
            /*
             * 枚举说明器示例:
             *
             * public interface EnumDescriptor {
             *     // 获取枚举项说明
             *     String obtainDescription();
             * }
             */
            if (enumObj instanceof EnumDescriptor) {
                return name + ":" + ((EnumDescriptor) enumObj).obtainDescription();
            }
            return name;
        }
        
        /**
         * 构建无序列表html
         *
         * @param itemList 列表元素
         *
         * @return 无序列表html
         */
        private String buildHtmlUnOrderList(@Nullable List<String> itemList) {
            if (CollectionUtils.isEmpty(itemList)) {
                return "";
            }
            StringBuilder sb = new StringBuilder();
            sb.append("<ul>");
            for (String item : itemList) {
                sb.append("<li>");
                sb.append(item);
                sb.append("</li>");
            }
            sb.append("</ul>");
            return sb.toString();
        }
    }
}

第三步:放行相关资源 & 保证启动了knife4j

  • 放行相关资源

    对于管控了权限的应用,应放行以下资源

    /** 对于管控了权限的应用,应放行以下资源 */
    public static String[] RESOURCE_URLS = new String[]{"/webjars/**", "/swagger**", "/v3/api-docs", "/doc.html"};
    
  • 保证启动了knife4j

    knife4j:
      # 启动knife4j(注:有时,如果我们不进行此配置,knife4j不会开启)
      # 其实核心是:保证com.github.xiaoymin.knife4j.spring.configuration.Knife4jAutoConfiguration生效即可。如果knife4j怎么也没启动,请debug此类,确保其被加载
      enable: true
      # 添加自定义文档
      documents:
        - group: my-group # 这里的group是处理自定义文档时用到的标识,自定义唯一即可
          # 效果等价使用 @Api(tags = "自定义文档")
          name: 自定义文档
          # 指定.md文档位置 (更多详见官网:https://doc.xiaominfo.com/docs/features/selfdocument)
          # 每个md文档的标题,则等价于 @ApiOperation(value = "md文档的标题")
          locations: classpath:api-doc/*.md
    
    # 兼容swagger3
    spring:
      mvc:
        pathmatch:
          matching-strategy: ant_path_matcher
    

第四步:测试一下

第一小步:编写controller
import com.ideaaedi.demo.controller.model.UserAddReqVO;
import com.ideaaedi.demo.controller.model.UserDetailRespVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 用于测试knife4j的controller
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 1.0.0
 */
@RestController
@Api(tags = "我是DemoController")
public class TestController {
    
    @GetMapping("/hello")
    @ApiOperation(value = "哈喽")
    public String hello(@ApiParam(name = "name", value = "姓名", required = true) @RequestParam String name) {
        return "hello " + name;
    }
    
    @GetMapping("/hi")
    @ApiOperation(value = "嘿")
    public String hei(@ApiParam(name = "name", value = "姓名", required = true) @RequestParam String name) {
        return "hello " + name;
    }
    
    @PostMapping("/user/add")
    @ApiOperation(value = "新增用户")
    public UserDetailRespVO addUser(@RequestBody UserAddReqVO req) {
        UserDetailRespVO resp = new UserDetailRespVO();
        resp.setId(9527L);
        resp.setName(req.getName());
        resp.setAge(req.getAge());
        return resp;
    }
    
    @DeleteMapping("/user/delete/{id}")
    @ApiOperation(value = "删除用户")
    public Boolean addUser(@ApiParam(name = "id", value = "数据id", required = true) @PathVariable Long id) {
        return true;
    }
    
    /**
     * 测试 @RequestBody、@RequestParam、@PathVariable并存
     */
    @PostMapping("/multi-anno/{id}")
    @ApiOperation(value = "组合使用测试")
    public UserDetailRespVO testMultiAnno(@RequestBody UserAddReqVO req, @ApiParam(name = "name", value = "姓名",
            required = true) @RequestParam String name,
                                          @ApiParam(name = "id", value = "数据id", required = true) @PathVariable Long id) {
        UserDetailRespVO resp = new UserDetailRespVO();
        resp.setId(9527L);
        resp.setName(req.getName());
        resp.setAge(req.getAge());
        return resp;
    }
    
}

此controller中用到的相关模型

  • UserAddReqVO

    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    
    import javax.validation.constraints.NotBlank;
    
    /**
     * 用户新增req模型
     */
    @Data
    public class UserAddReqVO {
        
        @ApiModelProperty(value = "姓名",required = true)
        @NotBlank(message = "姓名不能为空")
        private String name;
        
        @ApiModelProperty("年龄")
        private Integer age;
    }
    
  • UserDetailRespVO

    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    
    /**
     * 用户详情resp模型
     */
    @Data
    public class UserDetailRespVO {
        
        @ApiModelProperty("id")
        private Long id;
        
        @ApiModelProperty("姓名")
        private String name;
        
        @ApiModelProperty("年龄")
        private Integer age;
    }
    
第二小步:启动项目,访问api文档

启动项目后,直接访问http://{ip}:{端口}/doc.html即可

在这里插入图片描述

在这里插入图片描述

说明:

  • 文档分组(不设置则默认分组名称为default):可切换观察其余分组下的api
  • 主页:概览
  • Swagger Models:可以查看所有请求模型的信息
  • 文档管理:可以导出文档、进行高级设置(如设置后处理脚本等)、进行全局参数设置、查看api信息
  • 自定义文档:可以添加一些自定义的md格式的文档
  • 点击进入文档后,会展示api的详细信息,也可以进行调试,还可以打开api json数据等等

相关资料

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

springboot2集成knife4j(swagger3) 的相关文章

  • CesiumJSQuickstart_译

    CesiumJSQuickstart 译 这是一个用Cesium使用真实世界数据建立3D应用的快速开始 你会学到怎样像这样在网页设置一个Cesium应用 省略 步骤1 创建一个账号获取一个令牌 Cesium ion是一个流送和管理3D内容的
  • HSQLDB 和 H2 数据库比较

    前面在介绍Vaadin SQL Container时使用了HSQLDB 也说过SQL Container在使用上并不十分方便 不如直接使用hibernate 来的实用 最近准备开始介绍hibernate 的开发指南 数据库系统也会使用H2
  • java对csv格式的读写操作

    CSV格式 它可以用excel打开 如果用记事本打开 会发现它是一种使用逗号 作为不同列之间的分隔符 读取CSV格式 import java io 使用例子 CSVReader reader new CSVReader D 114 csv

随机推荐

  • String StringBuffer 和 StringBuilder

    1 String StringBuffer 和 StringBuilder 的区别是什么 String 为什么是不可变的 简单的来说 String 类中使用 final 关键字字符数组保存字符串 private final char val
  • Linux下rz,sz与ssh的配合使用

    一般来说 linux服务器大多是通过ssh客户端来进行远程的登陆和管理的 使用ssh登陆linux主机以后 如何能够快速的和本地机器进行文件的交互呢 也就是上传和下载文件到服务器和本地 与ssh有关的两个命令可以提供很方便的操作 sz 将选
  • 使用dplyr包的rename函数为R语言中的数据框中的所有列重命名

    使用dplyr包的rename函数为R语言中的数据框中的所有列重命名 在R语言中 dplyr是一个强大的数据处理包 它提供了一系列函数来操作数据框 其中 rename函数可以用于为数据框中的列进行重命名操作 本文将详细介绍如何使用dplyr
  • C++ Lambda表达式(匿名函数): [](){}

    C Lambda表达式 匿名函数 Lambda 匿名函数表达式 1 完整的声明格式如下 1 1 省略声明形式 2 使用示例 作为STL中的仿函数functor充当谓词函数 3 Lambda函数中的变量截取规则 修改捕获变量 mutable关
  • 微信公众号H5利用JS-SDK中的开放标签wx-open-launch-weapp跳转第三方小程序

    场景 在微信公众号的h5页面中有这样一个需求 需要跳转到第三方小程序 我这里主要记录一下使用JS SDK中的开放标签wx open launch weapp来实现跳转 以及我在需求中遇到的一些问题 以及怎么解决的 我是使用的HBuilder
  • 等价类划分法设计用例(超详细)

    等价类划分法 等价类 1 解决了不能穷举测试的问题 控制成本 控制测试用例数量 2 数据值要明确 对文字敏感 3 依据需求将输入划分为若干个等价类 划分等价类 需求 数据特征 等价类设计用例的难点 如何根据时间成本划分等价类 等价类分为 1
  • aircrack-ng渗透WPA加密

    目录 一 WPA概念介绍 二 工作原理 三 wifi设置 1 打开wifi设置网站 2 选择无线配置 3 选择加密方式 四 打造字典 1 crunch生成密码 2 手工打造 五 渗透步骤 1 查看网卡 2 开启监听模式 3 扫描wifi 4
  • 2019-2-28 dvwa学习(4)--sql注入级别impossible

    继续把dvwa环境安全级别调整为impossible 观察界面 看上去似乎和low级别没有大的区别 只是浏览器中get方法提交的参数多了一个 impossible php代码如下
  • 相似性度量准则(距离 相似系数)

    在分类聚类算法 推荐系统中 常要用到两个输入变量 通常是特征向量的形式 距离的计算 即相似性度量 不同相似性度量对于算法的结果 有些时候 差异很大 因此 有必要根据输入数据的特征 选择一种合适的相似性度量方法 令X x1 x2 xn T Y
  • 数据结构 实验一 顺序表的操作

    一 顺序表的操作 任务一 初始化一个顺序表并输出 相关知识 需要掌握 1 结构体变量初始化 2 如何遍历顺序表 结构体变量初始化 定义结构体变量的同时 给结构体变量的各个成员变量赋值 示例如下 sequenlist sq 1 3 6 7 3
  • 【Linux】文件的权限

    权限笼统分为两种一种是人的权限 一种是文件的访问权限 而root 管理员 具有Linux最高的权限 最多只有一个 而普通用户可以有多个 要受到权限的约束 一 切换root权限 既然root是最大的权限 那么这里就来了解一下如果切换到root
  • 六、pcb文件设计规则

    设计pcb文件的布线规则 1 普通线宽为6mil 电源线或者需要加强的信号线宽为15mil 点击设计 gt 规则 进行信号线的设计 设置为6mil 如图 2 设置过孔规则 如图 设置过孔大小为12mil 过孔直径为22mil 3 新建一个类
  • Intellij idea 快捷键(1)--生成for循环代码块

    使用Intellij idea 时 想要快捷生成for循环代码块 itar 生成array for代码块 for int i 0 i lt array length i array i itco 生成Collection迭代 for Ite
  • 程序员职业的未来会受到失业的影响吗?

    关于提到的程序员未来是否会失业的问题 这是一个复杂而有争议的话题 虽然自动化和人工智能的发展可能对某些程序员的工作产生一定冲击 但是在同样的时间 也会产生新的就业机会和需求 技术的不断进步需要我们持续学习和适应 同时培养创新能力和解决问题的
  • servlet容器、web容器、spring容器、springmvc容器

    容器介绍 web容器中有servlet容器 spring项目部署后存在spring容器和springmvc容器 其中spring控制service层和dao层的bean对象 springmvc容器控制controller层bean对象 se
  • Java工程师看过来:入门到高级书单都在这!

    关于程序员 除了做项目来提高自身的技术 还有一种提升自己的专业技能就是 多 看 书 Java程序员你们准备好了吗 我们大圣众包 www dashengzb cn 双手奉上Java程序员必读之热门书单 入门 HeadFirstJava 作者
  • oracle 9i在线重定义功能应用于生产库

    今天 在客户生产库了用oracle 9i的在线重定义功能实现了由普通表转换为分区表的实施 总体实施过程还算 比较顺利 以下是测试过程 create test mid table create table test test mid ID N
  • python下使用libsvm:计算点到超平面的距离

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 最近在看的资料里涉及到计算 点到支持向量机分类超平面的距离 这一点内容 我使用的svm是libsvm 由于是新手 虽然看了一些资料 但中英转换误差等等原因导致经常出现理解错
  • Postman post请求返回错误状态码总结

    目录 Postman 踩坑总结 一 404 page not found 1 请求的参数不对 比如是POST请求但是参数却写的是GET 2 页面url写错或不存在 二 status 500 最近在使用Postman对接口进行测试 踩了几个坑
  • springboot2集成knife4j(swagger3)

    springboot2集成knife4j swagger3 springboot2集成knife4j swagger3 环境说明 集成knife4j 第一步 引入依赖 第二步 编写配置类 第三步 放行相关资源 保证启动了knife4j 第四