3.3 将Spring Cloud Config与Spring Boot客户端集成

 
  在上一章中,我们构建了一个简单的许可证服务框架,这个框架只是返回一个代表数据库中单个许可记录的硬编码Java对象。在下一个示例中,我们将构建许可证服务,并与持有许可数据的Postgres数据库进行交流。
    我们将使用Spring Data与数据库进行通信,并将数据从许可证表映射到保存数据的POJO。数据库连接和一条简单的属性将从Spring Cloud配置服务器中读出。图3-6展示了许可证服务和Spring Cloud配置服务之间的交互。
 
图3-6 使用开发环境profile检索配置信息
 
当许可证服务首次启动时,将通过命令行传递两条信息:Spring的profile和许可证服务用于与Spring Cloud配置服务通信的端点。Spring的profile值映射到为Spring服务检索属性的环境。当许可证服务首次启动时,它将通过从Spring的profile传入构建的端点与Spring Cloud Config服务进行联系。然后,Spring Cloud Config服务将会根据URI上传递过来的特定Spring profile,使用已配置的后端配置存储库(文件系统、Git、Consul或Eureka)来检索相应的配置信息,然后将适当的属性值传回许可证服务。接着,Spring Boot框架将这些值注入应用程序的相应部分。
    3.3.1 建立许可证服务对Spring Cloud Config服务器的依赖
    让我们把焦点从配置服务器转移到许可证服务。我们需要做的第一件事,就是在许可证服务中为Maven文件添加更多的条目。代码清单3-4展示了需要添加的条目。
 
代码清单3-4 许可证服务所需的其他Maven依赖项
<dependency>   
<groupId>org.springframework.boot</groupId>   
<artifactId>spring-boot-starter-data-jpa</artifactId>    ⇽---  告诉Spring Boot将要在服务中使用Java Persistence API(JPA) </dependency> 
<dependency>   
<groupId>postgresql</groupId>   
<artifactId>postgresql</artifactId>    ⇽---  告诉Spring Boot拉取Postgres JDBC驱动程序   
<version>9.1-901.jdbc4</version> 
</dependency> 
<dependency>   
<groupId>org.springframework.cloud</groupId>   
<artifactId>spring-cloud-config-client</artifactId>    ⇽---  告诉Spring Boot拉取Spring Cloud Config客户端所需的所有依赖项 </dependency>
    第一个和第二个依赖项spring-boot-starter-data-jpa和postgresql导入了Spring Data Java Persistence API(JPA)和Postgres JDBC驱动程序。最后一个依赖项是spring-cloud-config-client,它包含与Spring Cloud配置服务器交互所需的所有类。
 
    3.3.2 配置许可证服务以使用Spring Cloud Config
    在定义了Maven依赖项后,需要告知许可证服务在哪里与Spring Cloud配置服务器进行联系。在使用Spring Cloud Config的Spring Boot服务中,配置信息可以在bootstrap.yml和application.yml这两个配置文件之一中设置。
    在其他所有配置信息被使用之前,bootstrap.yml文件要先读取应用程序属性。一般来说,bootstrap.yml文件包含服务的应用程序名称、应用程序profile和连接到Spring Cloud Config服务器的URI。希望保留在本地服务(而不是存储在Spring Cloud Config中)的其他配置信息,都可以在服务中的application.yml文件中进行本地设置。通常情况下,即使Spring Cloud Config服务不可用,我们也会希望存储在application.yml 文件中的配置数据可用。bootstrap.yml 和application.yml保存在项目的src/main/resources文件夹中。
    要使许可证服务与Spring Cloud Config服务进行通信,需要添加一个licensing-service/src/ main/resources/bootstrap.yml文件,并设置3个属性,即spring.application.name、spring. profiles.active和spring.cloud.config.uri。
    代码清单3-5展示了许可证服务的bootstrap.yml文件。
 
    代码清单3-5 配置许可证服务的bootstrap.yml文件
    spring:   
        application:     
            name: licensingservice    ⇽---  指定许可证服务的名称,以便Spring Cloud Config客户端知道正在查找哪个服务   
         profiles:     
            active:       
                default    ⇽---  指定服务应该运行的默认profile。profile映射到环境   
        cloud:   
            config:         
                uri: http://localhost:8888    ⇽---  指定Spring Cloud Config服务器的位置
    注意
    
    Spring Boot应用程序支持两种定义属性的机制:YAML(Yet another Markup Language)和使用“.”分隔的属性名称。我们选择YAML作为配置应用程序的方法。YAML属性值的分层格式直接映射到spring.application.name、spring.profiles.active和spring.cloud.config.uri名称。
 
