Я использую Spring Boot (2.1.1) для автоматического создания HAL REST API моих интерфейсов JpaRepository.
В большинстве случаев эти интерфейсы пусты, например:
public interface ProjectRepository extends JpaRepository<Project, Long> {}
public interface ProtocolRepository extends JpaRepository<Protocol, Long> {}
Объект Project содержит множество объектов Protocol.
И объект Protocol имеет обратную ссылку на свой объект Project.
Когда я посещаю http://localhost:8080/admin/protocols/4711, я получаю ссылку на его проект:
...
"project": {
"href": "http://localhost:8080/admin/protocols/4711/project"
}
...
Но когда я перехожу по этой ссылке, все дальнейшие ссылки генерируются ошибочно:
...
"_links": {
"self": {
"href": "http://localhost:8080/admin/project/1"
},
"project": {
"href": "http://localhost:8080/admin/project/1"
}
...
}
...
Ошибка в ссылке заключается в том, что используется существительное единственного числа project вместо формы множественного числа projects.
Поскольку эти ссылки генерируются автоматически, неясно, как можно изменить это поведение.
Во время отладки внутренних компонентов Spring я понял, что PersistentEntityResourceAssembler использует экземпляр DefaultSelfLinkProvider для создания самостоятельных ссылок.
Когда я отлаживал этот класс, я понял, что он работает неправильно, когда для объекта, который проксируется Hibernate, создается собственная ссылка.
Поэтому я попытался заменить DefaultSelfLinkProvider собственной реализацией интерфейса SelfLinkProvider.
Это можно сделать с помощью BeanPostProcessor:
@Bean
public BeanPostProcessor entityManagerBeanPostProcessor()
{
return new BeanPostProcessor()
{
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
{
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
{
if (bean instanceof SelfLinkProvider)
{ return new HibernateSelfLinkProvider((SelfLinkProvider) bean); }
return bean;
}
};
}
А HibernateSelfLinkProvider — это простая оболочка над SelfLinkProvider:
public class HibernateSelfLinkProvider implements SelfLinkProvider
{
private final SelfLinkProvider selfLinkProvider;
public HibernateSelfLinkProvider(SelfLinkProvider selfLinkProvider)
{
this.selfLinkProvider = selfLinkProvider;
}
@Override
public Link createSelfLinkFor(Object instance)
{
instance = Hibernate.unproxy(instance);
return selfLinkProvider.createSelfLinkFor(instance);
}
}
Преимущество Hibernate.unproxy() в том, что он оставляет заданный объект без изменений, если он не является прокси-объектом.
С этим дополнением я получаю правильную ссылку: "http://localhost:8080/admin/projects/1".
Но я не уверен, что это лучшее место для изменения поведения.