一、什么是Spring?
Spring是Java开发的开源框架。 2003年6月首次发布 。Spring主要用作Enterprise Java Beans 1.0和2.0的替代品。 开发人员在Enterprise Java Bean中遇到了很多问题。 Spring是出于对EJB的挫折而创建的。
它的一些特点如下:
框架,模式和模板的集合
IoC - 使用bean,上下文和核心反转控制支持 (DI和CDI就是IoC的实现)
与流行的对象关系映射(ORM)技术集成,如Hibernate和JPA规范
内置支持持久性和事务
面向方面编程(AOP),用于处理横切关注点
用于构建Java Web应用程序的基于Web的模型 - 视图 - 控制器(MVC)
消息传递,测试 - 支持异步消息传递和单元测试
Spring Framework运行时可以分为以下几个部分:
Data access/integration
Web
AOP and Aspects(AOP为Aspect Oriented Programming的缩写,意为:面向切面编程 Spring Aspects:Spring提供的对AspectJ框架的整合)
Instrumentation
Messaging
Core container
And test support, provided as part of the framework to support test-driven development
Spring的Bean容器
EJB 2.0的替代方案
赋予POJO的权力
与EJB 3.0类似
生命周期由Spring容器管理
二、什么是Spring Boot?
Spring Boot是Spring家族的一个项目。 该项目于2013年初开始。当前版本为2.x。
Spring Boot在Java企业界非常流行,并且是基于REST的微服务Web应用程序的事实上的标准。
Spring Boot基本上是Spring框架的扩展,它消除了设置Spring应用程序所需的XML配置,为更快,更高效的开发生态系统铺平了道路。
Spring Boot中的一些特征:
创建独立的Spring应用。
嵌入式Tomcat、Jetty、 Undertow容器(无需部署war文件)。
提供的starters 简化构建配置
尽可能自动配置spring应用。
提供生产指标,例如指标、健壮检查和外部化配置
完全没有代码生成和XML配置要求
以下是经过认证和测试以与OpenShift Application Runtimes一起使用的Spring Boot启动器列表。
spring-boot-starter-parent
spring-boot-starter-web
cxf-spring-boot-starter-jaxrs
spring-boot-starter-test
spring-boot-starter-logging
spring-boot-starter-data-jpa
spring-boot-starter-data-rest
spring-boot-starter-actuator
spring-boot-starter-jdbc
spring-cloud-starter-hystrix and spring-cloud-starter-ribbon
三、什么是Spring Data?
Spring Data可以轻松使用数据访问技术,关系数据库和非关系数据库、map-reduce框架和基于云的数据服务。
Module | Description |
Spring Data Commons | Core Spring concepts underpinning every Spring Data project |
Spring Data Gemfire | Provides easy configuration and access to GemFire from Spring applications |
Spring Data JPA | Makes it easy to implement JPA-based repositories |
Spring Data KeyValue | Map-based repositories and SPIs to easily build Spring Data module for key-value stores |
Spring Data LDAP | Provides Spring Data repository support for Spring LDAP |
Spring Data MongoDB | Spring-based object-document support and repositories for MongoDB |
Spring Data REST | Exports Spring Data repositories as hypermedia-driven RESTful resources |
Spring Data Redis | Provides easy configuration and access to Redis from Spring applications |
Spring Data for Apache Cassandra | Spring Data module for Apache Cassandra |
Spring Data for Apache Solr | Spring Data module for Apache Solr |
为避免将应用程序与任何特定的数据访问策略耦合,正确编写的存储库应通过接口公开其功能。 该图显示了设计数据访问层的正确方法。
如下所示,服务对象通过接口访问存储库。 它使您的服务对象易于测试,因为它们没有耦合到特定的数据访问实现。 实际上,您可以创建这些数据访问接口的模拟实现。 这使您可以在不必连接到数据库的情况下测试服务对象,从而显着加快单元测试并排除由于数据不一致导致测试失败的可能性。
四、start.spring.io的妙用
过去,SpringFramework被批评为难以入手。最近,Spring社区通过添加SpringBoot项目和在start.spring.io上托管的Spring Initalizr服务解决了这个问题。 Spring Boot和Spring Initializr都支持特定的框架和第三方库。RedHat在launch.openshift.io上提供了类似的服务,它也支持特定的框架和目标平台(OpenShift)。
打开浏览器start.spring.io网站,然后选择或输入以下值:
Generate a: Maven Project
with: Java
and Spring Boot: 1.5.19
Group: com.redhat.coolstore
Artifact: product-catalog-lab1
In the Search for dependencies field, enter:
web
jpa
h2
填写内容如下图所示:
生成的应用程序使用具有JPA和H2支持的Spring Web堆栈。适当的依赖项包含在Maven POM文件中,生成的软件包如下。
打开终端shell并解压缩下载的项目:
编译源码:
mvn clean package
查看target目录,编译生成了jar包:
接下来,将项目导入JBoss Developer Studio
启动JBoss Developer Studio。
选择文件→导入。
在“导入”对话框中,选择“Maven”→“现有Maven项目”,然后单击“下一步”。
单击“浏览”并导航到〜/ appmod_foundations_spring_demo / spring / lab1。
在JBoss Developer Studio中,导航到项目目录并选择src / main / java。
创建一个名为com.redhat.coolstore.productcataloglab1.service的新包。
在包中创建名为ProductCatalogService的Java类。
使用@Component注释类并添加以下import语句:
添加一个名为sayHello的公共方法,它返回字符串“Hello World!”:
为ProductCatalogService创建单元测试
在本节中,您将为ProductCatalogService创建单元测试。 在JBoss Developer Studio中,导航到项目目录并选择src / test / java。 展开com.redhat.coolstore.productcataloglab1包。 打开ProductCatalogLab1ApplicationTests类。
增加@Autowired注释和ProductCatalogService添加所需的import语句:
将以下代码添加到ProductCatalogLab1ApplicationTests:
package com.redhat.coolstore.productcataloglab1;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.beans.factory.annotation.Autowired;
import com.redhat.coolstore.productcataloglab1.service.ProductCatalogService;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductCatalogLab1ApplicationTests {
@Autowired
private ProductCatalogService productCatalogService;
@Test
public void contextLoads() {
}
@Test
public void testDefaultProductList() {
String message = productCatalogService.sayHello();
assertTrue(message!=null);
assertEquals(message,"Hello World!");
}
}
验证结果是单元测试成功:
使用@Autowired注释注入组件 - 这类似于CDI在Java EE中的工作方式。
Spring 2.5 引入了 @Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Autowired的使用来消除 set ,get方法。
使用特定于环境的应用程序属性Spring支持从属性文件中读取环境值。您还可以根据环境(dev,qa,prod)读取不同的值。这在Spring中被称为配置文件。 在本节中,您将添加对读取属性文件的基本支持。 更改ProductCatalogService类并添加以下类变量
验证结果是正确的:
添加特定于配置文件的消息
在本节中,您将更新测试类以使用名为dev的配置文件来生成Hey Developer!作为消息。
向@ActiveProfiles(“dev”)添加注释到ProductCatalogLab1ApplicationTests类。
创建环境变量文件,并为cool.message赋值:
验证的结果是成功的:
使用Apache CXF添加REST功能
在本节中,您将添加REST支持并提供产品名称列表。
打开ProductCatalogService类。
删除@Component注释。
将@RestController(“/ products”)注释添加为类级注释。
编译并运行应用:
验证应用端点,默认输入是Hello World!
停止Spring Boot应用程序。 运行应用程序,但这次激活dev配置文件:
因为使用了环境变量,因此结果是Hey Devlopers!
五、如何将Spring Boot应用一键式部署到Openshift
在本实验中,将逐步完成Spring Boot项目。这使您可以深入了解如何在Red Hat OpenShift应用程序运行时上部署Spring应用程序。
Spring Boot测试和验证版本
Spring Boot运行时版本1.5.8发行版经过测试和验证,可以在OpenShift上使用嵌入式Apache Tomcat容器运行。当与Spring Boot一起使用时,此嵌入式容器以及其他组件(如Java容器映像)将作为Red Hat订阅的一部分提供。经过测试和验证的Spring Boot运行时工件可从Red Hat JBoss Middleware General Availability Maven Repository获得。
您的Maven POM文件可以使用此代码段引用Red Hat支持的位:
首先编译测试代码:
构建成功,生成war包:
查看对PostgreSQL的支持将此应用程序部署到OpenShift时,您可能更喜欢使用数据库实例而不是本地H2实例。为此,您将为OpenShift与本地引入不同的配置文件。 打开pom.xml文件。 查看配置文件部分:
在构建部分中,请注意默认构建设置为使用本地配置文件:
查看OpenShift配置文件在本节中,您将查看三个关键配置文件。运行Fabric8 Maven插件时,将处理这些文件以及应用程序的构建,以使应用程序及其数据库部署到OpenShift。查看部署配置模板OpenShift Container Platform中的部署是基于称为部署配置的用户定义模板的复制控制器。 您可以定义新模板,以便轻松地重新创建应用程序的所有对象。模板定义它创建的对象以及一些元数据,以指导这些对象的创建。
打开src / main / fabric8 / deployment.yml文件。
查看内容:
此文件定义目录服务的容器,容器生命周期的管理方式以及许多其他配置值。
请特别注意,此文件定义了通过Java系统属性spring.profiles.active激活的Spring配置文件。
查看Secrets 配置Secret对象类型提供了一种机制来保存敏感信息,如密码,OpenShift Container Platform客户端配置文件,Docker配置文件,私有源存储库凭据等。秘密从pod中分离敏感内容。您可以使用卷插件将机密信息挂载到容器中,或者系统可以使用机密代表pod执行操作。 打开src / main / fabric8 / credentials-secret.yml文件。 查看内容:
此文件定义数据库用户和密码的Secrets 。
查看路由配置OpenShift容器平台路由以主机名(如www.example.com)公开路由,以便外部客户端可以按名称访问它。 打开src / main / fabric8 / route.yml文件。 查看内容:
此文件允许OpenShift之外的使用者使用外部DNS名称,协议以及众所周知且通常不受限制的TCP端口(例如80,8080和8443)访问负载平衡服务。例如,如果要访问服务从您同事的桌面,您不能使用服务名称,您必须使用此路由的主机名。 有关路由的更多详细信息,请参见链接:OpenShift文档 - 路由。 请注意,数据库没有路由对象。 这意味着无法从OpenShift集群外部访问数据库。唯一面向外部的服务是库存服务。
部署到OpenShift
在本节中,您将应用程序部署到OpenShift服务器。
登录远程OpenShift环境。
创建一个新项目并将名称首字母添加到名称中以使其唯一:
OpenShift中的项目名称必须全部小写且唯一。
创建一个PostgreSQL database:
构建项目并将其部署到OpenShift服务器:
此数据从部署在OpenShift服务器上的产品目录REST API返回
六:通过Spring Boot构建一个购物车微服务
Spring Boot非常适合Web应用程序开发
spring-boot-starter-web starter提供了所需的依赖项
包括嵌入式HTTP服务器(Tomcat)
可以使用Spring MVC或JAX-RS开发REST API
Spring MVC:通用模型 - 视图 - 控制器Web框架
JAX-RS:标准Java EE REST API规范
在Spring MVC中,您可以创建REST应用程序。
您创建一个使用@RestController注释的控制器类。
然后定义使用@RequestMapping注释的处理程序方法。
您还可以使用@PathVariable注释的URI模板参数。
此处显示的代码示例公开了REST端点/ users / {user}。
{user}的代码是用于自定义REST请求的路径变量。
Spring Boot提供了一个Jackson ObjectMapper的自动配置,用于JSON有效负载和POJO对象之间的自动编组。
Spring在构建REST资源时,通常使用@RestController和@RequestMapping来定义。而JavaEE定义的时候,通常使用JAX-RS实现@Path。当然,在Spring中也可以使用JAX-RS。
作为开发的另一种选择,您可以使用JAX-RS。 默认的Spring Boot JAX-RS实现是Jersey,它是Glassfish Jax-RS实现。 您可以使用spring-boot-starter-jersey Spring Boot启动器。 Spring Boot提供Jersey servlet和Jackson数据绑定的自动配置。 您可以使用标准的JAX-RS注释构建REST端点,例如@ javax.ws.rs.Path,@ javax.ws.rs.Produces和javax.ws.rs.GET。此代码示例演示如何使用JAX-RS在Spring中构建REST资源。
REST资源类需要在Jersey上下文中注册。这里显示了一个例子。
Spring Boot包括对嵌入式Tomcat,Jetty和Undertow服务器的支持。
默认情况下,Spring Boot启动程序(特别是spring-boot-starter-web)使用Tomcat作为嵌入式容器。要使用备用服务器,请排除Tomcat依赖项并包含所需的依赖项。
这里的代码片段显示了如何排除Tomcat并包含Undertow服务器所需的依赖关系。
Spring Framework为使用SQL数据库提供了广泛的支持。 您可以使用JdbcTemplate直接进行JDBC访问。 您还可以将JPA对象关系映射与Hibernate一起使用。 Spring Data为支持SQL,NoSQL,MapReduce框架和基于云的数据服务的数据访问提供了基于Spring的编程模型。 Spring Boot提供以下启动器:spring-boot-starter-jdbc和spring-boot-starter-data-jpa。 Spring Boot的一个很好的功能是内存数据库的自动配置,非常适合测试。 此外,Spring Boot还提供具有外部配置属性的数据源的自动配置。
Spring Framework还提供实体类扫描,以及Spring Data Repository类的自动注册。 Spring JPA存储库是封装数据访问的接口。 JPA查询是从方法名称自动创建的。这里显示了一个例子。
Spring Boot配置可以外部化,以便相同的应用程序代码可以在不同的环境中工作。
您可以使用属性文件,YAML文件,环境变量和命令行参数来外部化配置。
可以使用@Value注释将属性值直接注入bean,通过Spring的Environment抽象访问,或通过@ConfigurationProperties绑定到结构化对象。
Spring Boot使用一个有序的序列来指定属性,以便允许合理地覆盖值。顺序如下:
@TestPropertySource
命令行参数
Java系统属性
OS环境变量
打包JAR之外的特定于配置文件的应用程序属性
打包的JAR中的特定于配置文件的应用程序属性
默认属性
使用@Value(“$ {property}”)注释来注入配置属性可能很麻烦。
Spring Boot提供了另一种选择。您可以定义强类型bean来管理和验证应用程序的配置。
下面的代码示例定义了以下属性:
foo.enabled,默认为false
foo.remote-address,具有可以从String强制转换的类型
foo.security.username,具有嵌套安全性
foo.security.roles,带有String集合
您可以像使用任何其他bean一样注入此配置。
Spring Profiles提供了一种隔离应用程序配置部分的方法,并使每个部分仅在某些环境中可用 - 例如,dev,QA或production。
任何@Component或@Configuration都可以用@Profile标记,以限制何时加载。
特定于配置文件的属性文件名为application- {profile} .properties。
当配置文件处于活动状态时,您可以覆盖application.properties中的默认属性。
您可以通过以下任何方式指定活动配置文件:使用-Dspring.profiles.active = dev作为命令行上的系统属性 作为使用导出SPRING_PROFILES_ACTIVE = dev的环境变量 在使用spring.profiles.active = dev的应用程序属性中 在使用@ActiveProfiles(“test”)的测试用例中
Spring Cloud是一个用于开发云原生应用程序的框架。Spring Cloud提供了通用设计模式的实现,以支持云本机应用程序的开发。 Spring Cloud提供的解决方案:
集中配置管理
服务注册和发现
负载均衡
断路器
异步通信
分布式跟踪
Spring Cloud还提供第三方工具和库的集成和抽象:
Netflix OSS Eureka服务注册表
HashiCorp服务登记
Netflix OSS Hystrix断路器和隔板
Netflix OSS功能区客户端负载均衡器
Apache Kafka和RabbitMQ消息代理
Zipkin分布式追踪
Spring Cloud Kubernetes提供Spring Cloud与Kubernetes和OpenShift的集成。 它由Red Hat Fabric8.io团队发起,现在由Spring Cloud Incubator托管。
功能包括以下内容:
带有ConfigMaps和secret的Spring Boot配置
当在ConfigMap中检测到更改时,PropertySource重新加载以触发应用程序重新加载
pod运行状况指示器,用于将特定于pod的运行状况数据添加到Spring Actuator运行状况端点
在Kubernetes上运行时,Kubernetes配置文件自动配置
Kubernetes的功能区发现
Archaius-a Netflix OSS配置管理库-ConfigMap属性源
透明度 - 当应用程序在Kubernetes / OpenShift之外运行时,Spring Cloud Kubernetes不会中断
在Red Hat Fuse Integration Services 2.x版中,Spring Boot是在OpenShift上开发Camel应用程序的首选框架。 启动器模块是camel-spring-boot-starter。 CamelContext的自动配置在Spring应用程序上下文中注册。 使用@Component注释的Camel路由会自动检测并注入CamelContext。
二:实验展现:构建购物车
在本实验中,您添加了为Coolstore应用程序的购物车微服务公开REST API的功能。实验室从上一个实验室的解决方案代码开始,包括您使用的其他文件。
为购物车微服务实现和公开REST API
查看并运行REST API的单元测试
应用架构
购物车微服务由一个Maven项目组成,该项目内部由许多服务对象组成:
PriceCalculationService包含用于计算购物车的运费和总价值的逻辑。
CatalogService负责调用目录服务以获取产品数据。
ShoppingCartService负责管理购物车。
CartEndpoint包含用于访问购物车微服务的REST API。
在本实验中,您将添加REST API的实现以访问CartEndpoint类中的购物车微服务。
启动Red Hat Developer Studio。 选择文件→导入。 在“导入”对话框中,选择“Maven”→“现有Maven项目”,然后单击“下一步”。 单击“浏览”并导航到〜/ appmod_springboot_experienced / lab-02。 这是您解压缩本实验的代码的目录。 确保为项目选中了/pom.xml框,然后单击Finish。 导入后,验证您是否看到该项目。
查看购物车服务项目的pom.xml文件,并注意以下事项:
在dependencyManagement部分中,导入spring-boot-dependencies物料清单(BOM)POM。此POM包含特定Spring Boot版本支持的所有依赖项的策划列表。实际上,这意味着您不必手动跟踪在构建配置中添加的依赖项的版本,因为Spring Boot正在为您管理。升级Spring Boot本身时,依赖关系也会以一致的方式升级。
在Red Hat OpenShift Application Runtimes环境中认证的Spring Boot版本是1.5.10.RELEASE。
spring-boot-maven插件用于构建可执行的JAR文件(fat JAR)。插件的重新打包目标创建了一个可自动执行的JAR(或WAR)文件。
使用Spring Boot开发一个购物车微服务使用Spring Boot,可以使用不同的技术来构建REST API。您可以使用Spring MVC框架或实现JAX-RS规范的框架。 对于购物车服务,需要以下REST端点: GET / cart / {cartId}按ID获取购物车。 POST / cart / {cartId} / {itemId} / {quantity}将商品添加到购物车。 DELETE / cart / {cartId} / {itemId} / {quantity}从购物车中删除商品。 POST / cart / checkout / {cartId}检查购物车。
我们查看源码,进行分析:
package com.redhat.coolstore.cart.rest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.redhat.coolstore.cart.service.ShoppingCartService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import com.redhat.coolstore.cart.model.ShoppingCart;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
@RestController
@RequestMapping("/cart")
//@RequestMapping批注标识资源类或类方法为其请求的URI路径。购物车服务的URI路径是/ cart。@RestController注释将此类标识为REST资源//
public class CartEndpoint {
private static final Logger LOG = LoggerFactory.getLogger(CartEndpoint.class);
//使用SLF4J API配置一个logger//
@Autowired
private ShoppingCartService shoppingCartService;
//使用Spring @Autowired注释注入ShoppingCartService//
@GetMapping("/{cartId}")
public ShoppingCart getCart(@PathVariable String cartId) {
return shoppingCartService.getShoppingCart(cartId);
}
//@GetMapping表示带注释的方法响应HTTP GET请求。URI部分附加到类级别定义的基本路径。 {cartId}表示模板参数名称。@PathVariable将URI模板参数的值绑定到方法变量。此代码将调用委托给ShoppingCartService.getShoppingCart方法。//
@PostMapping("/{cartId}/{itemId}/{quantity}")
public ShoppingCart add(@PathVariable String cartId,
@PathVariable String itemId,
@PathVariable int quantity) {
return shoppingCartService.addToCart(cartId, itemId, quantity);
}
//此方法实现REST POST / cart / {cartId} / {itemId} / {quantity}端点。//
@DeleteMapping("/{cartId}/{itemId}/{quantity}")
public ShoppingCart delete(@PathVariable String cartId,
@PathVariable String itemId,
@PathVariable int quantity) {
return shoppingCartService.removeFromCart(cartId, itemId, quantity);
}
//此方法实现DELETE / cart / {cartId} / {itemId} / {quantity}端点。//
@PostMapping("/checkout/{cartId}")
public ShoppingCart checkout(@PathVariable String cartId) {
ShoppingCart cart = shoppingCartService.checkoutShoppingCart(cartId);
LOG.info("ShoppingCart " + cart + " checked out");
return cart;
}
}
//此方法实现REST POST / cart / checkout / {cartId}端点。目前,只需记录购物车已签出的事实就足够了。//
查看并运行端到端集成测试此时,您已准备好所有部分。在本节中,您将查看并运行购物车服务的端到端测试(或集成测试)。 Spring Boot允许集成测试,而无需实际部署应用程序或连接到其他基础架构。 Spring Boot应用程序作为测试本身的一部分进行自举。所需要的只是依赖于spring-boot-starter-test启动器。 您可以使用不同的技术在集成测试中测试REST端点。 REST Assured是一种流畅而优雅的Java DSL,用于简化基于REST的服务的测试,可用于验证和验证这些服务的响应。 Rest Assured使得验证JSON或XML有效负载变得特别容易。 要模拟远程目录服务,您可以使用WireMock框架,就像在CatalogService服务的测试中一样。 Spring Boot应用程序 - 更具体地说,是CatalogService实现 - 期望将catalog.service.url系统属性设置为远程目录服务的URL。将此属性注入测试的一种方法是利用Spring配置文件和Spring Boot对特定于配置文件的属性的支持。 在项目的src / test / resources文件夹中,查看名为application-test.properties的文件:
无需为此属性设置值,因为实际的URL(特别是WireMock服务器绑定的端口)在执行测试本身之前是未知的。您将实际URL注入测试代码。 Spring Boot从类路径根目录中的application.properties和application- {profile} .properties文件加载属性,并将它们添加到Spring环境中。在这种情况下,仅在测试配置文件处于活动状态时才加载application-t\
est.properties。
选择项目的src / test / java文件夹。 查看com.redhat.coolstore.cart.rest包中的CartEndpointTest类。
此批注激活测试配置文件,因此application-test.properties文件将加载到Spring上下文中。
我们查看测试的源码:
package com.redhat.coolstore.cart.rest;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasSize;
import java.io.InputStream;
import java.nio.charset.Charset;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
//SpringRunner类提供了JUnit测试框架和Spring框架之间的集成。当使用SpringRunner,Spring应用程序上下文时 - 在Spring Boot的情况下,这是Spring Boot应用程序本身 - 在测试和启用Spring组件的依赖注入之前的bootstraps。//
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.embedded.LocalServerPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.util.ReflectionTestUtils;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import com.redhat.coolstore.cart.service.CatalogService;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//@SpringBootTest注释在常规Spring测试框架上提供了一些特定于Spring Boot的增强功能。特别是,SpringBootTest.WebEnvironment.RANDOM_PORT环境加载了一个嵌入式WebApplicationContext并提供了一个真正的servlet环境。
嵌入式servlet容器(在本例中为Tomcat)在随机端口上启动和监听。
//
public class CartEndpointTest {
@LocalServerPort
private int port;
@Rule
public WireMockRule wireMockRule = new WireMockRule(wireMockConfig().dynamicPort());
@Autowired
private CatalogService catalogService;
@Before
public void beforeTest() throws Exception {
RestAssured.baseURI = String.format("http://localhost:%d/cart", port);
ReflectionTestUtils.setField(catalogService, null, "catalogServiceUrl", "http://localhost:" + wireMockRule.port(), null);
initWireMockServer();
}
@Test
public void retrieveCartById() throws Exception {
given().get("/{cartId}", "123456")
.then()
.assertThat()
.statusCode(200)
.contentType(ContentType.JSON)
.body("id", equalTo("123456"))
.body("cartItemTotal", equalTo(0.0f))
.body("shoppingCartItemList", hasSize(0));
}
@Test
@DirtiesContext
//为了能够测试调用远程目录服务的REST端点,可以使用WireMock模拟目录服务。由于WireMock服务器绑定到随机端口(对于每个测试方法可能都是不同的端口),因此必须使用ReflectionTestUtils将实际的WireMock URL注入CatalogService实例。
使用WireMock时,会为每个测试方法实例化WireMock服务器的新实例,并绑定到不同的端口。因此,还必须为使用WireMock服务器的测试方法重新创建Spring上下文。这可以通过使用Spring @DirtiesContext注释来注释这些测试方法来完成。
//
public void addItemToCart() throws Exception {
given().post("/{cartId}/{itemId}/{quantity}", "234567", "111111", new Integer(1))
.then()
.assertThat()
.statusCode(200)
.contentType(ContentType.JSON)
.body("id", equalTo("234567"))
.body("cartItemTotal", equalTo(new Float(100.0)))
.body("shoppingCartItemList", hasSize(1))
.body("shoppingCartItemList.product.itemId", hasItems("111111"))
.body("shoppingCartItemList.price", hasItems(new Float(100.0)))
.body("shoppingCartItemList.quantity", hasItems(new Integer(1)));
}
@Test
@DirtiesContext
public void addExistingItemToCart() throws Exception {
given().post("/{cartId}/{itemId}/{quantity}", "345678", "111111", new Integer(1));
given().post("/{cartId}/{itemId}/{quantity}", "345678", "111111", new Integer(1))
.then()
.assertThat()
.statusCode(200)
.contentType(ContentType.JSON)
.body("id", equalTo("345678"))
.body("cartItemTotal", equalTo(new Float(200.0)))
.body("shoppingCartItemList", hasSize(1))
.body("shoppingCartItemList.product.itemId", hasItems("111111"))
.body("shoppingCartItemList.price", hasItems(new Float(100.0)))
.body("shoppingCartItemList.quantity", hasItems(new Integer(2)));
}
@Test
@DirtiesContext
public void addItemToCartWhenCatalogServiceThrowsError() throws Exception {
given().post("/{cartId}/{itemId}/{quantity}", "234567", "error", new Integer(1))
.then()
.assertThat()
.statusCode(500);
}
@Test
@DirtiesContext
public void removeAllInstancesOfItemFromCart() throws Exception {
given().post("/{cartId}/{itemId}/{quantity}", "456789", "111111", new Integer(2));
given().delete("/{cartId}/{itemId}/{quantity}", "456789", "111111", new Integer(2))
.then()
.assertThat()
.statusCode(200)
.contentType(ContentType.JSON)
.body("id", equalTo("456789"))
.body("cartItemTotal", equalTo(new Float(0.0)))
.body("shoppingCartItemList", hasSize(0));
}
@Test
@DirtiesContext
public void removeSomeInstancesOfItemFromCart() throws Exception {
given().post("/{cartId}/{itemId}/{quantity}", "567890", "111111", new Integer(3));
given().delete("/{cartId}/{itemId}/{quantity}", "567890", "111111", new Integer(1))
.then()
.assertThat()
.statusCode(200)
.contentType(ContentType.JSON)
.body("id", equalTo("567890"))
.body("cartItemTotal", equalTo(new Float(200.0)))
.body("shoppingCartItemList", hasSize(1))
.body("shoppingCartItemList.quantity", hasItems(new Integer(2)));
}
@Test
@DirtiesContext
public void checkoutCart() throws Exception {
given().post("/{cartId}/{itemId}/{quantity}", "678901", "111111", new Integer(3));
given().post("/checkout/{cartId}", "678901")
.then()
.assertThat()
.statusCode(200)
.contentType(ContentType.JSON)
.body("id", equalTo("678901"))
.body("cartItemTotal", equalTo(new Float(0.0)))
.body("shoppingCartItemList", hasSize(0));
}
private void initWireMockServer() throws Exception {
InputStream isresp = Thread.currentThread().getContextClassLoader().getResourceAsStream("catalog-response.json");
stubFor(get(urlEqualTo("/product/111111")).willReturn(
aResponse().withStatus(200).withHeader("Content-type", "application/json").withBody(IOUtils.toString(isresp, Charset.defaultCharset()))));
stubFor(get(urlEqualTo("/product/error")).willReturn(
aResponse().withStatus(500)));
}
}
使用Red Hat Developer Studio中的JUnit测试运行器运行测试。
或者,在命令行中使用Maven:
开始测试:
测试过程中的打印:
14项测试成功:
七、Spring Boot与K8S的结合
1.参数注入
在OpenShift上配置应用程序的推荐方法是使用ConfigMaps。使用ConfigMap可确保配置与应用程序映像分离,并使应用程序映像保持可移植性。使用ConfigMap,配置数据在运行时注入容器。
Spring Boot没有直接支持ConfigMaps。
另一种方法是使用Spring Cloud Kubernetes项目,该项目提供与Kubernetes和OpenShift的Spring Cloud集成。 Spring Cloud Kubernetes项目由Red Hat Fabric8团队启动,现已转移到Spring Cloud Incubator保护伞下。
https://github.com/spring-cloud-incubator/spring-cloud-kubernetes
Spring Cloud Kubernetes项目通过提供ConfigMapPropertySource为Kubernetes和OpenShift ConfigMaps提供支持。 ConfigMapPropertySource搜索Kubernetes ConfigMap。 ConfigMap的名称是Spring应用程序的名称,由spring.application.name属性定义。 如果找到这样的ConfigMap,则通过处理各个配置属性来处理它。通过将名为application.properties的任何属性的内容应用为属性文件来处理这些单独的属性。
2.使用Spring Cloud Kubernetes
要使用Spring Cloud Kubernetes,您需要在此处显示Maven依赖项。 groupId是org.springframework.cloud。 artifactId`是`spring-cloud-starter-kubernetes-config。
要使用Spring Cloud Kubernetes,您需要一个必需的属性文件。您的application.properties文件必须包含应用程序名称的条目,如此处所示。 然后使用spring.application.name属性(本例中为cart-service)定义的相同条目创建ConfigMap。 oc create命令用于创建ConfigMap。
部署应用程序时,了解它是否可用以及是否可以开始处理传入请求非常重要。通过实施运行状况检查模式,您可以监视应用程序的运行状况,从而检查应用程序是否可用并且能够为请求提供服务。
3.健康检查
有关健康服务检查有五个关键概念:
Liveness
Readiness
Failover
Resilience and stability
Probe
Liveness定义应用程序是否正在运行。有时正在运行的应用程序会进入无响应或停止状态,必须重新启动。检查活动有助于确定是否需要重新启动应用程序。 Readiness情况定义正在运行的应用程序是否可以服务请有时,正在运行的应用程序会进入错误或损坏的状态,在该状态下它无法再处理请求。检查准备情况有助于确定是否可以继续将请求路由到该应用程序。
故障转移可以正常处理服务请求中的失败。如果应用程序无法为请求提供服务,则该请求和将来的请求可以进行故障转移,这意味着它们将路由到另一个应用程序。这通常是同一应用程序的冗余副本。 弹性和稳定性使服务请求失败得以优雅地处理。如果应用程序由于连接丢失而无法为请求提供服务,则在弹性系统中,可以在重新建立连接后重试该请求。 探针是Kubernetes操作,可定期对正在运行的容器执行诊断。
您的应用程序可以公开REST端点以报告准备情况和活跃度。 为了准备就绪,请公开/ health / readyiness REST端点。如果应用程序已准备好处理请求,请让它返回HTTP状态代码200.如果不是,请让它返回HTTP状态代码503(如果可能)。 为了活跃,暴露/ health / liveness REST端点。如果应用程序处于活动状态,请让它返回HTTP状态代码200.如果不是,请让它返回HTTP状态代码503(如果可能)。
spring Boot公开用于管理和监视的HTTP和Java Management Extensions或JMX端点。
弹簧启动 - 启动器 - 执行器启动器启用此功能。 执行器需要Spring MVC。 以下是一些Actuator端点: autoconfig显示一个自动配置报告,显示所有自动配置候选项以及它们应用或未应用的原因。 beans显示应用程序中所有Spring bean的完整列表。 健康显示应用健康信息。 指标显示当前应用程序的“指标”信息。 当Jolokia在类路径上时,jolokia通过HTTP公开JMX bean。
从所有HealthIndicator bean收集健康状况。 Spring Boot包含许多自动配置的HealthIndicator。 默认情况下,仅返回运行状况。完整细节需要验证。 您可以为运行状况和信息端点添加自定义HealthIndicators或InfoContributors。 Actuator端点是完全可配置的,包括服务器地址,路径,端口,SSL和安全性。
Spring Boot应用程序通常打包为可执行JAR或WAR归档。 Spring Boot Maven插件的重新打包目标将在Maven的打包阶段构建的JAR存档重写为可执行的JAR。这里显示了一个例子。 Spring Boot可执行的自包含JAR文件与着色的uber-JAR不同。 还有可用于可部署WAR的替代包装。
Spring Boot JAR内部结构支持嵌套JAR。这里显示了一个例子。
使用java命令启动Spring Boot应用程序,如下所示。 您可以指定带有双连字符的环境属性命令行参数。 您可以使用Spring Boot Maven插件编译和运行应用程序。
使用java命令启动Spring Boot应用程序:
$ java -jar my-app-1.0.0.jar
环境属性命令行参数前缀为 - :
$ java -jar my-app-1.0.0.jar --server.port = 9000
使用Spring Boot Maven插件编译并运行应用程序:
$ mvn spring-boot:run
Fabric8 Maven插件是为Docker,Kubernetes和OpenShift构建和部署Java应用程序的一站式商店。它将您的Java应用程序带到Kubernetes和OpenShift。它提供了与Maven的紧密集成,并从已提供的构建配置中获益。它侧重于三个任务:
构建Docker镜像
创建OpenShift和Kubernetes资源
在Kubernetes和OpenShift上部署应用程序
Fabric8 Maven插件支持以下目标:
fabric8:resource创建Kubernetes和OpenShift资源描述符。
fabric8:build构建Docker镜像。
fabric8:push将Docker镜像推送到注册表。
fabric8:deploy将OpenShift资源对象部署到集群。
fabric8:观察重建和重启的情况。
Fabric8 Maven插件支持Kubernetes和OpenShift描述符。 它还支持使用二进制源的OpenShift Docker构建。 该插件还提供灵活的自定义。 Generators分析Maven构建并为某些系统生成自动Docker镜像配置,包括spring-boot,plain Java和Apace Karaf。 Enchers使用SCM标签等额外信息扩展Kubernetes和OpenShift资源描述符,并可以添加服务等默认对象。可以单独配置发生器和浓缩器并将其组合到配置文件中。
Fabric8 Maven插件还支持各种配置样式:
零配置:用于快速提升预先确定默认值的默认值。
内联配置:在XML语法的插件配置中。
外部配置:插件丰富的实际部署描述符的模板。
Docker Compose配置:提供Docker组合文件,并在Kubernetes / OpenShift集群上启动Docker组合部署。
资源描述符可以作为指定骨架的外部YAML文件提供。然后,这个骨架由浓缩物填充,这些浓缩物可以添加标签等。这些文件中的Maven属性将解析为其值。使用此模型,您可以使用每个Kubernetes或OpenShift资源对象的所有灵活性,但仍然可以获得添加构建信息的好处。
默认情况下,Fabric8插件从src / main / fabric8目录中读取资源片段。
fabric8:deploy是构建Docker镜像的主要目标。
此目标旨在在部署之前运行fabric8:build和fabric8:资源。
它生成OpenShift资源并将它们部署到OpenShift服务器。