兴趣的朋友可以去了解一下其他几篇,你的赞就是对我最大的支持,感谢大家!

(一) SpringBoot起飞之路-HelloWorld

(二) SpringBoot起飞之路-入门原理分析

(三) SpringBoot起飞之路-YAML配置小结(入门必知必会)

(四) SpringBoot起飞之路-静态资源处理

(五) SpringBoot起飞之路-Thymeleaf模板引擎

(六) SpringBoot起飞之路-整合JdbcTemplate-Druid-MyBatis

(七) SpringBoot起飞之路-整合SpringSecurity

(八) SpringBoot起飞之路-整合Shiro

说明:

  • 3.0 的版本没怎么用过,只是进行了简单的整合,或许会有一些不完善的地方,欢迎大家交流分享
  • SpringBoot 起飞之路 系列文章的源码,均同步上传到 github 了,有需要的小伙伴,随意去 down

一 初识 Swagger

跳过铺垫,请直接翻越到 第二大点 ~

(一) 先谈谈前后端分离

在最早的 JavaWeb 时代的时候,如果想要返回一个页面,你需要一行一行的去 print,如果遇到变量,还需要自己进行字符串的拼接

Public class TestServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponseresp response)
throws ServletException, IOException {
String name = req.getParameter("name");
String age = req.getParameter("age");
PrintWriter out = resp.getWriter();
out.println("<html>");
out.println("<head><title>展示数据</title></head>");
out.println("<body>name:" + name + "<br/> age: " + age +"</body>");
out.println("</html>");
}
}

接着 JSP 就出现了,其本质虽然还是一个 Servlet,不过编写一个 JSP 的方式和编写 HTML 的是基本一致的,但是 JSP 开始允许我们在页面中通过 %% 引入 Java 代码,也就是说我们可以通过在 JSP 页面中通过书写 Java 代码达到显示动态内容的效果,例如在 JSP 中定义方法、书写控制台输出语句等等,大部分你能想到的逻辑都可以在这里来做

后来一看这不行啊,业务逻辑和视图逻辑都混一块了,越写越乱,一个页面就几百行甚至上千行,真可怕啊,后来大家就想了个法子,用 MVC 来解耦啊,把 JSP 的业务抽取出来交给后台的 Serlvet 去做,JSP 用来承载数据,展示页面

如下代码:从 session 中取到数据,然后再 JSP 中进行遍历,不过这段代码有简单用了一些标签,这是我们后面要说的

最后 JSP 会被编译成 Servlet

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@page import="cn.ideal.pojo.Good" %>
<!--
请求 /index.do 进行初始化
-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<body>
<img src="img/cart.jpg" height="60px" width="60px">
<a href="cart.view">已采购
<c:out value="${sumNum}" default="没有任何"/>商品
</a><br>
<br>
<table border="1">
<%
ArrayList<Good> goodList = (ArrayList<Good>)session.getAttribute("goodList");
for (Good good : goodList){
out.println("<tr>") ;
out.println("<td> <img src='" + good.getGoodImg() + "'> </td>");
String id = good.getGoodId();
out.println("<td> <a href=shopping.view?id=" + id + ">采购</a></td>");
out.println("</tr>");
}
%>
</table></body>
</html>

虽然 JSP 不处理业务逻辑了,看起来也没那么臃肿了,但是如上所示, JSP 想获取 Servlet传来 的一些内容,仍然需要通过 %% 去获取,例如拿到后遍历,判断等内容还需要自己在JSP中写 Java 代码

EL 和 JSTL 表达式就来了,通过一些定义好的标签,简单的拿到数据以及展示

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!--
请求 /index.do 进行初始化
-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<body>
<img src="img/cart.jpg" height="60px" width="60px">
<a href="showCart.jsp">已采购 <c:out value="${sessionScope.cart.sumNum}" default="没有任何"/>品
</a><br>
<br>
<table border="1">
<c:forEach var="good" items="${cart.goodList}">
<tr>
<td><img src='${good.goodImg}'></td>
<td><c:out value='${good.getPrice()}'/> 元</td>
<td><a href="shopping.view?id=${good.bookId}&type=1&method=1">采购</a></td>
</tr>
</c:forEach>
</table>
</body>
</html>