spring.application.name是应用程序的名称(如licensingservice)并且必须直接映射到Spring Cloud配置服务器中的目录的名称。对于许可证服务,需要在Spring Cloud配置服务器上有一个名为licensingservice的目录。
    第二个属性spring.profiles.active用于告诉Spring Boot应用程序应该运行哪个profile。profile是区分Spring Boot应用程序要使用哪个配置数据的机制。对于许可证服务的profile,我们将支持服务的环境直接映射到云配置环境中。例如,通过作为profile传入开发环境,Spring Cloud配置服务器将使用开发环境的属性。如果没有设置profile,许可证服务将使用默认profile。
 
第三个也是最后一个属性spring.cloud.config.uri是许可证服务查找Spring Cloud配置服务器端点的位置。在默认情况下,许可证服务将在http://localhost:8888上查找配置服务器。在本章的后面,读者将看到如何在应用程序启动时覆盖boostrap.yml和application.yml文件中定义的不同属性,这样可以告知许可证微服务应该运行哪个环境。
    现在,如果启动Spring Cloud配置服务,并在本地计算机上运行相应的Postgres数据库,那么就可以使用默认profile启动许可证服务。这可以通过切换到许可证服务的目录并执行以下命令来完成:
 
mvn spring-boot: run
    通过运行此命令而不设置任何属性,许可证服务器将自动尝试使用端点(http://localhost: 8888)和在许可证服务的bootstrap.yml文件中定义的活跃profile(默认),连接到Spring Cloud配置服务器。
    如果要覆盖这些默认值并指向另一个环境,可以通过将许可证服务项目编译到JAR,然后使用-D系统属性来运行这个JAR来实现。下面的命令行演示了如何使用非默认profile启动许可证服务:
    java  -Dspring.cloud.config.uri=http://localhost:8888        
            -Dspring.profiles.active=dev        
            -jar target/licensing-service-0.0.1-SNAPSHOT.jar 
使用上述命令行将覆盖两个参数,即spring.cloud.config.uri和spring.profiles. active。
使用-Dspring.cloud.config.uri=http://localhost:8888系统属性将指向一个本地运行的配置服务器。
    注意
    
    如果读者尝试从自己的台式机上使用上述的Java命令来运行从本章的GitHub存储库下载的许可证服务,将会运行失败,这是因为没有运行桌面Postgres服务器,并且GitHub存储库中的源代码在配置服务器上使用了加密。本章稍后将介绍加密。前面的例子演示了如何通过命令行来覆盖Spring属性。
    使用-Dspring.profiles.active=dev系统属性,可以告诉许可证服务使用开发环境profile(从配置服务器读取),从而连接到开发环境的数据库的实例。
 
    使用环境变量传递启动信息
    在这些示例中,将这些值硬编码传递给-D参数值。在云中所需的大部分应用程序配置数据都将位于配置服务器中。但是,对于启动服务所需的信息(如配置服务器的数据),则需要启动VM实例或Docker容器并传入环境变量。
    本书每章的所有代码示例都可以在Docker容器中完全运行。使用Docker,我们可以通过特定环境的Docker-compose文件来模拟不同的环境,从而协调所有服务的启动。容器所需的特定环境值作为环境变量传递到容器。例如,要在开发环境中启动许可证服务,docker/dev/docker-compose.yml文件要包含以下用于许可证服务的条目:
 
licensingservice:   
image: ch3-thoughtmechanix/licensing-service   
ports:     - "8080:8080"   
environment:     ⇽---  指定许可证服务容器的环境变量的开始     
PROFILE: "dev"    ⇽---  PROFILE环境变量被传递给Spring Boot服务命令行,告诉Spring Boot应该运行哪个profile     
CONFIGSERVER_URI: http://configserver:8888    ⇽---  配置服务的端点     
CONFIGSERVER_PORT: "8888"    
DATABASESERVER_PORT: "5432"
    该文件中的环境条目包含两个变量PROFILE的值,这是许可证服务将要运行的Spring Boot profile。
CONFIGSERVER_URI被传递给许可证服务,该属性定义了Spring Cloud配置服务器实例
的地址,服务将从该URI读取其配置数据的。
    在由容器运行的启动脚本中,我们将这些环境变量以-D参数传递到启动应用程序的JVM。
在每个项目中,可以制作一个Docker容器,然后该Docker容器使用启动脚本启动该容器中的软件。
对于许可证服务,容器中的启动脚本位于licensing-service/src/main/docker/run.sh中。
在run.sh脚本中,以下条目负责启动许可证服务的JVM:
echo  "********************************************************" 
echo  "Starting License Server with   $CONFIGSERVER_URI"; 
echo  "********************************************************" 
java     -Dspring.cloud.config.uri=$CONFIGSERVER_URI  
            -Dspring.profiles.active=$PROFILE 
            -jar /usr/local/licensingservice/licensing-service-0.0.1-SNAPSHOT.jar 
因为我们是通过 Spring Boot Actuator 来增强服务的自我检查能力的,所以可以通过访问http://localhost:8080/env来确认正在运行的环境。/env端点将提供有关服务的配置信息的完整列表,包括服务启动的属性和端点,如图3-7所示。
 
图3-7 可以通过调用/env端点来检查许可证服务加载的配置
 
图3-7中要注意的关键是,许可证服务的活跃profile是dev。通过观察返回的JSON,还可以看到被返回的Postgres数据库URI是开发环境URI:jdbc:postgresgl://database: 5432/eagle-ege-dev。
    暴露太多的信息
围绕如何为服务实现安全性,每个组织都会有自己的规则。许多组织认为,服务不应该广播任何有关自己的信息,也不允许像/env端点这样的东西在服务上存在,因为他们相信(这是理所当然的)这样会为潜在的黑客提供太多的信息。Spring Boot为配置Spring Actuator端点返回的信息提供了丰富的功能,这些知识超出了本书的范围。Craig Walls的优秀著作《Spring Boot实战》详细介绍了这个主题,我强烈建议读者回顾一下企业安全策略并阅读
 
Walls的书,以便能够提供想通过Spring Actuator公开的正确级别的细节。
    3.3.3 使用Spring Cloud配置服务器连接数据源
    至此我们已将数据库配置信息直接注入微服务中。数据库配置设置完毕后,配置许可证微服务就变成使用标准Spring组件来构建和从Postgres数据库中检索数据的练习。许可证服务已被重构成不同的类,每个类都有各自独立的职责。这些类如表3-2所示。
    表3-2 许可证服务的类及其所在位置
 
类名
 
位置
 
License
 
licensing-service/src/main/java/com/thoughtmechanix/licenses/model
 
LicenseRepository
 
licensing-service/src/main/java/com/thoughtmechanix/licenses/repository
 
LicenseService
 
licensing-service/src/main/java/com/thoughtmechanix/licenses/services
 
License类是模型类,它将持有从许可数据库检索的数据。代码清单3-6展示了License类的代码。
    代码清单3-6 单个许可证记录的JPA模型代码
package com.thoughtmechanix.licenses.model; 
import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.Id; 
import javax.persistence.Table; 
@Entity    ⇽---  @Entity注解告诉Spring这是一个JPA类 
@Table(name = "licenses")    ⇽---  @Table映射到数据库的表 
public class License{     
@Id    ⇽---  @Id将该字段标记为主键     
@Column(name = "license_id", nullable = false)     ⇽---  @Column 将该字段映射到特定数据库表中的列     
private String licenseId;     
@Column(name = "organization_id", nullable = false)     
private String organizationId;     
@Column(name = "product_name", nullable = false)     
private String productName;     /*为了简洁,省略了其余的代码*/ }
    这个类使用了多个Java持久性注解(Java Persistence Annotations,JPA),
帮助Spring Data框架将Postgres数据库中的licenses表中的数据映射到Java对象。
@Entity注解让Spring知道这个Java POJO将要映射保存数据的对象。
@Table注解告诉Spring JPA应该映射哪个数据库表。
@Id注解标识数据库的主键。最后,数据库中的每一列将被映射到由@Column标记的各个属性。
    Spring Data和JPA框架提供访问数据库的基本CRUD方法。如果要构建其他方法,
可以使用Spring Data存储库接口和基本命名约定来进行构建。Spring将在启动时从Repository接口解析方法的名称,并将它们转换为基于名称的SQL语句,然后在幕后生成一个动态代理类来完成这项工作。
代码清单3-7展示了许可证服务的存储库。
   
 代码清单3-7 LicenseRepository接口定义查询方法
    package com.thoughtmechanix.licenses.repository; 
import com.thoughtmechanix.licenses.model.License; 
import org.springframework.data.repository.CrudRepository; 
import org.springframework.stereotype.Repository; 
import java.util.List; 
@Repository    ⇽---  告诉Spring Boot这是一个JPA存储库类 
public interface LicenseRepository     extends CrudRepository<License,String>    ⇽---  定义正在扩展Spring CrudRepository 
{     
public List<License> findByOrganizationId(String organizationId);     ⇽---  每个查询方法被Spring解析为SELECT...FROM查询     
public License findByOrganizationId(String organizationId,String licenseId); 
}
存储库接口LicenseRepository用@Repository注解标记,这个注解告诉Spring应该将这个接口视为存储库并为它生成动态代理。
Spring提供不同类型的数据访问存储库。我们选择使用Spring CrudRepository基类来扩展LicenseRepository类。
CrudRepository基类包含基本的CRUD方法。除了从CrudRepository扩展的CRUD方法外,
我们还添加了两个用于从许可表中检索数据的自定义查询方法。
Spring Data框架将拆开这些方法的名称以构建访问底层数据的查询。
 
注意
    
Spring Data框架提供各种数据库平台上的抽象层,并不仅限于关系数据库。该框架还支持NoSQL数据库,如MongoDB和Cassandra。
与第2章中的许可证服务不同,我们现在已将许可证服务的业务逻辑和数据访问逻辑从LicenseController中分离出来,
并划分在名为LicenseService的独立服务类中(如代码清单3-8所示)。
    
代码清单3-8 用于执行数据库命令的LicenseService类
 
package com.thoughtmechanix.licenses.services; 
import com.thoughtmechanix.licenses.config.ServiceConfig; 
import com.thoughtmechanix.licenses.model.License; 
import com.thoughtmechanix.licenses.repository.LicenseRepository; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Service; 
import java.util.List; 
import java.util.UUID; 
@Service 
public class LicenseService {     
@Autowired     
private LicenseRepository licenseRepository;     
@Autowired     ServiceConfig config;     
public License getLicense(String organizationId,String licenseId) {
License license = licenseRepository.findByOrganizationIdAndLicenseId(organizationId, licenseId);         
return license.withComment(config.getExampleProperty());     
}
 
public List<License> getLicensesByOrg(String organizationId){         
return licenseRepository.findByOrganizationId(organizationId);     
}     
public void saveLicense(License license){         
license.withId( UUID.randomUUID().toString());         
licenseRepository.save(license);     
}        /*为了简洁,省略了其余的代码*/ 
}
使用标准的Spring @Autowired注解将控制器、服务和存储库类连接到一起。
 
 
 
 
原文地址:https://www.cnblogs.com/mongotea/p/11973208.html