A Comprehensive guide to Spring Boot 3.2 with Java 21, Virtual Threads, Spring Security, PostgreSQL, Flyway, Caching, Micrometer, Opentelemetry, JUnit 5, RabbitMQ, Keycloak Integration, and More! (10/17) | by Jonathan Chevalier | Nov, 2023 | Medium
// Adds errors fields on validation errors, following RFC 9457 best practices. if (CollectionUtils.isNotEmpty(errors)) { problemDetail.setProperty("errors", errors); }
return problemDetail; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
{ "type":"about:blank", "title":"Bad Request", "status":400, "detail":"Validation failed.", "instance":"/management/companies", "errors":[ { "pointer":"name", "reason":"must not be blank" }, { "pointer":"slug", "reason":"must not be blank" } ] }
@ExceptionHandler(RootException.class) public ResponseEntity<ProblemDetail> rootException(final RootException ex) { log.info(ex.getMessage(), ex);
// Uses default message, can be adapted to use ex.getMessage(). finalProblemDetailproblemDetail= this.buildProblemDetail( ex.getHttpStatus(), API_DEFAULT_REQUEST_FAILED_MESSAGE, ex.getErrors());
{ "type": "about:blank", "title": "Internal Server Error", "status": 500, "detail": "Something went wrong. Please try again later or enter in contact with our service.", "instance": "/back-office/hello-world" }
/* * This cache implementation is only valid if the table is not * frequently updated since it will clear the cache at every update operation * If you want to be more performant you can use something like https://github.com/ms100/cache-as-multi * */ @NonNull @CacheEvict(cacheNames = CACHE_NAME, allEntries = true) @Override <S extendsCompany> List<S> saveAll(@NonNull Iterable<S> entities);
/* * This cache implementation is only valid if the table is not * frequently updated since it will clear the cache at every delete operation * If you want to be more performant you can use something like https://github.com/ms100/cache-as-multi * */ @CacheEvict(cacheNames = CACHE_NAME, allEntries = true) @Override voiddeleteAll(@NonNull Iterable<? extends Company> entities); }
Spring 提供了标准的缓存接口,即便是后续需要切换到 Redis,使用的 API 和注解都不会发生改变。
线程
Java21 后支持了虚拟线程,几乎可以无限的实现线程,在 Spring Boot 3.2 需要单独开启。
/* * Override default SimpleAsyncTaskExecutor to provide context propagation in @Async function * */ @Bean public TaskExecutor simpleAsyncTaskExecutor() { finalSimpleAsyncTaskExecutortaskExecutor=newSimpleAsyncTaskExecutor(); taskExecutor.setTaskDecorator(newContextPropagatingTaskDecorator()); return taskExecutor; } }