再到后来,Freemarker、Thymeleaf、Velocity 等,模板引擎就来了,他们和 JSP 的表达式写法很相似,有自己的一套表达式来拿到并且处理数据,例如这样:

<table border="1">
<tr>
<th>Name</th>
<th>Age</th>
</tr>
<tr th:each="user : ${userList}">
<td th:text="${user.nickname}">NULL</td>
<td th:text="${user.age}">0</td>
</tr>
</table>

这样一套老开发模式,实际上大部分压力都放在了后端人员上,因为例如前端发来一份 html,后端还需要修改为 JSP ,也就是用标签替换其中一些固定的内容,实现动态效果,但是项目比较大的情况下,不管从人力亦或是开发成本来看都是不合适的,而且术业有专攻,如果后端只需要管自己后台业务的事情就行了该多好

这个时候前端就开始异军突起了,他们开始只使用简单 HTML、CSS 来展示数据,也就是纯粹的页面展示,通过 JS,把数据请求得到的数据进行填充,最开始可能会使用操作 DOM,但是随着前端框架的完善,例如现在常见的 Vue、Angular,前端都开始用 MVC 这套,也就是 html 作为视图,js作为控制器,异步请求,通过标签来展示数据,直接把数据填充到 html 页面中

<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
[v-clock] {
display: none;
}
</style>
</head>
<body>
<div id="hello-8" v-clock>
<div>{{info.name}}</div>
<div>{{info.blog}}</div>
<div>{{info.about.country}}</div>
<div>{{info.about.phone}}</div>
<div>{{info.students[0].name}}</div>
</div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script>
var vm = new Vue({
el: "#hello-8",
data() {
return {
info: {}
}
},
mounted() {
axios.get("../json/data.json").then(response => (this.info = response.data));
}
})
</script>
</body>
</html>

这就意味着,后端只需要给出根据前端接口需要的,返回对应数据(例如 JSON)就可以了

这种通过 API 进行交互的方式实际上也是松耦合的,双方都比较灵活且独立

(二) 为什么用 Swagger

前面感觉一直在说题外话,但是问题的关键就在于这一点,这种模块开发,前后分离的情况下,前端或者后端的沟通就会极为重要,有时候没能做到及时的协商,就会出现很多意想不到的问题,同时前后端分离大行其道,也就代表着接口调用会大量的出现,如果你的 API 同时对接 Web、IOS、Android 等多个开发,为了减少沟通的代价,那必然就是要写文档的,用来记录所有接口的细节

但是如何创建一个繁多且复杂的文档就是一个非常吃力的事情了,毕竟要考虑的地方非常多,同时随着代码的迭代,API 文档更新管理一定要严格把控,否则都会带来不必要的麻烦,显然传统的 Wiki

手写文档一定是一个非常麻烦的过程,Swagger 他就来了

(三) 什么是 Swagger

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务

其总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。

刚开始的时候,Swagger 只是一种规范,产品开发迭代的时候,通过更新描述文件就可以生成接口文档和客户/服务端代码,但是开发人员时不时就会忘记更新此文件,直接改代码,后序,作为大头的 Spring 就将其正式纳入麾下,建立了 Spring-swagger项目,即现在的 springfox(swagger2)

其最大的优点就是,能实时同步 API 和文档,只需要通过简单的整合亦配合一些注解,就可以体验到其丰富的功能

通常就目前为止,大部分项目中还是在用 Swagger2,通过maven仓库也可以看到,2.9.2 是使用率最高的,同样后面我们还会演示一下 Swagger3 的版本,因为它毕竟是今年刚出的,其简化了很多配置,个人感觉还算香

说明

二 Springboot 集成 Swagger 2

(一) 依赖及初始化

先初始化一个 Springboot 项目

(1) 引入依赖

<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>

(2) 编写Controller

@RestController
@RequestMapping("test/")
public class TestController {
@RequestMapping("/testA")
public String testA(){
return "This is a test";
}
}

(3) 创建 Swagger 配置类

@Configuration // 配置类
@EnableSwagger2 // 开启Swagger2的自动配置
public class SwaggerConfig {
// 暂时为空
}

