回到我以前的项目之一,我被要求制作一些应急申请。 时间表紧张,范围简单。 内部编码标准是PHP,因此尝试建立经典的Java EE堆栈将是一个真正的挑战。 而且,说实话,完全过大了。 那怎么办 我趁机尝试了Spring。 我以前使用过它,但是在旧版本中,它被隐藏在此时困扰我的门户软件的技术堆栈中。
我的目标是使WebOps可以简单地放在装有Java的服务器上并运行它。 无需摆弄数十种XML配置和内存微调。 就像java -jar application.jar
一样容易。
这是“ Spring Boot”的完美呼唤。 这个Spring项目旨在简化开发人员的工作流程,并消除对配置和样板代码的负担。
我的项目渴望的另一件事是面向文档的数据存储。 我的意思是,该应用程序的主要目的是提供现实世界纸质表格的数字版本。 那么,如果我们可以将文档表示为文档,那么为什么要创建关系混乱呢? 我之前在几个小型项目中使用过MongoDB,因此我决定继续使用它。
这和这篇文章有什么关系? 好吧,我将向您展示如何快速整合Web应用程序所需的所有点点滴滴。 Spring Boot将使很多事情变得相当容易,并使代码最少。 最后,您将拥有一个JAR文件,该文件是可执行文件,只需将其拖放到服务器上即可进行部署。 您的WebOps将为此而爱您。
Productr (这就是我是软件工程师,而不是销售或市场营销的原因……)。
Productr将做令人惊奇的事情,本文将向您展示其早期阶段,这些阶段是:
- 提供简单的REST界面来查询所有可用产品
- 从MongoDB加载这些产品
- 提供生产就绪的监控设施
- 使用JavaScript UI显示所有产品
您需要开始的全部是:
- Java 8
- 马文
- 您最喜欢的IDE(IntelliJ,Eclipse,vi,edlin,butterfly…)
- 浏览器(可以,或者是Internet Explorer / MS Edge,但是谁真的想要这个?!)
对于不耐烦的人,该代码也可以在GitHub上获得 。
让我们开始吧
创建具有以下内容的pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.0.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>net.h0lg.tutorials.rapid</groupId>
<artifactId>rapid-resting</artifactId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在这几行中,已经发生了很多事情。 最重要的是已定义的父项目。 这将为我们带来许多有用且必要的依赖项,例如日志记录,Tomcat运行时等等。 由于Spring的模块化,所有内容都可以通过pom.xml或依赖项注入进行重新配置。 为了快速启动所有功能,默认设置绝对可以。 (配置公约,有人吗?)
现在,创建强制性的Maven文件夹结构:
mkdir -p src/main/java src/main/resources src/test/java src/test/resources
我们已经解决了。
启动引擎
/ api / products下可用的REST集合。 为此,我们必须做一些事情:
- 我们需要创建包含有关我们令人难以置信的产品的所有信息的“数据模型”
- 我们需要一个提供一种方法的控制器,该方法可以完成GET请求的所有必要操作
- 为我们的应用程序创建主入口点
demo.model的包和一个名为Product的类。 Product类非常简单:
package demo.model;
import java.io.Serializable;
/**
* Our very important and sophisticated data model
*/
public class Product implements Serializable {
String productId;
String name;
String vendor;
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVendor() {
return vendor;
}
public void setVendor(String vendor) {
this.vendor = vendor;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Product product = (Product) o;
if (getProductId() != null ? !getProductId().equals(product.getProductId()) : product.getProductId() != null)
return false;
if (getName() != null ? !getName().equals(product.getName()) : product.getName() != null) return false;
return !(getVendor() != null ? !getVendor().equals(product.getVendor()) : product.getVendor() != null);
}
@Override
public int hashCode() {
int result = getProductId() != null ? getProductId().hashCode() : 0;
result = 31 * result + (getName() != null ? getName().hashCode() : 0);
result = 31 * result + (getVendor() != null ? getVendor().hashCode() : 0);
return result;
}
}
equals() & hashCode()方法。
demo.controller和一个名为ProductsController的类,其内容如下:
package demo.controller;
import demo.model.Product;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* This controller provides the REST methods
*/
@RestController
@RequestMapping(value = "/", method = RequestMethod.GET)
public class ProductsController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public List getProducts() {
List products = new ArrayList();
return products;
}
}
这实际上是提供REST接口所需的一切。 好的,此刻返回了一个空列表,但是定义起来很简单。
演示中创建一个名为Productr的类,然后为其提供以下内容:
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* This is the entry point of our application
*/
@SpringBootApplication
public class ProductrApplication {
public static void main (String... opts) {
SpringApplication.run(ProductrApplication.class, opts);
}
}
无论如何,@SpringBootApplication会完成我们每个Web应用程序所需的一些操作。 该注释是以下注释的简写:
- @组态
- @EnableAutoConfiguration
- @ComponentScan
现在是时候第一次启动我们的应用程序了。 借助我们在pom.xml中配置的Spring Boot的maven插件,启动该应用程序非常简单: mvn spring-boot:run
。 只需在项目根目录中运行此命令即可。 您更喜欢IDE提供的惰性N键单击方式? 好的,只需指示您喜欢的IDE运行ProductrApplication即可
一旦启动,请使用浏览器,REST客户端(您应该签出Postman ,我喜欢这个工具)或诸如curl的命令行工具。 您要查找的地址是: http:// localhost:8080 / api / products / 。 因此,使用curl
curl http://localhost:8080/api/products/
请提供资料
好的,返回一个空列表不是很闪亮,是吗? 因此,让我们引入数据。 在许多项目中,经典的关系数据库通常会显得过大(如果必须使用它并进行横向扩展,则会很痛苦)。 这可能是NoSQL数据库被大肆宣传的原因之一。 一个(在我看来很好)的例子是MongoDB。
启动和运行MongoDB非常简单。 在Linux上,您可以使用软件包管理器进行安装。 例如,对于Debian / Ubuntu,只需执行: sudo apt-get install mongodb
。
自制软件 : brew install mongodb
并按照“注意事项”部分中的说明进行操作。
Windows用户应使用MongoDB安装程序(和toi toi toi)。
好吧,我们只是对数据存储进行了排序。 现在是时候使用它了。 有一个特定的Spring项目处理数据–称为Spring Data 。 碰巧的是,一个名为Spring Data MongoDB的子项目正等着我们。 甚至更多,Spring Boot提供了一个依赖包,可以立即加快速度。 难怪pom.xml的<dependencies>
部分中的以下几行足以引入我们需要的所有内容:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
demo.domain的新包,并放入一个名为ProductRepository的新接口。 Spring提供了一种非常简洁的方法来摆脱编写与数据源交互通常所需的代码。 大多数基本查询都是由Spring Data生成的-您只需要定义一个接口即可。 甚至没有指定方法标题就可以使用几个查询方法。 一个示例是findAll()
方法,该方法将返回集合中的所有条目。
但是,让我们看看它在起作用,而不是在谈论它。 定制的ProductRepository接口应如下所示:
package demo.domain;
import demo.model.Product;
import org.springframework.data.mongodb.repository.MongoRepository;
/**
* This interface lets Spring generate a whole Repository implementation for
* Products.
*/
public interface ProductRepository extends MongoRepository {
}
ProductService的类。 此类的目的是实际提供一些用于查询产品的有用方法。 现在,代码就像这样简单:
package demo.domain;
import demo.model.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* This is a little service class we will let Spring inject later.
*/
@Service
public class ProductService {
@Autowired
private ProductRepository repository;
public List getProducts() {
return repository.findAll();
}
}
看看我们如何在没有在接口中定义的情况下使用repository.findAll()
? 很漂亮,不是吗? 尤其是在您急于需要快速起床的情况下。
好了,到目前为止,我们已经为数据访问奠定了基础。 我认为是时候将它们连接在一起了。 为此,只需回到我们的类demo.controller.ProductsController
并稍加修改即可。 我们要做的就是注入我们闪亮的新ProductService服务并调用其getProducts()
方法。 之后,该类将如下所示:
package demo.controller;
import demo.domain.ProductService;
import demo.model.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* This controller provides the REST methods
*/
@RestController
@RequestMapping("/api/products/")
public class ProductsController {
// Let Spring DI inject the service for us
@Autowired
private ProductService productService;
@RequestMapping(value = "/", method = RequestMethod.GET)
public List getProducts() {
// Ask the data store for a list of products
return productService.getProducts();
}
}
而已。 启动MongoDB(如果尚未运行),再次启动我们的应用程序(还记得mvn spring-boot:run
whaty ?!),并向http:// localhost:8080 / api / products /发起另一个GET请求:
$ curl http://localhost:8080/api/products/
[]
等等,还是一个空名单? 是的,或者您还记得我们将任何内容放入数据库吗? 让我们使用以下命令来更改它:
mongo localhost/test --eval "db.product.insert({productId: 'a1234', name: 'Our First Product', vendor: 'ACME'})"
这会将一个名为“我们的第一个产品”的产品添加到我们的数据库中。 好的,我们的服务现在返回什么? 这个:
$ curl http://localhost:8080/api/products/
[{"productId":"5657654426ed9d921affc3c0","name":"Our First Product","vendor":"ACME"}]
很简单,不是吗?
寻找更多数据却没有时间自己创建? 好了,快到圣诞节了,所以请选择我的一些小测试:
curl https://gist.githubusercontent.com/daincredibleholg/f8667a26ce2f17776903/raw/ed9b4c8ec6c9c455dc063e833af2418648928ba6/quick-web-app-product-example.json | mongoimport -d test -c product --jsonArray
基本要求唾手可得
在当今繁忙的日子里,随着“微服务”文化的传播,关注服务器或云环境上实际运行的内容变得越来越困难。 因此,在过去几年中,我在几乎所有环境中进行监控都是一件大事。 一种常见的模式是提供运行状况检查端点。 从简单的ping端点到运行状况指标,您可以找到所有内容,并返回与业务相关的指标的详细概述。 所有这些在大多数情况下都是“复制粘贴”冒险,涉及处理许多样板代码。 这是我们要做的–只需将以下依赖项添加到pom.xml中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
并重新启动服务。 让我们看看如果查询http:// localhost:8080 / health会发生什么:
$ curl http://localhost:8080/health
{"status":"UP","diskSpace":{"status":"UP","total":499088621568,"free":83261571072,"threshold":10485760},"mongo":{"status":"UP","version":"3.0.7"}}
这应该为基本的健康检查提供足够的数据。 如果遵循启动日志消息,则可能会发现许多其他端点。 尝试一下,然后查看执行器文档以获取更多信息。
给我看看
好的,我们获得了REST服务和一些数据。 但是我们想将这些数据显示给我们的用户。 因此,让我们继续并提供一个页面,其中概述了我们的出色产品。
感谢圣诞老人,这里有一个非常活跃的Web UI社区,致力于处理漂亮,易用的前端框架和库。 一个很受欢迎的例子是Bootstrap 。 它易于使用,并且所有所需的点点滴滴都通过开放的CDN提供。
我们希望对我们的产品有一个简短的概述,所以表格视图会很不错。 Bootstrap Table将帮助我们实现这一目标。 它建立在Bootstrap之上,也可以通过CDN使用。 我们生活在一个怎样的世界里……
src / main / resources / static的文件夹,并创建一个具有以下内容的名为index.html的新HTML文件:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Productr</title>
<!-- Import Bootstrap CSS from CDNs -->
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.9.1/bootstrap-table.min.css">
</head>
<body>
<nav class="navbar navbar-inverse">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Productr</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<div class="container">
<table data-toggle="table" data-url="/api/products/">
<thead>
<tr>
<th data-field="productId">Product Reference</th>
<th data-field="name">Name</th>
<th data-field="vendor">Vendor</th>
</tr>
</thead>
</table>
</div>
<!-- Import Bootstrap, Bootstrap Table and JQuery JS from CDNs -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.9.1/bootstrap-table.min.js"></script>
</body>
</html>
这个文件不是很复杂。 它只是一个HTML文件,其中包括CDN中最小化CSS文件。 如果您第一次看到类似//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css
之类的引用,那么协议(http或https)丢失就是一个不错的错误。 以这种方式引用的资源将通过与加载主页相同的协议进行加载。 说,如果您使用http:// localhost:8080 / ,它将使用http:加载CSS文件。
<body>块包含一个导航栏(使用HTML5 <nav>标记)和一个表。 该表定义的有趣部分是提供的data-url属性。 Bootstrap表将其解释为加载数据。 我们的定义指向我们先前创建的REST端点。 通过<th>定义上的data-field属性定义在哪一列中使用JSON对象的哪一部分。 您可以找到匹配的属性名称吗?
最后但并非最不重要的一点是,我们加载了所需JavaScript库。 所有与Bootstrap相关JavaScript功能都需要JQuery,因此这是第一个要加载的库。 紧随其后的是主要的Bootstrap和Bootstrap Table JavaScript文件。 这些库文件中的每个文件均以最小化版本加载,以使下载时间保持最少。
现在去哪里
可以说我们现在有一个非常简单的Web应用程序。 好吧,本文的主要目的是向您展示如何以尽可能少的代码来加快速度。 您已经看到,有时POM文件中的依赖项为您带来了一项全新的功能,而无需任何其他代码。 退后一步,看看到目前为止我们已经建立了什么,然后考虑下一步需要做的事情。 并开始在Spring宇宙中四处看看。
我认为,除了添加缺少的测试之外,接下来需要的最关键的步骤之一就是引入安全性。 查看Spring Security及其子项目Spring Security OAuth 。 对“经典”网页更感兴趣? 查看Spring MVC以及集成非常复杂的模板引擎有多么容易(例如,通过遵循本指南 )。
希望您喜欢这篇文章,也喜欢我喜欢它的创作。 祝大家圣诞快乐,如果一个或另一个想要与您取得联系,您可以在Twitter , G +和LinkedIn上找到我。
翻译自: https://www.javacodegeeks.com/2015/12/quick-web-app-prototyping-spring-boot-mongodb.html