1.Spring 的 STOMP 消息传递
STOMP 消息传递来创建交互式 Web 应用程序。 STOMP 是一个运行在底层 WebSocket 之上的子协议。
使用Gradle,需要添加以下依赖:
implementation 'org.springframework.boot:spring-boot-starter-websocket'
implementation 'org.webjars:webjars-locator-core'
implementation 'org.webjars:sockjs-client:1.0.2'
implementation 'org.webjars:stomp-websocket:2.3.3'
implementation 'org.webjars:bootstrap:3.3.7'
implementation 'org.webjars:jquery:3.1.1-1'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
1.1请求
请求为:
{
"name": "kenychen16888"
}
1.2请求代码为:
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
Thread.sleep(1000); // simulated delay
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
1.3返回是
{
"content": "Hello, kenychen16888!"
}
1.4Create a Message-handling Controller 控制层
package com.example.messagingstompwebsocket;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.web.util.HtmlUtils;
@Controller
public class GreetingController {
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greetinggreeting(HelloMessage message)throws Exception {
Thread.sleep(1000); // simulated delay
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) +"!");
}
}
1.5在idea点击运行
1.6运行后
点击Connect按钮,再输入what’s your name 输入,kenychen ,就获取到结果为Hello, kenychen!
1.7恭喜!您刚刚使用 Spring 开发了一个基于 STOMP 的消息服务。
2.Testing in Spring Boot
我们将了解如何使用 Spring Boot 中的框架支持编写测试。我们将介绍可以独立运行的单元测试以及在执行测试之前引导 Spring 上下文的集成测试。我们将在本文中使用的应用程序是一个 API,它提供了对 Employee 资源的一些基本操作。这是典型的分层架构——API 调用从 Controller 到 Service 再到 Persistence 层进行处理。
spring-boot-starter-test 是主要依赖项,包含我们测试所需的大部分元素。
H2 DB 是我们的内存数据库。它消除了为测试目的配置和启动实际数据库的需要。
先要建立一个使用 Spring 构建 REST 服务项目为了测试上面
Spring Boot 可以与任何 IDE 一起使用。您可以使用 Eclipse、IntelliJ IDEA、Netbeans 等。Spring Tool Suite 是一个开源的、基于 Eclipse 的 IDE 发行版,它提供了 Eclipse 的 Java EE 发行版的超集。它包括使使用 Spring 应用程序更加容易的功能。它绝不是必需的。但是,如果您想要为您的击键增加额外的魅力,请考虑一下。这是一个演示如何开始使用 STS 和 Spring Boot 的视频。这是让您熟悉这些工具的一般性介绍
2.1定义一个Employee 类
package com.kenychen.sprintcrudrest;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
class Employee {
private @Id @GeneratedValue Longid;
private Stringname;
private Stringrole;
Employee() {}
Employee(String name, String role) {
this.name = name;
this.role = role;
}
public LonggetId() {
return this.id;
}
public StringgetName() {
return this.name;
}
public StringgetRole() {
return this.role;
}
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setRole(String role) {
this.role = role;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(oinstanceof Employee))
return false;
Employee employee = (Employee) o;
return Objects.equals(this.id, employee.id) && Objects.equals(this.name, employee.name)
&& Objects.equals(this.role, employee.role);
}
@Override
public int hashCode() {
return Objects.hash(this.id, this.name, this.role);
}
@Override
public StringtoString() {
return "Employee{" +"id=" +this.id +", name='" +this.name +'\'' +", role='" +this.role +'\'' +'}';
}
}
@Entity 是一个 JPA 注释,用于使该对象准备好存储在基于 JPA 的数据存储中。
id、name 和 role 是我们的 Employee 域对象的属性。 id 用更多 JPA 注释标记以表明它是主键并由 JPA 提供程序自动填充。
当我们需要创建一个新实例但还没有 id 时,就会创建一个自定义构造函数。
2.2定义一个EmployeeRepository
package com.kenychen.sprintcrudrest;
import org.springframework.data.jpa.repository.JpaRepository;
interface EmployeeRepository extends JpaRepository {
}
创建新员工
更新现有的
删除员工
查找员工(一个、全部或按简单或复杂属性搜索)
2.3 定义主函数SprintCrudRestApplication
package com.kenychen.sprintcrudrest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SprintCrudRestApplication {
public static void main(String[] args) {
SpringApplication.run(SprintCrudRestApplication.class, args);
}
}
2.4 定义LoadDatabase
package com.kenychen.sprintcrudrest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class LoadDatabase {
private static final Loggerlog = LoggerFactory.getLogger(LoadDatabase.class);
@Bean
CommandLineRunnerinitDatabase(EmployeeRepository repository) {
return args -> {
log.info("Preloading " +repository.save(new Employee("keny chen", "burglar")));
log.info("Preloading " +repository.save(new Employee("chen boss", "thief")));
};
}
}
2.5 运行主函数
2.6 HTTP 是平台
spring mvc EmployeeController
package com.kenychen.sprintcrudrest.Controller;
import java.util.List;
import com.kenychen.sprintcrudrest.EmployeeRepository;
import com.kenychen.sprintcrudrest.Model.Employee;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
class EmployeeController {
private final EmployeeRepositoryrepository;
EmployeeController(EmployeeRepository repository) {
this.repository = repository;
}
// Aggregate root
// tag::get-aggregate-root[]
@GetMapping("/employees")
Listall() {
return repository.findAll();
}
// end::get-aggregate-root[]
@PostMapping("/employees")
EmployeenewEmployee(@RequestBody Employee newEmployee) {
return repository.save(newEmployee);
}
// Single item
@GetMapping("/employees/{id}")
Employeeone(@PathVariable Long id) {
return repository.findById(id)
.orElseThrow(() ->new EmployeeNotFoundException(id));
}
@PutMapping("/employees/{id}")
EmployeereplaceEmployee(@RequestBody Employee newEmployee, @PathVariable Long id) {
return repository.findById(id)
.map(employee -> {
employee.setFirstname(newEmployee.getFirstname());
employee.setRole(newEmployee.getRole());
return repository.save(employee);
})
.orElseGet(() -> {
newEmployee.setId(id);
return repository.save(newEmployee);
});
}
@DeleteMapping("/employees/{id}")
void deleteEmployee(@PathVariable Long id) {
repository.deleteById(id);
}
}
2.7.定义一个model
package com.kenychen.sprintcrudrest.Model;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Employee {
private @Id @GeneratedValue Longid;
private Stringfirstname;
private Stringlastname;
private Stringrole;
public Employee() {}
public Employee(String firstName,String lastName, String role) {
this.firstname = firstName;
this.lastname =lastName;
this.role = role;
}
public LonggetId() {
return this.id;
}
public StringgetFirstname() {
return this.firstname ;
}
public StringgetLastname() {
return this.lastname ;
}
public StringgetRole() {
return this.role;
}
public void setId(Long id) {
this.id = id;
}
public void setFirstname(String name) {
this.firstname = name;
}
public void setLastname(String name) {
this.lastname = name;
}
public void setRole(String role) {
this.role = role;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(oinstanceof Employee))
return false;
Employee employee = (Employee) o;
return Objects.equals(this.id, employee.id) && Objects.equals(this.firstname, employee.firstname)
&& Objects.equals(this.role, employee.role);
}
@Override
public int hashCode() {
return Objects.hash(this.id, this.firstname, this.role);
}
@Override
public StringtoString() {
return "Employee{" +"id=" +this.id +", name='" +this.firstname +'\'' +", role='" +this.role +'\'' +'}';
}
}
2.8.定义一个EmployeeRepository extends
package com.kenychen.sprintcrudrest;
import com.kenychen.sprintcrudrest.Model.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
public interface EmployeeRepository extends JpaRepository {
}
2.9 运行查询雇员
通过postman可以进行post请求
2.9使用@SpringBootTest 进行集成测试
当我们需要引导整个容器时,@SpringBootTest 注释很有用。注释通过创建将在我们的测试中使用的 ApplicationContext 来工作。
我们可以使用@SpringBootTest的webEnvironment属性来配置我们的运行环境;我们在这里使用 WebEnvironment.MOCK 以便容器将在模拟 servlet 环境中运行。
接下来,@TestPropertySource 注释有助于配置特定于我们的测试的属性文件的位置。请注意,使用@TestPropertySource 加载的属性文件将覆盖现有的 application.properties 文件。
application-integrationtest.properties 包含配置持久性存储的详细信息
package com.kenychen.sprintcrudrest.Controller;
import com.kenychen.sprintcrudrest.EmployeeRepository;
import com.kenychen.sprintcrudrest.SprintCrudRestApplication;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
@RunWith(SpringRunner.class)
@SpringBootTest(
classes = SprintCrudRestApplication.class)
@AutoConfigureMockMvc
@TestPropertySource(
locations ="classpath:integrationtest.properties")
public class EmployeeRepositoryIntegrationTest{
@Autowired
private MockMvcmvc;
@Autowired
private EmployeeRepositoryrepository;
// write test cases here
@Test
public void whenFindByName_thenReturnEmployee() {
// given
Employee alex =new Employee("alex","alex","alexrole");
entityManager.persist(alex);
entityManager.flush();
//when
String name = alex.getName();
Optional any =employeeRepository.findAll().stream().filter(employee -> employee.getName().equals(name)).findAny();
if (any.isPresent()){
System.out.println(any.get().getName());
assertThat(any.get().getName())
.isEqualTo(alex.getName());
}
}
}
编写一个简单新雇员,然后再进行查询
查看日志
2021-07-21 18:13:21.021 INFO 224300 --- [ Test worker] .k.s.C.EmployeeRepositoryIntegrationTest : Starting EmployeeRepositoryIntegrationTest using Java 16 on KENY-PC with PID 224300 (started by Dev in D:\spring\sprint-crud-rest)
2021-07-21 18:13:21.024 INFO 224300 --- [ Test worker] .k.s.C.EmployeeRepositoryIntegrationTest : No active profile set, falling back to default profiles: default
2021-07-21 18:13:21.393 INFO 224300 --- [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2021-07-21 18:13:21.448 INFO 224300 --- [ Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 48 ms. Found 2 JPA repository interfaces.
2021-07-21 18:13:21.508 INFO 224300 --- [ Test worker] beddedDataSourceBeanFactoryPostProcessor : Replacing 'dataSource' DataSource bean with embedded version
2021-07-21 18:13:21.677 INFO 224300 --- [ Test worker] o.s.j.d.e.EmbeddedDatabaseFactory : Starting embedded database: url='jdbc:h2:mem:44725134-c7b2-441b-ab6d-bf5651821193;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'
2021-07-21 18:13:21.972 INFO 224300 --- [ Test worker] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2021-07-21 18:13:22.026 INFO 224300 --- [ Test worker] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.4.32.Final
2021-07-21 18:13:22.158 INFO 224300 --- [ Test worker] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2021-07-21 18:13:22.269 INFO 224300 --- [ Test worker] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
Hibernate: drop table if exists customer_order CASCADE
Hibernate: drop table if exists employee CASCADE
Hibernate: drop sequence if exists hibernate_sequence
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: create table customer_order (id bigint not null, description varchar(255), status integer, primary key (id))
Hibernate: create table employee (id bigint not null, first_name varchar(255), last_name varchar(255), role varchar(255), primary key (id))
2021-07-21 18:13:22.790 INFO 224300 --- [ Test worker] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2021-07-21 18:13:22.798 INFO 224300 --- [ Test worker] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2021-07-21 18:13:23.156 INFO 224300 --- [ Test worker] .k.s.C.EmployeeRepositoryIntegrationTest : Started EmployeeRepositoryIntegrationTest in 2.444 seconds (JVM running for 3.43)
2021-07-21 18:13:23.180 INFO 224300 --- [ Test worker] o.s.t.c.transaction.TransactionContext : Began transaction (1) for test context [DefaultTestContext@382ca419 testClass = EmployeeRepositoryIntegrationTest, testInstance = com.kenychen.sprintcrudrest.Controller.EmployeeRepositoryIntegrationTest@aecb8e8, testMethod = whenFindByName_thenReturnEmployee@EmployeeRepositoryIntegrationTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@ac5130e testClass = EmployeeRepositoryIntegrationTest, locations = '{}', classes = '{class com.kenychen.sprintcrudrest.SprintCrudRestApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@1d84677e, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@3b6a052e, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@b5055a9f, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@3777b63, [ImportsContextCustomizer@1ab08501 key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@4cc7f642, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@7d258e97, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@0], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@355df7a0]; rollback [true]
Hibernate: call next value for hibernate_sequence
Hibernate: insert into employee (first_name, last_name, role, id) values (?, ?, ?, ?)
Hibernate: select employee0_.id as id1_1_, employee0_.first_name as first_na2_1_, employee0_.last_name as last_nam3_1_, employee0_.role as role4_1_ from employee employee0_
alex alex
2021-07-21 18:13:23.501 INFO 224300 --- [ Test worker] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test: [DefaultTestContext@382ca419 testClass = EmployeeRepositoryIntegrationTest, testInstance = com.kenychen.sprintcrudrest.Controller.EmployeeRepositoryIntegrationTest@aecb8e8, testMethod = whenFindByName_thenReturnEmployee@EmployeeRepositoryIntegrationTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@ac5130e testClass = EmployeeRepositoryIntegrationTest, locations = '{}', classes = '{class com.kenychen.sprintcrudrest.SprintCrudRestApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@1d84677e, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@3b6a052e, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@b5055a9f, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@3777b63, [ImportsContextCustomizer@1ab08501 key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@4cc7f642, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@7d258e97, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@0], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]
2021-07-21 18:13:23.512 INFO 224300 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2021-07-21 18:13:23.512 INFO 224300 --- [ionShutdownHook] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'
Hibernate: drop table if exists customer_order CASCADE
Hibernate: drop table if exists employee CASCADE
Hibernate: drop sequence if exists hibernate_sequence
BUILD SUCCESSFUL in 4s