注:我们使用的是 Swagger2

(4) 访问测试

先测试一下 controller 有没有问题,接着通过如下地址就可以访问到 swagger 的界面

http://localhost:8080/swagger-ui.html

可以看到大概这个页面分为三个部分,上面就是一些介绍信息,中间就是一个一个的接口信息,下面是实体

(二) 配置 Swagger 信息

我们需要在我们自定义的 Swagger 配置类中配置一些内容,就需要引入一个 Bean,Swagger 的实例Bean 就是 Docket,所以我们需要实例化一个 Docket

/**
* 配置 docket 和 Swagger 的具体参数
* @return
*/
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2);
}

大家可以点开 Docket 源码,然后可以看到它引入了 ApiInfo 这个类,其中定义了一些基本的信息例如,版本,标题等等

public static final Contact DEFAULT_CONTACT = new Contact("", "", "");
public static final ApiInfo DEFAULT = new ApiInfo("Api Documentation", "Api Documentation", "1.0", "urn:tos",
DEFAULT_CONTACT, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList<VendorExtension>()); private final String version;
private final String title;
private final String description;
private final String termsOfServiceUrl;
private final String license;
private final String licenseUrl;
private final Contact contact;
private final List<VendorExtension> vendorExtensions;

大家可能已经看出来了,这个地方的内容,就是负责刚开我们打开那个 swagger-ui 页面的头部文档信息的,其默认值例如 Api Documentation 、1.0 、Apache 2.0 大家可以自己对照一下

好了,知道了它的类和基本结构,我们就要自定义这些信息了

/**
* 配置文档信息
* @return
*/
private ApiInfo apiInfo() {
Contact contact = new Contact("BWH_Steven", "https://www.ideal-20.cn", "ideal_bwh@163.com");
return new ApiInfo(
"Swagger2学习——理想二旬不止", // title-标题
"学习演示如何配置Swagger2", // description-描述
"v1.0", // version-版本
"https://www.ideal-20.cn", // termsOfServiceUrl-组织链接
contact, // contact-联系人信息
"Apach 2.0 许可", // license-许可
"许可链接", // licenseUrl-许可连接
new ArrayList<>()// vendorExtensions-扩展
);
}

配置完后,我们还没有把它和 Docket 实例挂起钩来

/**
* 配置 docket 和 Swagger 的具体参数
* @return
*/
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo());
}

再次访问地址,看看效果

http://localhost:8080/swagger-ui.html

(三) 配置自定义扫描方式配置

(1) 如何配置

留心的朋友大家看到,第一次访问的时候上面还有一个默认的 basic-seeor-controller,而后面则没有了,这是因为我在演示配置信息的时候,忘记把我自定义扫描方式给注释掉了

所以,我们现在来提一下如何自己定义扫描哪些接口

在 Docket 中可以通过调用 select() 方法来配置扫描的接口,要使用这个方式就必须在其后跟上 build,这是设计模式中的一种,建造者模式,具体如何配置呢,只需要在 select 和 build 中 apis 即可,因为 apis 中需要一个 requestHandlerSelector,所以 requestHandlerSelector 就是我们最终要具体配置的地方

先上一份例子,对照上面的解释看

/**
* 配置 docket 和 Swagger 的具体参数
* @return
*/
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
// 通过.select()方法,去配置扫描接口
.select()
// RequestHandlerSelectors 配置扫描接口的具体方式
.apis(RequestHandlerSelectors.basePackage("cn.ideal.controller"))
.build();
}

(2) 可选扫描方式

大家可以点进去 RequestHandlerSelectors 看一下,除了 basePackage 还有哪些

any()

  • 扫描项目中的所有接口

none()

  • 不扫描任何接口

withMethodAnnotation(final Class<? extends Annotation> annotation)

  • 通过方法上的注解扫描,如 withMethodAnnotation(GetMapping.class) 只扫描 get 请求

withClassAnnotation(final Class<? extends Annotation> annotation)

  • 通过类上的注解扫描,如 .withClassAnnotation(Controller.class) 只扫描有 controller 注解的类中的接口

basePackage(final String basePackage)

  • 根据包路径扫描接口

