开篇词

该指南将引导你构建在基于 Spring Data JPA 的后端上使用基于 Vaadin UI 的应用。

你将创建的应用

我们将为简单的 JPA 存储库构建 Vaadin UI。我们将获得具有完整 CRUD(创建、读取、更新、删除)功能和使用自定义存储库方法的过滤示例的应用。

我们可以从两个不同的部分开始,既可以从已配置的 “initial” 项目开始,也可以重新开始。随后讨论它们的差异。

你将需要的工具

  • 大概 15 分钟左右;
  • 你最喜欢的文本编辑器或集成开发环境(IDE)
  • JDK 1.8 或更高版本;
  • Gradle 4+Maven 3.2+
  • 你还可以将代码直接导入到 IDE 中:
    • Spring Too Suite (STS)
    • IntelliJ IDEA
      Vaadin 还需要 NodeJS 10.x 或更高版本来生成前端资源捆绑物。我们可以用 Maven 命令在当前项目中本地安装 NodeJS:mvn com.github.eirslett:frontend-maven-plugin:1.7.6:install-node-and-npm -DnodeVersion="v10.16.3"

如何完成这个指南

像大多数的 Spring 入门指南一样,你可以从头开始并完成每个步骤,也可以绕过你已经熟悉的基本设置步骤。如论哪种方式,你最终都有可以工作的代码。

  • 要从头开始,移步至用 Gradle 来构建
  • 要跳过基础,执行以下操作:
    • 下载并解压缩该指南将用到的源代码,或借助 Git 来对其进行克隆操作:git clone https://github.com/spring-guides/gs-crud-with-vaadin.git
    • 切换至 gs-crud-with-vaadin/initial 目录;
    • 跳转至该指南的创建后端服务

待一切就绪后,可以检查一下 gs-crud-with-vaadin/complete 目录中的代码。

用 Gradle 来构建

首先,我们设置一个基本的构建脚本。在使用 Spring 构建应用时可以使用任何喜欢的构建系统,但此处包含使用 GradleMaven 所需的代码。如果你都不熟悉,请参阅使用 Gradle 构建 Java 项目使用 Maven 构建 Java 项目

创建目录结构

在我们选择的项目目录中,创建以下自目录结构;例如,在 *nix 系统上使用 mkdir -p src/main/java/hello

└── src└── main└── java└── hello

创建 Gradle 构建文件

以下是初始 Gradle 构建文件。
build.gradle

buildscript {repositories {mavenCentral()}dependencies {classpath("org.springframework.boot:spring-boot-gradle-plugin:2.1.6.RELEASE")}
}apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'bootJar {baseName = 'gs-crud-with-vaadin'version =  '0.1.0'
}repositories {mavenCentral()maven { url "https://maven.vaadin.com/vaadin-addons" }
}sourceCompatibility = 1.8
targetCompatibility = 1.8dependencies {compile("org.springframework.boot:spring-boot-starter-data-jpa")compile("com.h2database:h2")testCompile("junit:junit")
}

Spring Boot gradle 插件提供了许多方便的功能:

  • 它收集类路径上的所有 jar,并构建一个可运行的单个超级 jar,这使执行和传输服务更加方便;
  • 它搜索 public static void main() 方法并将其标记为可运行类;
  • 它提供了一个内置的依赖解析器,用于设置版本号以及匹配 Spring Boot 依赖。我们可以覆盖所需的任何版本,但默认为 Boot 选择的一组版本。

用 Maven 来构建

首先,我们搭建一个基本的构建脚本。使用 Spring 构建应用时,可以使用任何喜欢的构建系统,但是此处包含了使用 Maven 所需的弟阿玛。如果你不熟悉 Maven,请参阅使用 Maven 构建 Java 项目

创建目录结构

在我们选择的项目目录中,创建以下自目录结构;例如,在 *nix 系统上使用 mkdir -p src/main/java/hello

└── src└── main└── java└── hello

创建 Maven 构建文件

以下是初始 Maven 构建文件。
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><modelVersion>4.0.0</modelVersion><groupId>org.springframework</groupId><artifactId>gs-crud-with-vaadin</artifactId><version>0.1.0</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.6.RELEASE</version></parent><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

