单页面应用:
良好的开发流程:
一个完整的Spring应用:
微服务是可选的并且完全支持服务端技术栈,除此之外还支持:
Jhipster提供了6中方式以便使用JHipster,主要有:
官方推荐选择第二种安装方式,即:使用Yarn进行本地安装。
yarn config set registry https://registry.npm.taobao.org
)yarn global add yo
yarn global add generator-jhipster
yarn global add bower
yarn global add gulp-cli
yarn global add yo
yarn global add generator-jhipster
M2_HOME=/maven-home
如果使用Yarn有问题,请检查$HOME/.config/yarn/global/node_modules/.bin
有没有此路径。 在Mac或者Linux下,需要
export PATH="$PATH:`yarn global bin`:$HOME/.config/yarn/global/node_modules/.bin"
jhipster使用Yeoman进行代码生成,需要查看更多信息、提示或者帮助,请移步 the Yeoman “getting starting” guide
开始之前为你的应用创建一个空文件夹:mkdir myapplication
进入到此文件夹中 cd myapplication
生成你的应用,执行jhipster
回答你看到的问题,这些问题用于定制你的应用,稍后详细解释每个问题。
当一个应用生成好之后,你可以使用./mvnw
或者./mvnw.bat
来启动,(也可以使用yarn && mvn spring-boot:run
来启动)
应用将在http://localhost:8080可用
你选择的问题答案不同可能会影响到接下来的问题显示,如:你不想使用hibernate的缓存那么就不能选择sql数据库。
你期望的应用的名称
应用使用的默认包名,使用Yeoman的时候此值会被存储,当下次使用的时候此值会成为默认值,可覆写此值
JHipster Registry是一个开源的工具,用于管理你正在运行的应用(微服务注册中心和统一配置中心),只有在微服务架构时才会使用
所有可能的答案:
你可以选择的选项:
选择你线上环境使用的数据库,此选项决定src/main/resources/config/application-prod.yml
的配置
此选项决定你src/main/resources/config/application-dev.yml
profile的数据库配置项,你可以选择:
由于Spring对于Cache的允许用户使用不同的cache实现,你可以使用chcache(本地缓存),Hazelcast(分布式缓存)或者Infinispan(另一种分布式缓存),此选项可以提升你的应用的性能
此选项仅当你选择SQL数据库并且在Q9选择了一个缓存实现。Hibernate使用二级缓存可以更好的提升它的性能
构建此项目时将要使用的工具,Maven
或者Gradle
多选,你可以为你的应用添加多种技术,如:
启用Websockets支持,将使用Spring WebSocket,JHipster提供了简单的例子展示如和高效的使用
是否使用Kafka来发布和订阅消息
选择使用那种客户端技术:
Node-sass对于设计CSS是一个优秀的解决方案,便于高效使用,你需要运行一个Gulp服务,jhipster会自动配置
JHipster对于国际支持非常友好,你可以在客户端和服务端使用。但一般对于国际化要求不多的场景,可以不选择。
默认Jhipster提供了Java单元/集成测试(spring`s Junit)和JavaScript单元测试(Karma.js),你也可以选择:
是否需要去JHipster商城安装第三方插件模块
你可以使用JHipster的一些可选命令行选项,使用jhipster app --help
查看详细说明。
--skip-cache
忽略之前记住的答案--skip-git
不自动生成git项目--skip-install
不自动安装依赖--skip-client
跳过客户端的生成,只生成后台服务,也可以使用jhipster server
代替--skip-client
跳过生成服务端代码,只生成客户端代码jhipster clinet
代替--skip-user-management
跳过生成用户管理代码,包含前端和后端--i18n
当跳过客户端代码生成时禁用或启用i18n,否则没应用--auth
当跳过服务端代码生成时指定认证类型--db
当跳过服务端代码生成时指定数据库--with-entities
如果已经生成了实体则重新生成一次--skip-checks
跳过需要工具的检查--jhi-prefix
添加服务、组件或路由的前缀,默认jhi
--npm
使用npm代替Yarn--eperimental
启用实验特性,请注意这些特性可能不稳定--force
强制覆盖以存在的文件当你刚创建完应用时,你可能想要创建一些实体类。例如你也许想要创建一个Book和Author实体类。对于每个实体,你需要:
如果你有多个实体,你可能还想创建他们之间的关系,比如这个例子,你需要:
实体sub-generator(子生成器)将会为每个实体创建所有需要的文件并且提供增删改查后端,sub-generator通过jhipster entity
运行
它支持的选项有:
--table-name
通过JHipster会生成一个表,他的名称基于你的实体名称,如果你想要修改为不同的名称使用此选项--angular-suffix
如果你想所有的都带有自定义的后缀,可使用此选项--regenerate
将会不做任何询问生成已存在的实体--skip-server
不会服务端代码--skip-client
不会生成客户端代码--db
跳过服务端代码生成时指定数据库此章节描述如何使用JHipster的标准命令行接口创建实体类。如果你想创建多个实体,你也许要使用一些图形工具。
如果你使用JDL Studio:
你已使用import-jdl
子生成器从JDL 文件生成实体,jhipster import-jdl your-jdl-file.jh
--json-only
标志,跳过实体生成仅创建json文件于.jhipster
文件夹中import-jdl
仅会重新生成被改变的实体,如果你想所有的实体被重新生成,则通过--force
生成如果你使用JhipsterUML代替import-jdl
子生成器,你需要安装npm install -g jhipster-uml
然后运行jhipster-uml youFileName.jh
对于每个实体,你可以添加很多字段。你需要输入字段名称和类型,JHipster将会生成你所需的代码和配置。这些字段名称不能包含关键字
JHipster支持很多字段类型,这些支持依赖于你的后台数据库,所有我们使用Java类型去描述他们:一个Java String
在Oracle和Cassandra中不同,这是Jhipster生成健壮和正确的数据库代码的一种方式。
String
它的默认长度取决于后端,如果你使用JPA默认长度255
,你可以通过检验规则来修改它Integer
Long
Float
Double
BigDecimal
LocalDate
用于在Java中正确的管理日期Instant
用于时间戳ZoneDateTime
给定时区的本地时间Enumeration
枚举对象,当选择此类型时,子生成器将会询问你对应的枚举值并创建enum类Blob
用于存储二进制数据,当被选中时,子生成器会询问你存储的数据类型,是图片对象还是CLOB可以对每个字段设置校验,不同的字段具有不同校验选项
校验将会被自动的生成在:
当对象被以下面的方式使用时,会自动的对领域对象进行校验:
@Valid
注解时)校验信息将尽可能清晰的使用数据库列的元数据描述:
non-nullable
校验功能会有一些限制:
实体关系仅支持关系型数据库,稍后有章节详细说明
相对于rest controller,单独的service类中允许有多种逻辑组合。业务层允许使用DTO。
相同逻辑使用spring service sub-generator,后续将由章节详细说明
默认JHipster实体不使用DTOs,但是如果使用业务层时,DTOs是一中可用的数据传输方式,具体用法后续章节详细说明
可选,实体存储在数据库中可以使用JPA过滤。
当你的应用使用Cassandra创建的时候,是不能使用分页的。当然这个特性将会被添加。
JHipster在服务端和客户端提供了依照标准规则的实现
当实体被生成时,JHipster提供了4中分页选项:
实体配置保存在~/.jhipster/*.json
文件中,如果你再次使用已存在的实体类名运行子生成器,你就可以更新或者重新生成实体
当你对一个已存在的实体运行子生成器时,将会询问’Do you want to update the entity? This will replace the existing files for this entity, all your custom code will be overwritten’(你想更新实体吗?这将会替换已存在的实体文件和实体相关的所有的代码将会被覆盖),有两种答案:
Yes, re generate the entity
,这将重新生成你的实体Yes, add more fields and relationships
询问你以添加更多字段和关系Yes, remove fields and relationships
浙江询问你是否移除已存在的字段和关系No, exit
你更新实体的理由可能如下:
.json
文件,所以你需要有一个新版本的实体.json
文件,然后想生成此实体快速生成多个实体:
for f in `ls .jhipster`; do jhipster entity ${f%.*} --force ; done
for %f in (.jhipster/*) do jhipster entity %~nf --force
这是一个简短的引导以创建两个具有one-to-many的实体(Author 和 Book)
一个Author包含多个Book的关系,所以我们需要首先创建Author,在数据库级别Jhipster将会创建一个外键在Book表上,这外键指向Author表
jhipster entity author
输入关于这个实体字段的问题的答案:
输入关于这个实体关系的问题答案:
jhipster entity book
回答接下来关于book字段的的问题 :
回答关于关系的问题:
运行生成的测试用例mvn test
,此测试用例测试Author和Book实体
启动应用,登录后点击- entities > Author
生成的文件包含了基础的CURD操作。如果你的需求比较简单的话,都不用进行任何修改。
如果你想修改生成的代码或者数据库表,你应该阅读后续的“开发指导”章节
如果你有一些组合的业务行为,你也许需要添加Spring的@Service
类,请使用 service生成器
controller生成器相对实体生成器而言,简单的多。此生成器用于生成Spring MVC REST controller,如:jhipster spring-controller Foo
,简单的回答完问题后,即可生成
在dev模式下,点击aministration > API
即可看到。
只需要添加@Secured
注解在你的类或者方法上,就可以限定用户的权限。
值需要添加@Timed
注解就可以监控
此生成器相对于实体类生成器简单的多。
此生成器生成你应用程序的业务逻辑的spring service对象,如:
jhipster spring-service Bar
这将会生成BarService
.生成的代码行数很少,但通常会有一些问题。
我们这里有两个主要的架构原则:
简单的说:不需要!
如果需要详细的解释,那么:
一个主要的兴趣点是使用Spring AOP,这个技术允许Spring添加一些行为在你的Bean之上:对于实力而言,提供了诸如事务控制、安全的工作等。
为了增加这写行为,spring需要为你累创建一个代理,一般由两种方式创建代理:
很多人的争论点之一是使用接口易于编写测试用例,但是我们详细我们不应该为了测试用例儿修改我们的产品代码,并且还有一些新的mock框架(如EasyMock)允许我们不用接口创建一些有些的单元测试用例。
因此,最终我们发现接口对于我们的Service Bean通常是无用的,所以我们不推荐使用(但我们任务提供了生成接口的选项)
默认JPA使用延迟加载一对多和多对多的实体关系,如果你使用默认配置,你将很可能看到LazyInitializationException
:这意味着你尝试在事务外使用未初始化的关系。
由于生成的service class默认使用了@ Transactional
注解,所有的方式都具有事务性,这意味着你可以再方法内获取所需的延迟加载的关系了,不用担心出现LazyInitializationException
提示:如果你不修改数据时在方法上使用@Transactional(readOnly = true)
。这是一个比较好的性能优化方式(hibernate不需要刷新一级缓存,因为我们没有修改任何数据)以及使用一些JDBC驱动时质量的提升(Oracle不允许发送INSERT/UPDATE/DELETE命令)
仅需要在类或者方法上添加@Secured
注解即可限制用户的访问权限。
仅需在方法上添加注解@Timed
即可。
警告这是一个比较新的特性,目前处于测试阶段,使用它有一定的风险,非常欢迎反馈。
Jhipster默认在REST端点使用领域模型对象(典型的JPA实体)。这样做有很多好处,如易于使用、便于理解和扩展。
但是在一些复杂的场景,你也许想在REST的端点暴露出数据传输对象(DTOs)。这些对象将在domain对象上添加一些额外的东西,并专门针对REST进行优化:他们最大的好处就是可以将多个domain对象聚合起来使用。
在生产JHipster实体的时候,你拥有一个添加Service层的选项:只有当你选择一个需要进行映射的Service层时DTOs选线才可以使用(当你使用的是JPA,由于service层具有事务,所以延迟加载会起作用)。
当你选择拥有service层时,你将会拥有从实体生成DTO的选项,如:
@JsonIgnore
)Set
中,因此,只有在另一个实体也是用DTO的时候才能起作用。DTOs看起来很像一个实体,因此需要一个自动映射他们的解决方案。
JHipster选择的解决方案是使用MapStruct,它是一个注解处理器,在Java编译的时候自动的生成需要的映射。
我们发现,相对于反射而言,它干净而且高效。(使用反射进行映射的时候,对于性能而言比较糟糕)
MapStruct是一个注解处理,当IDE编译项目的时候应该将它设置为自动运行。
如果你使用的士Maven,你需要在IDE中激活profile,gradle用户不需要进行任何设置。
如何激活Profile在“配置IDE”章节说明。
MapStruct映射可以被配置为一个SpingBeans,并且支持依赖注入。你可以将Repository注入到映射中,就可以使用ID在Mapper中获取JPA实体。 如:
@Mapper
public abstract class CarMapper {
@Inject
private UserRepository userRepository;
@Mapping(source = "user.id", target = "userId")
@Mapping(source = "user.login", target = "userLogin")
public abstract CarDTO carToCarDTO(Car car);
@Mapping(source = "userId", target = "user")
public abstract Car carDTOToCar(CarDTO carDTO);
public User userFromId(Long id) {
if (id == null) {
return null;
}
return userRepository.findOne(id);
}
}
当使用JPA的时候,实体生成器可以创建实体之间的关系。
只有使用JPA的时候关系才会工作,如果你选择 Cassandra, MongoDB of Couchbase的话关系是不可用的。
一个关系产生于两个实体之间,JHipster会生成如下代码:
这个章描述了如何通过Jhipster标准命令行接口生成关系。如果你需要常见比较多的实体和关系的时候,更好使用一些图形工具。
你可以通过import-jdl
子生成器使用JDL文件生成实体和关系jhipster import-jdl you-jdl-file.jh
我们使用JPA,通常可用的关系有:one-to-many,many-to-one,many-to-many,以及one-to-one:
请注意通过jhipster生成的User
实体,你可以:
给此实体添加many-to-one
关系(一个Car
有many-to-one
到User
),这将会在你的新的实体Repository中生成一个特定的查询,然后你参考一使用当前的认证用户查询实体。在生成的angular界面,你可以通过下拉选择Car来选中一个用户。
many-to-many
和one-to-one
关系在User
实体中,另一个实体必须是关系的所有者(一个Team
和User
是many-to-many关系,但是Team
能增删User
,但是User
不能增删Team
).在angular界面,你可以在多选框中选择User
.
让我们从两个实体开始吧,Owner
和Car
。一个owner可以多辆car,一个car只能属于一个owner。
因此在owner这边是一个简单的一对多关系,而在另一边是一个多对一的关系。
Owner (1) <------> (*) Car
首先我们来创建Owner
,这有几个关系Owner
的问题:
jhipster entity Owner
...
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Car
? What is the name of the relationship? car
? What is the type of the relationship? one-to-many
? What is the name of this relationship in the other entity? owner
请注意我们选择默认的关系名称。接下来,我们生成Car
:
jhipster entity Car
...
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Owner
? What is the name of the relationship? owner
? What is the type of the relationship? many-to-one
? When you display this relationship with AngularJS, which field from 'Owner' do you want to use? id
使用下边的JDL也可以实现:
entity Owner
entity Car
relationship OneToMany {
Owner{car} to Car{owner}
}
这样,你现在就拥有了一个一对多的关系在两个实体之间。在angular界面你可以通过Car
下拉框选中一个User
.
在前边的例子中我们有一个双向关系:通过Car
实例可以发现他的Owner
,通过Owner
可以找到他的所有Car
一个单向多对一的关系意味着Car
可以知道他们的Owner
,但是不能反过来:
Owner (1) <------ (*) Car
拥有这样的关系可能有两个原因:
Owner
时性能提升(因为你不用哪个维护一个Car的集合)在这个例子中,你仍然需要先创建Owner
,但这次没有生成关系:
jhipster entity Owner
...
Generating relationships with other entities
? Do you want to add a relationship to another entity? No
接下来生成Car
实体,和前边的例子相似:
jhipster entity Car
...
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Owner
? What is the name of the relationship? owner
? What is the type of the relationship? many-to-one
? When you display this relationship with AngularJS, which field from 'Owner' do you want to use? id
这将会和前边的例子一样正常公国,但是我们不能通过Owner
来添加和删除Car
。在angular你将会通过Car
的下来框选中一个Owner
。下边是相关的JDL:
entity Owner
entity Car
relationship ManyToOne {
Car{owner} to Owner
}
单向一对多关系意味着Owner
实例可以获取一个Car
集合,但不反过来不能。这个和前边的例子是相反的:
Owner (1) ------> (*) Car
目前,这种关系类型Jhipster默认是不提供的。原因查看#1569
你有两种解决方式:
@OneToMany
注解上移除mappedBy
属性mvn liquibase:diff
生成表。在JHipster中不支持通过JDL生成这种关系。
在这个例子中,一个Person
可以成为多个Car
的拥有者,当然他也可以驾驶多辆Car
:
Persion (1) <---owns---> (*) Car
Persion (1) <---drivers---> (*) Car
为此我们需要使用我们之前的例子中保留的默认关系名称。
生成Persion
实体,它具有到Car
的一对多关系:
jhipster entity Person
...
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Car
? What is the name of the relationship? ownedCar
? What is the type of the relationship? one-to-many
? What is the name of this relationship in the other entity? owner
...
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Car
? What is the name of the relationship? drivedCar
? What is the type of the relationship? one-to-many
? What is the name of this relationship in the other entity? driver
生成Car
实体,使用在Person
中的相同的关系名称:
jhipster entity Car
...
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Person
? What is the name of the relationship? owner
? What is the type of the relationship? many-to-one
? When you display this relationship with AngularJS, which field from 'Person' do you want to use? id
...
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Person
? What is the name of the relationship? driver
? What is the type of the relationship? many-to-one
? When you display this relationship with AngularJS, which field from 'Person' do you want to use? id
也可以通过如下的JDL实现:
entity Person
entity Car
relationship OneToMany {
Person{ownedCar} to Car{owner}
}
relationship OneToMany {
Person{drivedCar} to Car{driver}
}
现在,一个Car
拥有一个driver和一个owner,他们都是Person
实体。在生产的angular的界面我们可以下拉Car
为owner
和driver
选中一个Person
.
一个Driver
可以驾驶多辆汽车,但是一辆Car
也可以有被多个司机。
Driver (*) <------> (*) Car
在数据库级别,这意味着Driver
和Car
会有一张关联表。
对于JPA,这两个实体中一个将负责管理关系:在我们的例子中,将有Car
来负责增删Driver
.
让我们在没有关系所有权的一方生成多对多关系:
jhipster entity Driver
...
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Car
? What is the name of the relationship? car
? What is the type of the relationship? many-to-many
? Is this entity the owner of the relationship? No
? What is the name of this relationship in the other entity? driver
接下来生成具有多对多关系所有权的一方Car
:
jhipster entity Car
...
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Driver
? What is the name of the relationship? driver
? What is the type of the relationship? many-to-many
? Is this entity the owner of the relationship? Yes
? When you display this relationship with AngularJS, which field from 'Driver' do you want to use? id
也可以通过JDL实现相同的目的:
entity Driver
entity Car
relationship ManyToMany {
Car{driver} to Driver{car}
}
这样,你就拥有了两个实体之间的多对多关系。在生成的angular界面,你可以通过一个Car的复选下拉框来选择属于这个Car
的多个Driver
了。
接着我们之前的例子,一个一对一关系意味着一个Driver
只能驾驶一辆Car
,一辆Car
只能有一个Driver
。
Driver (1) <------> (1) Car
首先我们创建没有关系所有权的一方,Driver
:
jhipster entity Driver
...
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Car
? What is the name of the relationship? car
? What is the type of the relationship? one-to-one
? Is this entity the owner of the relationship? No
? What is the name of this relationship in the other entity? driver
接下来我们创建拥有关系所有权的一方Car
:
jhipster entity Car
...
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Driver
? What is the name of the relationship? driver
? What is the type of the relationship? one-to-one
? Is this entity the owner of the relationship? Yes
? What is the name of this relationship in the other entity? car
? When you display this relationship with AngularJS, which field from 'Driver' do you want to use? id
通过JDL可以实现相同的目的:
entity Driver
entity Car
relationship OneToOne {
Car{driver} to Driver{car}
}
单向一对一关系意味着可以通过citizen
获取到passport
,但不能通过passport
获取citizen
Citizen (1) -----> (1) Passport
首先我们生成没有任何所属关系的一方Passport
:
jhipster entity Passport
...
Generating relationships with other entities
? Do you want to add a relationship to another entity? No
接下来,生成Citizen
实体:
jhipster entity Citizen
...
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Passport
? What is the name of the relationship? passport
? What is the type of the relationship? one-to-one
? Is this entity the owner of the relationship? Yes
? What is the name of this relationship in the other entity? citizen
? When you display this relationship with AngularJS, which field from 'Passport' do you want to use? id
做完这些,一个Citizen
拥有一个护照,但是在Passport
上没有定义Citizen
实例。生产的angular界面上,你可以下拉Citizen
来选中一个Passport
。
相应的JDL如下:
entity Citizen
entity Passport
relationship OneToOne {
Citizen{passport} to Passport
}
在生成一个新项目的时候,你将会被询问是否需要启用国际化的支持。
如果启用,你需要为你的应用选择一个原生语言。然后你可以选择你需要添加的其他语言。如果你一开始没有选择支持或者添加语言,那么稍后你可以通过语言生成器添加语言。
如果你确认你的应用不需要翻译成其他语言,你就可以不启用国际化。
可以通过以下命令来进行添加:
jhipster languages
Languages configuration is starting
? Please choose additional languages to install (Press to select, to toggle all, to inverse selection)
❯◯ Arabic (Libya)
◯ Armenian
◯ Catalan
◯ Chinese (Simplified)
◯ Chinese (Traditional)
◯ Czech
◯ Danish
(Move up and down to reveal more choices)
所有的语言文件存储于这些文件夹:客户端src/main/webapp/i18n
,服务端src/main/resources/i18n
下边是一个叫做new_lang
的语言的安装步骤:
src/main/webapp/i18/en
为src/main/webapp/i18/new_lang
(这是所有前端文件存储的地方)src/main/webapp/i18/new_lang
所有文件为AngularJs 1 添加语言的常量定义代码,此常量位于src/main/webapp/app/components/language/language.constants.js
.constant('LANGUAGES', [
'en',
'fr',
'new_lang'
// jhipster-needle-i18n-language-constant - JHipster will add/remove languages in this array
]
src/main/resources/i18n
文件夹中,拷贝messages_en.properties
文件为messages_new_lang.properties
(服务端所有的翻译文件存储在这儿)messages_new_lang.properties
文件中的所有键src/main/webapp/app/components/language/language.filter.js
文件的函数filter('findLanguageFromKey')
为Angular JS 1添加新的语言名称。在src/main/webapp/app/shared/language/find-language-from-key.pipe.ts
文件的变量FindLanguageFromKeyPipe
的languages
添加新的语言名称。在webpack.common.js
文件中为Angular 2+添加新的语言绑定
new MergeJsonWebpackPlugin({
output: {
groupBy: [
{ pattern: "./src/main/webapp/i18n/en/*.json", fileName: "./i18n/en.json" },
{ pattern: "./src/main/webapp/i18n/new_lang/*.json", fileName: "./i18n/new_lang.json" }
// jhipster-needle-i18n-language-webpack - JHipster will add/remove languages in this array
]
}
})
现在,新的语言new_lang
就出现在了语言菜单的下来框中,并且前端和后端应用都可用。
通过以下步骤移除叫做old_lang
的语言:
src/main/webapp/i18/old_lang
src/main/webapp/app/components/language/language.constants.js
或者 src/main/webapp/app/shared/language/language.constants.ts
和webpack.common.js
移除定义的常量src/main/resources/i18n/messages_old_lang.properties
当新版本的Jhipster被发布时,使用JHipster upgrade生成器帮助升级已存在的应用到最先进的版本。升级有益于以下几点:
在升级之前一定要仔细的阅读这个章节,并且理解升级过程的工作。
这个子生成器工作必须使用git
进入应用的根目录:cd myapplication/
升级你的应用: jhipster upgrade
执行此命令时有以下几个选项:
--verbose
打印每一步骤的详细日志--target-version=4.2.0
升级至指定的版本--force
如果没有新的版本可用也可以强制升级请注意:jhipster upgrade会在你的项目中创建一个没有历史信息的分支,尽管在此图中没有正确的显示出来。
jhister通过下边的步骤来进行升级:
jhipster_upgrade
分支存在,如果不存在则创建(详细的过程下一小节描述)jhipster_upgrade
分支jhipster --force --with-entities
jhipster_upgrade
分支jhipster upgrade
命令将会合并jhipster_upgrade
分支到原始分支第一次执行jhipster upgrade
为了避免擦除你的修改代码,将会有额外的步骤运行:
jhipster_upgrade
分支被创建master
分支上进行块提交:你的master
分支没有进行任何更改,master的当前jhipster分支是最新的,这是一种最佳实现不要再jhipster_upgrade
分支上进行任何提交,这个分支将只用于jhipster升级:每次升级的时候,都会创建一个新的jhipster_upgrade
分支
jhipster的第一个问题是询问你想生成何种类型的应用.你有两种不同架构的选择:
单体应用是非常容易被使用的,因此你如果没有特殊需求,这是一个推荐的选项,也是我们默认的选项
jhipster的微服务架构通过以下方式工作:
microservice gateway
),它用来处理web通信并且是angular应用的服务端。如果你想要后台适应前端,这儿提供好几个网关,但不是强制的。在下边的图中,绿色的组件是你的应用程序,位于蓝色组件提供的基础服务之下
Jhipster可以生成API网关,网关是一个普通的jhipster应用,你可以使用通用的jhipster选项和开发工作流程,当然,你也可以用它作为你的微服务的入口。特别的,它为所有微服务提供了HTTP路由和负载均衡,服务质量,安全和API文档
当网关和微服务启东时,他们会自己注册到注册中心(使用src/main/resources/config/application.yml
文件的键eureka.client.serviceUrl.defaultZone
)
网关会使用每个微服务应用名称,自动的代理所有的请求。比如,当微服务app1注册了,通过网关可以通过/app1
的URL来访问
比如,你的网关运行在localhost:8080
,你可以通过http://localhost:8080/app1/rest/foos
获取app1上的foos
资源。如果你在web浏览器上尝试,不要忘记jhipster对于REST资源的安全的控制。因此,你需要发送正确的JWT请求头(下个小节详细解释),或者在微服务的MicroserviceSecurityConfiguration
移除对这些URL的安全控制。
如果有多个相同服务实例运行时,网关可以从注册中心获取这些实例,然后:
Netflix Ribbon
进行HTTP请求负载Netflix Hystrix
提供一个断路器,当实例失败时,快速安全的移除每个网关都有一个特定的admin > gateway
菜单,在这里可以打开http路由和监控每个微服务
JHipster标准的安全选项在安全的章节有详细说明。然而,确保一个微服务体系有一些特定的调优和选项,这儿将详细解释:
JWT是一个工业标准,为微服务架构的安全提供了易于使用的方法
JHipster使用JJWT libary,它由Okta提供,实现了JWT
通过网关生成token,然后发送给位于网关之下的微服务:这些微服务共享一个安全密钥,微服务能够使用其验证token,并且使用这个token能对用户进行认证
这些Token将会提供一些重要的信息(自给自足的信息):它们具有授权和认证信息,因此每个微服务不需要查询数据库或者扩展系统。这对于微服务的水平伸缩架构时比较重要的。
为了安全工作,一个JWT密钥令牌需要在所有的应用之间共享:
.yo-rc.json
文件中src/main/resources/config/application.yml
文件的jhipster.security.authentication.jwt.secret
键值配置spring config server
共享(使用指定的配置在consul中)。这也是人们为什么使用中心配置服务的原因。jhipster提供了OpenId连接的支持,详细的在OpenID章节详细解释。
当我们选择openid的选项的时候,你将默认使用Keycloak,并且很可能你的微服务应用完全使用docker compose:请确认读了我们的docker compose文档和正确的在你的/ets/hosts配置了Keycloak
当使用OpenId连接的时候,jhipster网关将发送OAuth2的令牌给微服务,他们将会接收这些令牌并连接到keycloak.
不同JWT,这些Token并不能自给自足,主要由两个原因:
微服务中红的性能问题:由于查询当前用户信息是非常常见的需求,每个微服务调用OpenId连接服务器获取这些信息。在正常的设置中,每个微服务都会调用,每次请求很快会造成性能问题。
CachedUserInfoTokenServices
spring bean,她将会缓存这些调用。适当的调休会降低一些性能的问题。src/main/resources/application.yml
使用标准的spring boot配置键security.oauth2.resource.userInfoUri
认证在Keycloak和应用之间不自动同步,请注意在标准的openid连接工作流中,我们希望在这个问题上进行一些具体的改进。结果如:
jhipster提供了一个生成基于spring security的UAA
服务选项,这个服务为网关的安全提供了OAuth2的令牌
你将会在后续的章节发现UAA的文档。
网关使用Spring Security的JWT实现发送JWT令牌到微服务,这基本与上边描述的JWT配置相同
网关公开了微服务定义的swagger api,这样你就可以从一些工具中获得收益如swagger UI和swagger-codegen
网关的admin > API
下拉菜单,展示了网关api和api来自与哪个微服务。
使用这个下拉列表,所有的微服务API将会被自动的记录下来,并且可以从网关进行测试。
当使用一个安全控制的API,swaggerui会自动的添加安全令牌,因此一切请求开箱即用
这是一个先进的功能,它使用Bucket4j个Hazelcast来保障微服务的质量
网关提供了一些限速功能,一些REST请求可以被限制:
jhipster使用bucket4j和hazelcast来估算请求总量,当超过限制时会发送HTTP响应码429(太多请求),默认每个用户每小时调用10万次API
这是一个重要的功能,保证了微服务体系不会被一些特定的用户的请求淹没.
网关可以保护REST的端点,他可以完全获取用户的安全信息。因此很容易的扩展,根据用户的安全角色提供特定的速率限制。
启用速率限制,打开application-dev.yml
或者application-prod.yml
文件并设置enabled
为true
:
jhipster:
gateway:
rate-limiting:
enabled: true
数据存储在Hazelcast
中,因此只要配置了Hazelcast分布式缓存就可以对网关进行扩容,这些是开箱即用的:
如果你想要添加更多的规则或者修改已存在的规则,你需要在RateLimitingFilter
编码。例如可以修改:
默认通过网关可以访问所有注册的微服务。如果你想通过网关排除指定的API,你可以使用网关指定的访问控制策略过滤器。这个配置在application-*.yml
文件的jhipster.gateway.authorized-microservices-endpoints
键上,如:
jhipster:
gateway:
authorized-microservices-endpoints: # Access Control Policy, if left empty for a route, all endpoints will be accessible
app1: /api,/v2/api-docs # recommended dev configuration
例如,你仅想使bar
微服务的/api/foo
端点可用:
jhipster:
gateway:
authorized-microservices-endpoints:
bar: /api/foo
traefik是一个现代HTTP反向代理和负载局衡器,使得微服务的部署比较容易
它可以像zuul
一样路由HTTP请求,因此它与jhipster的gateway有一些功能上的重叠,但是它工作于网关的更低一级:它仅仅路由http请求,但不提供速率限制、安全和swagger文档聚合
对于多种微服务的发现方案是比较有益的,但是它仅工作与Consul
这有两种不同的架构风格,描述如下:
Traefik作为反向代理和均衡器,放弃使用Zuul,它将所有的http请求正确的转发到微服务。
在这种架构中,jhipster”网关“不是一个真正的网关,主要服务于angular应用,这是我们的默认配置
Traefik可以和Zuul一起工作:在这例子中,一个HTTP请求通过Traefik然后通过Zuul到达目的地。
这种方式多增加了一此网络请求,因此比之前的架构低效。但是这允许网关充分发挥潜力:进行限速和swagger文档聚合
作为结果,Traefik作为一个边缘服务,允许扩展jhipster网关
这个配置在JHipster是开箱即用的:唯一问题是客户端应用需要使用绝对URL。因此对于microservice1
:
/microservice1
,只能通过Traefik访问/gateway/microservice1
将会使用Traefik配置的gateway访问microservice1应用请注意Traefik只能工作于Consul,如果你使用的Jhipster Registry则不能工作
使用Traefik架构,运行docker-compose生成器时询问你选择哪种网关时,选择Traefik
这将会生成traefik.yml
配置文件以便于在Docker运行Traefik,并会生成并配置traefik/traefik.toml
文件
这个配置文件如此设置:
80
端口,如果你的需要访问网关,请访问http://localhost/gateway
8080
端口,你应该访问http://localhost:8080
当Traefik使用Consul时,检查Consul管理界面也比较有用,可以访问http://localhost:8500
jhipster registry是由jhipster团队提供的一个应用,它是基于Apache2-licensed协议开源的,它的代码已托管至github,jhipster/jhipster-registry
JHipster Registry有三个主要用途:
所有的这些功能都被打包在一个基于angular的用户界面应用中。
JHipster registry使用了jhipster通常使用的dev
和prod
的配置,以及使用了来自于 spring cloud config的标准native
和git
配置
结果:
dev
配置运行JHipster Registry的时候,它将会使用dev
和native
配置文件,native
将会从文件系统加载spring cloud的配置,具体的文件位于运行目录的central-config
prod
配置运行JHipster Registry的时候,将会使用prod
和git
配置文件。git
profile将会git仓库加载spring cloud配置,默认的仓库是https://github.com/jhipster/jhipster-registry-sample-config
。在真实使用的时候,这个地址应该被修改为正确的地址,可以通过重新配置src/main/resources/config/bootstrap-prod.yml
或者spring.cloud.config.server.git.uri
属性一旦JHipster Registry运行,你可以在Configuration > cloud config
菜单中检查他们。请注意,如果你不能登录,这可能是由于你没配置好导致JWT的签名不对
JHipster Registry是一个可执行的war包.
下载war包后,就像是通常运行JHipster应用程序一样,选择你想使用的配置。例如,运行Registry且Spring cloud config配置存储在cetral-config
文件夹中:
./jhipster-registry-
注意在启动Registry的时候提供一个JWT的密钥是比较重要的事情,你可以通过环境变量JHIPSTER_SECURITY_AUTHENTICATION_JWT_SECRET
或者使用类似于上边的参数的方式提供。另一种可能的方法是在application.yml
文件中设置。
类似的,在以prod
启动Registry的时候,你可以使用下边的命令:
./jhipster-registry-
JHipster Registry的源码可以从git上获取(克隆、引用或者下载).此应用也是通过jhipster生成器生成的。你可以用使用你喜欢的方式运行:
./mvnw
运行(作为一个Java服务)和yarn start
(作为前端)他们默认使用dev
配置。可以通过http://127.0.0.1:8761/访问./mvnw -Pprod package
打包至正式环境,通常生成一个可执行的war。你也可以通过使用dev
或prod
配置来运行:./jhipster-registry-.war --spring.profiles.active=prod
注意使用native
配置,你需要由一个central-config
目录。因此如果你已./jhipster-registry-
方式运行,你需要提供这个目录
如果你更喜欢通过docker镜像运行JHipster Registry,那么在docker hub上有一个可用镜像.存在于每个微服务的src/main/docker
目录下的docker-compose文件可以很轻易使其运行与docker上:
docker-compose -f src/main/docker/jhipster-registry.yml up
启动JHipster registry。它将会暴露端口为8761
,如果docker在你的主机上,可以访问http://127.0.0.1:8761/在云上托管一个实例是非常容易的。在生产中是强制的,但在开发中也是比较有用的(你没必要在你的笔记本上运行),后续在生产环境使用微服务章节详细解释如何部署在云上.
JHipster Registry是一个Eureka服务,它提供了所有应用的服务发现
JHipster Registry是一个Spring Config Server. 当应用程序启动时首先会连接Jhipster Registry获取他们的配置.对于网关和微服务都是这样的
这个配置是一个spring-boot的配置,类似于jhipster中的application-*.yml
文件,但是这些是集中存储的,因此更易于管理
当启动的时候,网关和微服务应用将会查询Registry的配置服务,使用在Registry定义的属性覆盖本地的属性
有两种配置源可用:
native
配置,一般被用于开发环境,使用本地文件系统git
配置,在生产环境默认使用,他们将会存储在git服务。允许使用git工具进行打标签、分支或者回滚配置.为了集中管理你的配置,你只需在你的配置源中增加appname-profile.yml
文件。例如,添加一个gateway-prod.yml
文件,将会只为gateway服务通过prod配置启动的时候设置这些属性。此外,application[-dev|prod].yml
文件中定义的属性将被设置到所有的服务上.
使用spring-boot网关也是可配置的,他也能使用SpringConfigServer管理。例如,你的分支v1
的应用app-v1
映射在/app1
的url上,分支v2
的应用app1-v2
也映射在/app1
上,这样就是一种很好的升级方式,而且不需要停机升级。
JHipster Registry有一个configuration > encryption
页面允许容易的加密和解密配置文件中的值。
为了加密配置文件中的值你需要:
encrypt.key
或者使用ENCRYPT_KEY
环境变量如果所有的设置都是正确的,你可以使用configuration > encryption
页面,你也可以POST
你想要处理的文本置于body
中请求/config/encrypt
和/config/decrypt
。
例如: curl localhost:8761/config/encrypt -d mypassword
密码文本必须放在*.yml
中,password= '{cipher}myciphertextafterencryotion'
将发送到客户端之前将被解密。这样你的git或者本地配置文件中就没有明文.
更多的细节,参照spring cloud config的加解密
JHipster registry提供了一个管理各种类型应用的面板。当应用注册到eureka服务不久,就会在面板中看到。
为了读取应用的敏感信息,JHipster Registry将会使用JWT令牌(这就是JHipster Registry使用JWT的原因).请求签发的JWT令牌对于应用程序和注册中心是相同的:JHipster Registry通过spring cloud config来配置应用程序,这应该是开箱即用的,因为它向所有应用程序发送相同的密钥。
指标面板使用Dropwizard指标对每个应用性能进行详细展示,这些指标有:
@Timed
注解)点击JVM线程指标后边的眼睛图标,可以查看应用的运行栈,这对于查找阻塞线程是比较有用的
健康面板使用springboot-actuator的接口获取应用的各部分健康信息。spring-boot-actuator提供了很多开心急用的方法,也很容易添加应用指定的健康检查。
配置面板使用spring-boot-actuator的配置接口获取当前应用的配置视图
日志面板允许在运行的时候管理Logbak的配置。改变package的日志级别简单到店家一个按钮就可以做到,这对于生成环境和开发环境提供了很大的便利性
JHipster Registry默认是安全的。你可以使用admin/admin
像登录普通服务一样登录Registry.
应用也可以使用admin用户通过HTTP Basic的认证方式连接到Registry。如果你的应用不能读取注册中心的数据,并且你看到了”401 authentication error”信息,那是因为你在应用的配置缺失了.
为了提高JHipster Registry的安全:
security.user.password
设置,因此你可以采用spring-boot的机制设置它:你可以修改application-*.yml
文件或者添加SECURITY_USER_PASSWORD
环境变量替代JHipster Registry的另一种选择是使用Consul——一个来自Hashicorp的数据中心管理解决方案,相对于使用Eurake有以下几种优点:
开始开发依赖Consul的应用程序,你可以在docker上启动Consul实例:
docker-compose -f src/main/docker/consul.yml up
启动一个consul服务在dev
模式下,consul将在你的Docker主机上的8500端口可用,因此你主机上的应该是http://127.0.0.1:8500/当你生成你的项目或者网关的时候选择了Consul选项,他们将会自动的从consul的键值存储中获取他们的配置.
存储的键值对可以通过界面或者REST API进行修改。但是这样的修改是临时性的,当Consul服务关闭时丢失。因此为了轻松的与键值存储交互以及将配置作为简单的yaml文件进行管理,JHipster提供了一个小工具consul-config-loader.当docker-compose通过consul.yml
文件启动consul的时候,consul-config-loader会自动的配置。它有两种运行模式:
dev
模式,从central-server-config
目录自动加载yaml文件到consul.此外对这个目录的任意修改都会同步到consul.prod
模式,使用Git2Consul将git仓库的yaml配置文文件设置为键值存储的源注意,将此作为JHipster Registry的时候,你的配置文件需要命名为appname-profile.yml
,appname好profile与你希望提供的服务的应用名称和配置相符。例如,添加一个consulapp-prod.yml
文件将会把文件中的属性应用到consulapp
应用的prod
启动的模式中。此外,定义于application.yml
文件定义的属性将会为所有的应用程序设置.
JHipster UAA是一个使用OAuth2认证协议、为JHipster微服务提供用户账户和授权的服务。
应当清晰的了解JHipster UAA和其他的UAA(例如cloudfoundry UAA)。 JHipster UAA是一个完全配置的使用内置的user和roleOAuth2授权认证服务,被打包成一个普通的JHipster应用。这允许开发人员深入的配置他的user
领域模型的每个部分,而不限制它访问UAA的策略。
在深入的研究位于JHipster微服务上的OAuth2与应用之前,需要清楚地了解可靠的安全解决方案的要求
大多数微服务都是独立自主的应用。因此希望有一个一致的认证体验而不想用户注意到他的请求来自于不同的安全配置的应用
最大的好处是构建的微服务可扩展,因此选择的安全解决方案不能影响扩展性。在服务器上保持用户会话状态较为棘手,因此一个无状态的解决方案成为首选.
需要清除的区分不同的用户和不同的机器。使用微服务架构可以构建一个大型的不同领域的数据资源中心,因此需要限制不同的客户端,例如本地的应用、多个SPAs等等的访问.
在维护集中的角色时,需要在每个微服务中配置详细的访问控制策略。微服务不应该起识别用户的责任,并且每个进入的请求都经过了授权.
无论一个安全解决方案能够解决多少问题,它都应该尽可能强大
使用无状态协议并不是安全解决方案的保证。最后,不应该有单点问题。一个反例是单节点共享的授权数据库或者单节点的授权服务实例。
使用OAuth2**协议**能够满足以上6点要求。它遵循严格的标准,使得它可以与所有的微服务或者其他的远程系统相容。JHipster基于此提供了一些解决方案:
REST-Client
,也可以是curl
以及一切可以发起请求的事物该设计可以应用于任何独立的语言或者框架的微服务架构体系
另外,以下规则也可以用于访问控制:
roles
和RBACscopes
和RBACroles
和scopes
在生成Jhipster微服务脚手架的时候,你可以选择使用UAA选项代替JWT认证
**注意**UAA解决方案也使用了JWT,它可以使用spring-cloud-security自定义JWT
最基本的设置包括:
这是生成它的顺序。
除了提供身份验证类型之外还需要提供UAA的位置
这个设置的工作方式与JWT类型相同,但会有一个服务
Jhipster UAA 服务做了三件事:
AuthorizationServerConfigurerAdapter
定义了基本的客户端(“web_app”和”internal”)/oauth/token_key
,将会被其他的微服务消费选择数据库、缓存方案、搜索引擎、构建工具以及更多的选项都开放给开发人员。
当一个微服务启动时,通常UAA已经启动并开始共享公钥。服务首先会调用/oauth/token_key
获取公钥并使用此密钥配置签名(JwtAccessTokenConverter
)
如果UAA没有启动,应用将会继续启动并在稍后获取公钥。有两个属性可以空多久(uaa.signature-verification.ttl
)获取一次公钥和限制请求次数(uaa.signature-verification.public-key-refresh-rate-limit
)避免频繁请求。这些值通常有默认值。在任何情况下,如果检查失败,微服务将会检查是否有新的公钥。这样,公钥可以被替换而服务一直可用.
从这点来看可能有两个用例使用这个基本设置:用户调用和机器调用(应用调用)
对于用户调用,登录请求发送至网关的/auth/login
端点。这个端点使用OAuth2TokenEndpointClientAdapter
发送请求到UAA进行密码去身份验证。因为这个请求发生在网关上,所以clientId和密钥在客户端没有存储而用户无法访问到。网关将返回一个包含令牌的Cookie,在客户端后续的请求中这个cookie将随着客户端请求发送至jhipster后端。
对于机器(应用)调用,该应用需要通过客户端证书使用UAA进行身份验证。JHipster提供了一个标准的解决方案,后续有详细描述。
刷新访问令牌通常发生在网关上,如下所示:
AuthResource
调用OAuth2AuthenticationService
的认证(设置cookies)RefreshTokenFilter
(在RefreshTokenFilterConfigurer
配置)会检查访问令牌是否有效、是否过期OAuth2AuthenticationService
刷新令牌OAuth2TokenEndpointClient
接口发送请求刷新令牌授权到OAuth2服务器,在我们的案例中是UAA(通过UaaTokenEndpointClient
)下面是一个开发人员应该知道的事情的简要列表.
生产和演示环境使用相同的签名密钥
非常严肃的推荐大家尽可能的使用不同的密钥。一旦一个签名密钥落入一个错误的人手中,就可能在不知道任何用户凭据的情况下获取完全的访问授权密钥.
不使用TLS
如果攻击者设法截取到令牌,他将获得该令牌的所有权限,知道令牌过期。可以有很多种方式实现这一点,特别是再没有TLS加密的情况下。这在OAuth1的时候是不会出现的因为协议要求强制加密.
在url中使用访问令牌
根据标准规范,访问令牌可以在url、header或者cookie中。从TLS的观点来看,这三种方法都是安全,实际上URL的安全性较低,有很多种方式从url记录中获取。
选在对称加密签名
JWT签名并不一定需要RSA,Spring Security也提供了对称的令牌签名。这虽然解决了一些问题,但使得开发困难。这也是不安全的,攻击者只需要获取一个节点的微服务就可以生成自己的JWT令牌。
目前,只有JHipster UAA提供了一种可伸缩的安全的跨服务通信方法。
使用JWT身份验证无需手动将JWT请求转发至内部以强制微服务之间通过网关互相调用(主请求的内部请求)。但即使自动转发,也不能将用户和应用的身份验证分离。
由于JHipster使用了OAuth2,所有的问题在这个协议上都得到了解决。
这个章节讲述了如何轻松的使用它。
当一个服务想要请求其他服务的数据时,会有4个组件参与进来。因此了解每个的职责是非常重要的:
综上所述,当我们需要访问一个URLhttp://uaa/oauth/token/
(此URL由运行在10.10.10.1:9999
和10.10.10.2:9999
上的两个实例提供服务),我们可以使用EUreka和Ribbon轮询算法快速的转换为http://10.10.10.1:9999/oauth/token
或者http://10.10.10.2:9999/oauth/token
在现实场景中,所有的服务实例都有可能出现问题。因此Hystrix作为一个断路器,以一种快速失败的方式来处理调用失败的场景
但是手工编码这些东西工作量巨大。而Feign提供使用一些带注解的接口为访问注册到Eureka端点的带负载均衡的REST客户端选项,并使用Hystrix实现了回退。
所以,对于内部通信,Feign是非常有用的。当一个服务需要通过REST客户端访问另一个服务”othser-service”时,可能的接口定义如:
@FeignClient(name = "other-service")
interface OtherServiceClient {
@RequestMapping(value = "/api/other-resources")
List getResourcesFromOtherService();
}
接下来通过依赖注入进行使用:
@Service
class SomeService {
private OtherServiceClient otherServiceClient;
@Inject
public SomeService(OtherServiceClient otherServiceClient) {
this.otherServiceClient = otherServiceClient;
}
}
类似于Spring Data JPA,这里也不需要实现任何的接口。当然如果你使用Hystrix时你也可以这样做。Feign接口的实现类作为回退的实现。
一个存在的问题是使用UAA保障通信安全。为了解决这个,有一些用于Feign的拦截器,它实现了来自于OAuth的客户端认证,以授权当前服务请求其他服务。在Jhipster中,你只需要使用@AuthorizedFeignClients
即可。这注解是由JHipster提供的,它确实做到了这一点。
@AuthorizedFeignClients
考虑到上边的”other-service“服务的资源是受保护的,这个接口的注解如下:
@AuthorizedFeignClient(name = "other-service")
interface OtherServiceClient {
@RequestMapping(value = "/api/other-resources")
List getResourcesFromOtherService() ;
}
当没有优点的令牌在内存中的时候,REST客户端会自动的从UAA获取认证。
与Feign一起工作的组件都是可测试的。在测试中使用Feign和生产中使用的方式一样,将迫使Jhipster Registry与UAA运行在同一台机器(测试用例执行的机器)。大多数场景中,你不需要测试Feign自己,但是使用Feign客户端的组件需要。
要测试内部使用Feign的组件可以使用@MockBean
(自spring-boot 1.4.0引入)
这有一个例子,测试SomeService
使用模拟数据的客户端:
@RunWith(SpringRunner.class)
@SpringBootTest(App.class)
public class SomeServiceTest {
@MockBean
private OtherServiceClient otherServiceClient;
@Inject
private SomeService someService;
@Test
public void testSomeService() {
given(otherServiceClient.getResourcesFromOtherService())
.willReturn(Arrays.asList(new OtherResource(...));
someService.performActionWhichInkvokesTheAboveMentionedMethod();
//assert that your application is in the desired state
}
}
因此,通过这种技术,你可以模拟其他服务的行为并提供预期的资源实体。所有Bean都可以注入模拟的客户端,而你只要关注Bean中具体的逻辑。
使用Spring对REST控制器的集成测试通常会绕过安全配置,因为它会使测试变得很困难,当唯一的意图是证明控制器在执行它应该做的事情时。但是有时候,测试控制器的安全性行为也是测试的一部分。
在这个用例中,JHipster提供了一个叫做OAuth2TokenMockUtil
的组件,它可以模拟有效的认证而无需客户端或者用户存在。
要使用这个特性,有两件事情必须完成:
@Inject
private OAuth2TokenMockUtil tokenUtil;
@PostConstruct
public void setup() {
this.restMockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
OAuth2TokenMockUtil
这个工具提供另一个方法oaut2authentication
,可使MockMvn的”with”操作可用。它而已被配置使用以下字段模拟认证:
例子:
@Test
public void testInsufficientRoles() {
restMockMvc.peform(
get("url/requiring/ADMIN/role")
.with(tokenUtil.oauth2Authentication("[email protected]", Sets.newSet("some-scope"), Sets.newSet("ROLE_USER")))
).andExpect(status().isForbidden());
}
微服务是JHipster的一种应用类型,它没有前端,它可以通过JHipster Registry进行配置、发现和管理
在微服务架构中使用实体生成器有一点不同,因为前端和后端代码不在同一个应用中。
首先,在微服务应用中生成实体:和通常一样你可以使用jhipster UML或者JDL studio来生成复杂的实体和关系。但微服务不需要前端,不会有angularJS代码生成。
接着,在网关上,再次运行实体生成器。一个新的问题出现在指定的网关上了:
如果你的应用使用SQL数据库,JHipster为微服务提供了二级缓存的解决方案:
这是微服务的默认解决方案,在这个体系中可以扩展你的微服务:
在微服务中使用Hazelcast将有一个特定配置:
dev
配置时,JHipster在localhost创建一个集群使用不同的端口。默认,Hazelcast的端口是你的应用程序的端口+5701.(你的应用程序的端口是8081
那么Hazelcast的端口是13872
)prod
配置的时候,JHipster将会在所有发现的节点中创建一个集群,使用默认的5701
端口只要微服务应用在没有使用数据库的情况下创建。这是因为微服务很小,并且没有用户管理代码。
没有数据库的微服务非常小,可以用来连接到特定的后端,比如遗留系统。
微服务是一种特殊的JHipster应用程序。请参考我们在生产文档中使用JHipster的主要内容,了解更多关于生产构建、优化和安全的信息。
请参考JHipster Registry[之前章节]
文档,来了解哪些运行时面板可用以及如何使用。
我们的监控文档也是比较重要的,可以学习使用的详细信息:
在微服务体系结构上工作意味着您需要多个不同的服务和数据库一起工作,在这种环境下,Docker组合是管理您的开发、测试和生产环境的一个很好的工具。
在我们的Docker-compose文档中包含了一个关于微服务的特定部分,我们强烈建议您在处理微服务体系结构时熟悉它。
当Docker集群使用与Docker机器相同的API时,在云中部署微服务体系结构与在本地机器上部署微服务架构是完全一样的。请跟随我们的Docker-compose文档[后续章节]
,了解更多关于使用Docker与JHipster的组合。
【未完持续】