(3) 配置扫描过滤

当你写好 select 和 build 后,其实两者中间就只提供调用 apis 和 paths 了,而 paths 就是现在要说的扫描过滤

/**
* 配置 docket 和 Swagger 的具体参数
* @return
*/
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("cn.ideal.controller"))
// 配置如何通过 path 过滤
// 这里只扫描请求以/test 开头的接口
.paths(PathSelectors.ant("/test/**"))
.build();
}

可选方式

any()

  • 扫描所有请求

none()

  • 任何请求都不扫描

regex(final String pathRegex)

  • 根据正则表达式

ant(final String antPattern)

  • 通过 ant() 控制(如上代码,可以使用一些通配符)

(四) 配置 Swagger 的开启和关闭

通过在 Docket 中调用 enable(boolean externallyConfiguredFlag) 可以控制 Swagger 的开和关,也就是说,如果配置 .enable(false) 浏览器中访问就会出错

讲个实际点的例子,一般我们会在开发以及测试阶段开启 Swagger ,但是正式上线就会把它关掉

所以,我们得动态的让其进行配置

首先,我们先分别创建 dev 和 test 以及 prod,三个环境

  • 创建 application-dev.properties,设置 server.port=8080

  • 创建 application-test.properties,设置 server.port=8081

  • 创建 application-prod.properties,设置 server.port=8082

/**
* 配置 docket 和 Swagger 的具体参数
* @return
*/
@Bean
public Docket docket(Environment environment) { // 设置要显示swagger的环境
Profiles of = Profiles.of("dev", "test");
// 判断当前是否处于该环境
boolean flag = environment.acceptsProfiles(of); return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
// 通过 enable() 接收决定是否要显示
.enable(flag)
.select()
.apis(RequestHandlerSelectors.basePackage("cn.ideal.controller"))
.paths(PathSelectors.ant("/test/**"))
.build();
}

在主配置 appplication.properties 中指定当前为开发版本 spring.profiles.active=dev

这样就可以访问对应设置好的 8080 了,同理设置为别的,就只能访问别的

(五) 配置分组

在 ui 界面其实大家可以注意到,右上角有一个 default,以及一个下拉选项(暂时还没内容)

这里就是一个分组的概念,也就是我们要实现 API 接口的分类

方法非常简单,只需要通过在 Docket 中,调用 groupName() 即可

@Bean
public Docket docketA(){
return new Docket(DocumentationType.SWAGGER_2).groupName("groupA");
}
@Bean
public Docket docketB(){
return new Docket(DocumentationType.SWAGGER_2).groupName("groupB");
}
@Bean
public Docket docketC(){
return new Docket(DocumentationType.SWAGGER_2).groupName("groupC");
}

重启后,就可以看到分组的效果了,不同的组别里进行不同的配置,能达到不同的访问效果

(六) 常用注解

(一) 作用在类

(1) @Api()

写了一些常用的注解和其参数,不一定全,不过应该还是够用的

Swagger 配合注解更香喔 ~

@Api():表示这个类是 swagger 资源

  • tags:表示说明内容,只写 tags 就可以省略属性名
  • value:同样是说明,不过会被 tags 替代
@Api(value = "测试接口value", tags = "测试接口Tags")
@RestController
@RequestMapping("test/")
public class TestController {
......
}

上面同时定义了 tags 和 value,最后显示的只有 tags ,两者都可以单独使用

(二) 作用在方法

(1) @ApiOperation()

@ApiOperation() :对方法的说明,注解位于方法上

  • value:方法的说明
  • notes:额外注释说明
  • response:返回的对象
  • tags:这个方法会被单独摘出来,重新分组
@ApiOperation(
value = "用户信息查询方法Value",
notes = "这是用户信息查询方法的注释说明",
response = User.class,
tags = {"用户信息查询方法Tags"})
@GetMapping("/queryUser")
public User queryUser() {
......
}

说明:注释需要点开每一个方法才能看到

补充:如果在方法上使用 @RequestMapping() 注解,那么文档中就会有各种类型的说明,例如 GET、POST 等等等等,所以一般我们会指定其请求类型,例如 @GetMapping("/queryUser")

(2) @ApiParam()