Spring Boot Maven 插件提供了许多方便的功能:

  • 它收集类路径上的所有 jar,并构建一个可运行的单个超级 jar,这使执行和传输服务更加方便;
  • 它搜索 public static void main() 方法并将其标记为可运行类;
  • 它提供了一个内置的依赖解析器,用于设置版本号以及匹配 Spring Boot 依赖。我们可以覆盖所需的任何版本,但默认为 Boot 选择的一组版本。

用 IDE 来构建

  • 阅读如何将该指南直接导入 Spring Tool Suite
  • 阅读如何在 IntelliJ IDEA 中使用该指南。

创建后端服务

该示例是使用 JPA 访问数据的延续。唯一的区别是,实体类具有 getter 和 setter,并且存储库中的自定义搜索方法对最终用户而言更为合适。我们不必阅读该指南即可逐步完成,但可以根据需要进行操作。

如果我们是从一个新项目开始的,那么添加以下实体和存储库对象就可以了。如果我们从 “initial” 步骤开始,则这些功能已经可供我们使用。
src/main/java/com/example/crudwithvaadin/Customer.java

package com.example.crudwithvaadin;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;@Entity
public class Customer {@Id@GeneratedValueprivate Long id;private String firstName;private String lastName;protected Customer() {}public Customer(String firstName, String lastName) {this.firstName = firstName;this.lastName = lastName;}public Long getId() {return id;}public String getFirstName() {return firstName;}public void setFirstName(String firstName) {this.firstName = firstName;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;}@Overridepublic String toString() {return String.format("Customer[id=%d, firstName='%s', lastName='%s']", id,firstName, lastName);}}

src/main/java/com/example/crudwithvaadin/CustomerRepository.java

package com.example.crudwithvaadin;import org.springframework.data.jpa.repository.JpaRepository;import java.util.List;public interface CustomerRepository extends JpaRepository<Customer, Long> {List<Customer> findByLastNameStartsWithIgnoreCase(String lastName);
}

我们可以保留基于 Spring Boot 应用的完整性,因为它将用一些示例数据填充我们的数据库。
src/main/java/com/example/crudwithvaadin/CrudWithVaadinApplication.java

