Разделите controller и in-memory service. Все изменения списка и получение его снимка защищайте одним lock; наружу возвращайте неизменяемые записи, а не внутреннюю коллекцию.
Создание потокобезопасного REST‑сервиса для управления постами пользователей
Разделите controller и in-memory service. Все изменения списка и получение его снимка защищайте одним lock; наружу возвращайте неизменяемые записи, а не внутреннюю коллекцию.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Разработать REST‑API, которое реализует следующие возможности:
- Эндпоинт для сохранения нового сообщения от пользователя.
- Эндпоинт для получения ленты новостей: возвращает либо все сообщения, либо только сообщения конкретного автора по параметру
user_name.
Требования к реализации:
- Данные должны храниться в оперативной памяти (InMemory).
- Решение должно быть безопасным при работе из нескольких потоков.
- Придерживаться объектно‑ориентированных принципов при проектировании.
Структура входящего запроса:
-
Request:
user_name— уникальный идентификатор автора.message— текст сообщения.
Ответ
Разделите controller и in-memory service. Все изменения списка и получение его снимка защищайте одним lock; наружу возвращайте неизменяемые записи, а не внутреннюю коллекцию.
Минимальный пример для Java 17+/Spring MVC (imports опущены):
record Post(String user_name, String message) {}
@Service
class PostService {
private final List<Post> posts = new ArrayList<>();
synchronized Post add(Post post) {
posts.add(post);
return post;
}
synchronized List<Post> list(String author) {
return posts.stream()
.filter(p -> author == null || author.equals(p.user_name()))
.toList();
}
}
@RestController
@RequestMapping("/posts")
class PostController {
private final PostService service;
PostController(PostService service) { this.service = service; }
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
Post add(@RequestBody Post post) {
if (post == null || post.user_name() == null || post.user_name().isBlank()
|| post.message() == null || post.message().isBlank()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Пустые поля");
}
return service.add(post);
}
@GetMapping
List<Post> list(@RequestParam(name="user_name", required=false) String author) {
return service.list(author);
}
}
Гарантии действуют в одном процессе, данные теряются при рестарте. Для production дополнительно нужны лимит хранения, пагинация и получение автора из проверенной авторизации, а не доверие произвольному user_name.