@ApiParam() :对方法参数的具体说明

  • name:参数名
  • value:参数说明
  • required:是否必填
@ApiOperation(value = "用户登录方法", notes = "用户登录注释说明")
@PostMapping("/login")
public String login(@ApiParam(name = "username", value = "用户名", required = true) String username,
@ApiParam(name = "password", value = "密码", required = true) String password) {
if ("123".equals(username) && "123".equals(password)) {
return "登录成功";
} else {
return "登录失败";
}
}

点开这个方法,可以看到,这些注解都体现在文档中了

(三) 作用在模型类

(1) @ApiModel()

@ApiModel() 作用在模型类上,如 VO、BO ,表示对类进行说明

  • value:代表模型的名称
  • description:一段描述

(2) @ApiModelProperty()

@ApiModelProperty() 作用在类方法和属性上,表示对属性的说明

  • value:字段说明
  • name:重写属性名字
  • dataType:重写属性类型
  • required:是否必填
  • example:举例说明
  • hidden:隐藏

(两者一起演示)可在文档首页,以及具体方法涉及到时,会显示此 model 说明信息

三 Springboot 集成 Swagger3

大部分内容上面都有提过,所以这里只说一些不同的地方

一个就是依赖可以用 3.0 的starter

其次就是配置中修改 return new Docket(DocumentationType.OAS_30)

同时不需要引入 @EnableSwagger2 注解了

都说启动类需要加 @EnableOpenApi , 试了一下,好像是不需要的

※ 启动路径变成了

http://localhost:8080/swagger-ui/index.html

(一) 引入依赖

<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>

(二) 配置类

@Configuration
public class Swagger3Config {
@Bean
public Docket createRestApi(Environment environment) {
// 设置要显示swagger的环境
Profiles of = Profiles.of("dev", "test");
// 判断当前是否处于该环境
boolean flag = environment.acceptsProfiles(of); return new Docket(DocumentationType.OAS_30)
.apiInfo(apiInfo())
// 通过 enable() 接收决定是否要显示
.enable(flag)
// 通过.select()方法,去配置扫描接口
.select()
// RequestHandlerSelectors 配置扫描接口的具体方式
.apis(RequestHandlerSelectors.basePackage("cn.ideal.controller"))
.paths(PathSelectors.ant("/test/**"))
.build();
} private ApiInfo apiInfo() {
Contact contact = new Contact("BWH_Steven", "https://www.ideal-20.cn", "ideal_bwh@163.com");
return new ApiInfo(
"Swagger3学习——理想二旬不止", // 标题
"学习演示如何配置Swagger2", // 描述
"v1.0", // 版本
"https://www.ideal-20.cn", // 组织链接
contact, // 联系人信息
"Apache 2.0 许可", // 许可
"许可链接", // 许可连接
new ArrayList<>()// 扩展
);
}
}

看看效果

四 Swagger 测试

Swagger 不仅仅是一个普通的文档,它还可以直接在生成的文档上进行测试,伴随着文档的说明,测试起来也非常方便

例如点击 Try it out 后,因为测试一个查询方法,是没有参数的,所以直接点击 Excute,就能看到响应结果了

响应结果

五 结尾

如果文章中有什么不足,欢迎大家留言交流,感谢朋友们的支持!

如果能帮到你的话,那就来关注我吧!如果您更喜欢微信文章的阅读方式,可以关注我的公众号

在这里的我们素不相识,却都在为了自己的梦而努力

一个坚持推送原创开发技术文章的公众号:理想二旬不止