package com.example.crudwithvaadin;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;@SpringBootApplication
public class CrudWithVaadinApplication {private static final Logger log = LoggerFactory.getLogger(CrudWithVaadinApplication.class);public static void main(String[] args) {SpringApplication.run(CrudWithVaadinApplication.class);}@Beanpublic CommandLineRunner loadData(CustomerRepository repository) {return (args) -> {// save a couple of customersrepository.save(new Customer("Jack", "Bauer"));repository.save(new Customer("Chloe", "O'Brian"));repository.save(new Customer("Kim", "Bauer"));repository.save(new Customer("David", "Palmer"));repository.save(new Customer("Michelle", "Dessler"));// fetch all customerslog.info("Customers found with findAll():");log.info("-------------------------------");for (Customer customer : repository.findAll()) {log.info(customer.toString());}log.info("");// fetch an individual customer by IDCustomer customer = repository.findById(1L).get();log.info("Customer found with findOne(1L):");log.info("--------------------------------");log.info(customer.toString());log.info("");// fetch customers by last namelog.info("Customer found with findByLastNameStartsWithIgnoreCase('Bauer'):");log.info("--------------------------------------------");for (Customer bauer : repository.findByLastNameStartsWithIgnoreCase("Bauer")) {log.info(bauer.toString());}log.info("");};}}

Vaadin 依赖

如果签出 “initial” 状态项目,则已经设置了所有必要的依赖项,但让我们看一下将 Vaadin 支持添加到新 Spring 项目中所需要做的工作。Vaadin Spring 集成包含一个 Spring boot starter 依赖集合,因此我们所要做的就是添加该 Maven 代码段或类似的 Gradle 配置:

<dependency><groupId>com.vaadin</groupId><artifactId>vaadin-spring-boot-starter</artifactId>
</dependency>

该示例使用的是 Vaadin 的更新版本,而不是启动程序模块带来的默认版本。要使用更新的版本,请按以下方式定义 Vaadin 材料单(BOM):

<dependencyManagement><dependencies><dependency><groupId>com.vaadin</groupId><artifactId>vaadin-bom</artifactId><version>${vaadin.version}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>

Gradle 默认情况下不支持 “BOM”,但是有一个方便的插件。查看 build.gradle 构建文件,以获取有关如何完成相同操作的示例

定义 MainView 类

MainView 类是 Vaadin UI 逻辑的切入点。在 Spring Boot 应用中,我们只需要使用 @Route 对其进行标注,它将由 Spring 自动拾取并显示在我们的 Web 应用根目录中。我们可以通过为 Route 注解提供参数来自定义显示视图的 URL。一个简单的 “hello world” 可能看起来像这样:

package com.example.crudwithvaadin;import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;@Route
public class MainView extends VerticalLayout {public MainView() {add(new Button("Click me", e -> Notification.show("Hello Spring+Vaadin user!")));}
}

将实体列入数据网格中

为了获得良好的布局,请使用 Grid 组件。可以使用 setItems 方法将来自构造函数注入的 CustomerRepository 中的实体列表简单地传递到 Grid。MainView 的主体将像这样展开:

@Route
public class MainView extends VerticalLayout {private final CustomerRepository repo;final Grid<Customer> grid;public MainView(CustomerRepository repo) {this.repo = repo;this.grid = new Grid<>(Customer.class);add(grid);listCustomers();}private void listCustomers() {grid.setItems(repo.findAll());}}

如果我们有大表且有大量并发用户,则很可能不想将整个数据集绑定到我们的 UI 组件。

尽管 Vaadin Grid 延迟将数据从服务器加载到浏览器,但上述解决方案将整个数据列表保留在服务器内存中。要节省一些内存,我们可以仅显示最上面的结果,使用分页或使用 setDataProvider(Data Provider) 方法提供延迟加载数据提供者。

过滤数据

在大型数据集成为服务器的问题之前,这将使我们的用户很难找到想要编辑的相关行。使用 TextField 组件创建顾虑条目。首先,修改 listCustomer() 方法以支持过滤:

void listCustomers(String filterText) {if (StringUtils.isEmpty(filterText)) {grid.setItems(repo.findAll());}else {grid.setItems(repo.findByLastNameStartsWithIgnoreCase(filterText));}
}

这是 Spring Data 的声明式查询非常有用的地方。编写 findByLastNameStartsWithIgnoringCaseCustomerRepository 中的单行定义。

将侦听器挂接到 TextField 组件,并将其值插入该过滤器方法。当我们在过滤器文本字段中定义 ValueChangeMode.EAGER 时,会在键入过程中自动调用 ValueChangeListener

TextField filter = new TextField();
filter.setPlaceholder("Filter by last name");
filter.setValueChangeMode(ValueChangeMode.EAGER);
filter.addValueChangeListener(e -> listCustomers(e.getValue()));
add(filter, grid);

定义编辑器组件

由于 Vaadin UI 只是纯 Java 代码,因此没有理由从一开始就不编写可重复使用的代码。为客户实体定义一个编辑器组件。我们将使它成为一个 Spring 托管的 bean,以便我们可以直接将 CustomerRepository 注入编辑器并处理 C、U 及 D 部分或我们的 CRUD 功能。
src/main/java/com/example/crudwithvaadin/CustomerEditor.java

package com.example.crudwithvaadin;import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.KeyNotifier;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.spring.annotation.UIScope;
import org.springframework.beans.factory.annotation.Autowired;/*** A simple example to introduce building forms. As your real application is probably much* more complicated than this example, you could re-use this form in multiple places. This* example component is only used in MainView.* <p>* In a real world application you'll most likely using a common super class for all your* forms - less code, better UX.*/
@SpringComponent
@UIScope
public class CustomerEditor extends VerticalLayout implements KeyNotifier {private final CustomerRepository repository;/*** The currently edited customer*/private Customer customer;/* Fields to edit properties in Customer entity */TextField firstName = new TextField("First name");TextField lastName = new TextField("Last name");/* Action buttons */// TODO why more code?Button save = new Button("Save", VaadinIcon.CHECK.create());Button cancel = new Button("Cancel");Button delete = new Button("Delete", VaadinIcon.TRASH.create());HorizontalLayout actions = new HorizontalLayout(save, cancel, delete);Binder<Customer> binder = new Binder<>(Customer.class);private ChangeHandler changeHandler;@Autowiredpublic CustomerEditor(CustomerRepository repository) {this.repository = repository;add(firstName, lastName, actions);// bind using naming conventionbinder.bindInstanceFields(this);// Configure and style componentssetSpacing(true);save.getElement().getThemeList().add("primary");delete.getElement().getThemeList().add("error");addKeyPressListener(Key.ENTER, e -> save());// wire action buttons to save, delete and resetsave.addClickListener(e -> save());delete.addClickListener(e -> delete());cancel.addClickListener(e -> editCustomer(customer));setVisible(false);}void delete() {repository.delete(customer);changeHandler.onChange();}void save() {repository.save(customer);changeHandler.onChange();}public interface ChangeHandler {void onChange();}public final void editCustomer(Customer c) {if (c == null) {setVisible(false);return;}final boolean persisted = c.getId() != null;if (persisted) {// Find fresh entity for editingcustomer = repository.findById(c.getId()).get();}else {customer = c;}cancel.setVisible(persisted);// Bind customer properties to similarly named fields// Could also use annotation or "manual binding" or programmatically// moving values from fields to entities before savingbinder.setBean(customer);setVisible(true);// Focus first name initiallyfirstName.focus();}public void setChangeHandler(ChangeHandler h) {// ChangeHandler is notified when either save or delete// is clickedchangeHandler = h;}}

在更大的应用中,我们可以在多个地方使用该编辑器组件。还要注意,在大型应用中,我们可能希望应用一些常见的模式(例如 MVP)来构造 UI 代码(不在该指南的范畴之内)。

使用编辑器

在前面步骤中,我们已经了解了基于组件编程的一些基础知识。使用 Grid 的 Button 和选择侦听器,我们可以将编辑器完全集成到主视图中。MainView 类的最终版本如下所示:
src/main/java/com/example/crudwithvaadin/MainView.java

package com.example.crudwithvaadin;import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.annotation.UIScope;
import org.springframework.util.StringUtils;@Route
public class MainView extends VerticalLayout {private final CustomerRepository repo;private final CustomerEditor editor;final Grid<Customer> grid;final TextField filter;private final Button addNewBtn;public MainView(CustomerRepository repo, CustomerEditor editor) {this.repo = repo;this.editor = editor;this.grid = new Grid<>(Customer.class);this.filter = new TextField();this.addNewBtn = new Button("New customer", VaadinIcon.PLUS.create());// build layoutHorizontalLayout actions = new HorizontalLayout(filter, addNewBtn);add(actions, grid, editor);grid.setHeight("300px");grid.setColumns("id", "firstName", "lastName");grid.getColumnByKey("id").setWidth("50px").setFlexGrow(0);filter.setPlaceholder("Filter by last name");// Hook logic to components// Replace listing with filtered content when user changes filterfilter.setValueChangeMode(ValueChangeMode.EAGER);filter.addValueChangeListener(e -> listCustomers(e.getValue()));// Connect selected Customer to editor or hide if none is selectedgrid.asSingleSelect().addValueChangeListener(e -> {editor.editCustomer(e.getValue());});// Instantiate and edit new Customer the new button is clickedaddNewBtn.addClickListener(e -> editor.editCustomer(new Customer("", "")));// Listen changes made by the editor, refresh data from backendeditor.setChangeHandler(() -> {editor.setVisible(false);listCustomers(filter.getValue());});// Initialize listinglistCustomers(null);}// tag::listCustomers[]void listCustomers(String filterText) {if (StringUtils.isEmpty(filterText)) {grid.setItems(repo.findAll());}else {grid.setItems(repo.findByLastNameStartsWithIgnoreCase(filterText));}}// end::listCustomers[]}

构建可执行 JAR

我们可以结合 Gradle 或 Maven 来从命令行运行该应用。我们还可以构建一个包含所有必须依赖项、类以及资源的可执行 JAR 文件,然后运行该文件。在整个开发生命周期中,跨环境等等情况下,构建可执行 JAR 可以轻松地将服务作为应用进行发布、版本化以及部署。

如果使用 Gradle,则可以借助 ./gradlew bootRun 来运行应用。或通过借助 ./gradlew build 来构建 JAR 文件,然后运行 JAR 文件,如下所示:

由官网没有提供任何命令,我需要这样才能运行:java -jar build/libs/crud-with-vaadin-0.0.1-SNAPSHOT.jar

如果使用 Maven,则可以借助 ./mvnw spring-boot:run 来运行该用。或可以借助 ./mvnw clean package 来构建 JAR 文件,然后运行 JAR 文件,如下所示:

由官网没有提供任何命令,我需要这样才能运行:java -jar target/crud-with-vaadin-0.0.1-SNAPSHOT.jar

我们还可以将 JAR 应用转换成 WAR 应用

我们应该看到以下输出:

2020-03-03 12:48:16.019  INFO 2187 --- [           main] c.e.c.CrudWithVaadinApplication          : Customers found with findAll():
2020-03-03 12:48:16.019  INFO 2187 --- [           main] c.e.c.CrudWithVaadinApplication          : -------------------------------
2020-03-03 12:48:16.339  INFO 2187 --- [           main] c.e.c.CrudWithVaadinApplication          : Customer[id=1, firstName='Jack', lastName='Bauer']
2020-03-03 12:48:16.339  INFO 2187 --- [           main] c.e.c.CrudWithVaadinApplication          : Customer[id=2, firstName='Chloe', lastName='O'Brian']
2020-03-03 12:48:16.339  INFO 2187 --- [           main] c.e.c.CrudWithVaadinApplication          : Customer[id=3, firstName='Kim', lastName='Bauer']
2020-03-03 12:48:16.339  INFO 2187 --- [           main] c.e.c.CrudWithVaadinApplication          : Customer[id=4, firstName='David', lastName='Palmer']
2020-03-03 12:48:16.340  INFO 2187 --- [           main] c.e.c.CrudWithVaadinApplication          : Customer[id=5, firstName='Michelle', lastName='Dessler']
2020-03-03 12:48:16.340  INFO 2187 --- [           main] c.e.c.CrudWithVaadinApplication          :
2020-03-03 12:48:16.352  INFO 2187 --- [           main] c.e.c.CrudWithVaadinApplication          : Customer found with findOne(1L):
2020-03-03 12:48:16.352  INFO 2187 --- [           main] c.e.c.CrudWithVaadinApplication          : --------------------------------
2020-03-03 12:48:16.353  INFO 2187 --- [           main] c.e.c.CrudWithVaadinApplication          : Customer[id=1, firstName='Jack', lastName='Bauer']
2020-03-03 12:48:16.353  INFO 2187 --- [           main] c.e.c.CrudWithVaadinApplication          :
2020-03-03 12:48:16.353  INFO 2187 --- [           main] c.e.c.CrudWithVaadinApplication          : Customer found with findByLastNameStartsWithIgnoreCase('Bauer'):
2020-03-03 12:48:16.353  INFO 2187 --- [           main] c.e.c.CrudWithVaadinApplication          : --------------------------------------------
2020-03-03 12:48:16.457  INFO 2187 --- [           main] c.e.c.CrudWithVaadinApplication          : Customer[id=1, firstName='Jack', lastName='Bauer']
2020-03-03 12:48:16.457  INFO 2187 --- [           main] c.e.c.CrudWithVaadinApplication          : Customer[id=3, firstName='Kim', lastName='Bauer']

打开浏览器并定位到 localhost:8080,我们将看到以下页面:

概述

恭喜你!我们已经使用 Spring Data JPA 编写了功能全面的 CRUD UI 应用,以实现持久性。而且我们无需公开任何 REST 服务或编写一行 JavaScript 或 HTML 就可以做到这一点。

参见

以下指南也可能会有所帮助:

  • 使用 Spring Boot 构建应用程序
  • 使用 JPA 访问数据
  • 使用 MongoDB 访问数据
  • 使用 GemFire 访问数据
  • 用 Neo4j 访问数据
  • 使用 MySQL 访问数据(尽请期待~)

想看指南的其他内容?请访问该指南的所属专栏:《Spring 官方指南

SpringBoot 2 使用 Vaadin 创建 CRUD UI相关推荐

  1. quartz配置_基于spring-boot 2.x +quartz 的CRUD任务管理系统

    基于spring-boot 2.x + quartz 的CRUD任务管理系统,适用于中小项目. 开发环境 JDK1.8.Maven.Eclipse 技术栈 SpringBoot 2.0.1.thyme ...

  2. Quick-cocos2d-x3.3 Study (一) --------- 创建一个UI标签

    创建一个UI标签: 1 cc.ui.UILabel.new({ 2 UILabelType = 2, text = "Hello ,World", size = 64 3 }) 4 ...

  3. 【ASP.NET Web API教程】2.3.5 用Knockout.js创建动态UI

    [ASP.NET Web API教程]2.3.5 用Knockout.js创建动态UI 原文:[ASP.NET Web API教程]2.3.5 用Knockout.js创建动态UI 注:本文是[ASP ...

  4. easyui crud java_轻松学习jQuery插件EasyUI EasyUI创建CRUD应用

    数据收集并妥善管理数据是网络应用共同的必要.CRUD 允许我们生成页面列表,并编辑数据库记录.本教程将向你演示如何使用 jQuery EasyUI 框架实现一个 CRUD DataGrid. 我们将使 ...

  5. SpringBoot 结合 Mybatis 实现创建数据库表

    SpringBoot 结合 Mybatis 实现创建数据库表 目录 博主介绍 前言 为什么要通过应用实现创建表的功能 准备创建表的 SQL 语句 实现通过 MyBatis 创建数据库表示例 在 Mav ...

  6. Springboot实战:Springboot+Netty优雅的创建websocket客户端 (附源码下载)

    Springboot-cli 开发脚手架系列 Netty系列:Springboot+Netty优雅的创建websocket客户端 (附源码下载) 文章目录 Springboot-cli 开发脚手架系列 ...

  7. 使用WPF来创建 Metro UI程序

    原文:使用WPF来创建 Metro UI程序 这个是我以前网上看到的一篇文章,原文地址是:Building a Metro UI with WPF,这篇文章一步步的介绍了如何实现一个Metro样式的窗 ...

  8. 创建Swagger UI文档的步骤

    Swagger是一个基于网络的API文档框架.它被用来为API创建交互式文档,这些API是为特定目的而建立的.与其他文档类型相比,Swagger UI文档享有许多优势. 它是开源的 使你能够创建和分享 ...

  9. springboot集成kafka及kafka web UI的使用

    springboot集成kafka application.properties spring.kafka.bootstrap-servers=CentOSA:9092,CentOSB:9092,Ce ...

最新文章

  1. Linker加载so失败问题分析
  2. python画小猪乔治_小孩挑食难搞定?试下猪肉这样炒,简单5步超下饭,比牛肉还香嫩...
  3. 转 Oracle 删除表,oracle 中删除表 drop delete truncate 的区别
  4. VTK:Filtering之VertexGlyphFilter
  5. 数据分析---ipython使用
  6. 重磅!阿里云发布业界首款SaaS化防火墙
  7. cocos2dx阴影层的实现
  8. win10配置python_win10中的Python安装与环境配置
  9. 如何将计算机桌面屏幕放大,如何放大电脑屏幕画面?这些方法你都知道吗
  10. win10系统wifi图标不见了,如何链接无线网
  11. 坚果pro官方固件_锤子坚果Pro(OD103)刷机包_线刷包_救砖包_官方ROM包_固件包下载- 线刷宝ROM中心...
  12. Chromium浏览器修改网页显示字体
  13. 使用LIME解释CNN
  14. 03_CSS字符属性
  15. 世卫组织使用的GIS软件是哪款?
  16. 单机100万连接,每秒10万次请求服务端的设计与实现(三) - 变量共享、超线程与高性能队列
  17. 用单片机c语言播放两只老虎,两只老虎的C程序的频率和音符??
  18. bootstrapt 表格自适应_BootStrap table表格插件自适应固定表头(超好用)
  19. 快递webservice接口
  20. 图谱实战 | 华农夏静波:深层语义知识图谱在药物重定位中的应用

热门文章

  1. 人生不如意之十之八九,故不论遇到什么,都要微笑着面对
  2. 大数据从业人员需要哪些技能?
  3. Html中input标签的详解
  4. 关于处理非法集资,非法中介屡禁不绝的一些建议
  5. easyui隔行变色
  6. 解决 Heroku CLI 登录问题
  7. CListCtrl详细使用方法
  8. PyTorch中加载模型权重
  9. for循环下拉框的值不能重复选择
  10. 火猴之呼吸灯(firemonkey)