(九) SpringBoot起飞之路-整合/集成Swagger 2 And 3的更多相关文章

  1. (八) SpringBoot起飞之路-整合Shiro详细教程(MyBatis、Thymeleaf)

    兴趣的朋友可以去了解一下前几篇,你的赞就是对我最大的支持,感谢大家! (一) SpringBoot起飞之路-HelloWorld (二) SpringBoot起飞之路-入门原理分析 (三) Sprin ...

  2. (七) SpringBoot起飞之路-整合SpringSecurity(Mybatis、JDBC、内存)

    兴趣的朋友可以去了解一下前五篇,你的赞就是对我最大的支持,感谢大家! (一) SpringBoot起飞之路-HelloWorld (二) SpringBoot起飞之路-入门原理分析 (三) Sprin ...

  3. springboot (spring mvc)集成swagger

    最近用springboot构建rest接口,考虑到最方便的验证接口,想到了引入swagger. 基本的步骤大致如下: 1.pom中引入swagger依赖: <dependency> < ...

  4. springboot 学习之路 15(集成shiro)

    shiro: Apache Shiro 是 Java 的一个安全框架.功能强大,使用简单的Java安全框架,它为开发人员提供一个直观而全面的认证,授权,加密及会话管理的解决方案.   更多shiro介 ...

  5. SpringBoot系列:六、集成Swagger文档

    本篇开始介绍Api文档Swagger的集成 一.引入maven依赖 <dependency> <groupId>io.springfox</groupId> < ...

  6. 小白的springboot之路(十九)、集成swagger(com.spring4all篇)

    0-前言 集成swagger,有两种方式: 一种在前面已经介绍过了,直接集成官方的springfox-swagger2的方式,这种方式需要在配置类中配置 第二种方式是这里要介绍的方式,国人写的com. ...

  7. 小白的springboot之路(二)、集成swagger

    0-前言 现在的项目开发,基本都是前后端分离,后端专注于API接口开发,都需要编写和维护API接口文档.如果你还在用Word来编写接口文档,那你就out了,这个时候,当当当当~神兵利器swagger隆 ...

  8. springboot 集成 swagger 自动生成API文档

    Swagger是一个规范和完整的框架,用于生成.描述.调用和可视化RESTful风格的Web服务.简单来说,Swagger是一个功能强大的接口管理工具,并且提供了多种编程语言的前后端分离解决方案. S ...

  9. Springboot系列(七) 集成接口文档swagger,使用,测试

    Springboot 配置接口文档swagger 往期推荐 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 springBoot系列(三)配 ...

随机推荐

  1. SpiderMonkey教程

    https://technotales.wordpress.com/2009/06/07/spidermonkey-introduction/ https://developer.mozilla.or ...

  2. Shader 语义

    在书写HLSL shader程序时,输入和输出变量需要拥有他们 含义来表明语义.这在HLSL shader中是一个标准的做法. Vertex shader 输入语义 主顶点着色器函数(被指令 #pra ...

  3. 2020年的UWP——通过Radio类控制Cellular(1)

    最近在做UWP的项目,在2020年相信这已经是相对小众的技术了,但是在学习的过程中,发现某软这么几年仍然添加了不少的API,开放了相当多的权限.所以打算总结一下最近的一些经验和收获,介绍一下2020年 ...

  4. 1.spring boot初始化项目

    初始化spring boot项目的方式非常多,如使用Spring Tool Suite.使用IntelliJ IDEA.使用NetBeans.在start.spring.io网站中.curl命令.sp ...

  5. Zabbix value cache working in low memory mode

    Zabbix监控自身时告警"Zabbix value cache working in low memory mode",出现这个问题是因为Zabbix Server的参数Valu ...

  6. shell知识点:${} 的神奇用法

    为了完整起见,我这里再用一些例子加以说明 ${ } 的一些特异功能:假设我们定义了一个变量为:file=/dir1/dir2/dir3/my.file.txt我们可以用 ${ } 分别替换获得不同的值 ...

  7. 经典SQL问题:Top 10%

    学生表: create table hy_student( id number(4,0) primary key, name nvarchar2(20) not null, score number( ...

  8. 与C中printf并列的System.out.printf 用法(转载)

    转载地址: https://blog.csdn.net/qq_39017218/article/details/80042287 printf的格式控制的完整格式: %  -  0  m.n  l或h ...

  9. 我的T440p出现怪事情了

    装上系统后,再稍微装些软件,或是打补丁,升级驱动什么的,再重启就起不来了. 也就是说,装一次系统只能好一次,关机或是重启就启动不了了,现象是在黑屏界面转两下就转不动了. 鼓捣一个周末也无效,昨天上系统 ...

  10. 原生js实现jquery的siblings()

    Element.prototype.siblings = function(callback){ var siblingElement = []; var parentAllElement = []; ...