У меня есть типизированный абстрактный RestController, который содержит некоторую общую логику для обработки всех объектов типа.Сервис для обработки предоставляется через конструктор.
Во время создания экземпляра bean-компонента подкласса оба конструктора вызываются с ненулевыми параметрами, и ненулевое утверждение суперкласса успешно прошло.
Вызов конечной точки API (путь URI является композицией путей подкласса и суперкласса) вызывает правильный метод с правильно определенными параметрами.Однако метод конечной точки генерирует исключение нулевого указателя, потому что предоставленная служба (та, которая прошла ненулевое утверждение) была нулевой.После проверки все свойства как подкласса, так и суперкласса компонента, метод которого был вызван, сообщают, что все свойства имеют значение null.
Вот упрощенный пример:
Модель:
public class Cookie {
public long id;
}
public class ChocolateCookie extends Cookie {
public long chipCount;
}
Служба:
public interface CookieService<T extends Cookie> {
T findCookie(long cookieId);
void eatCookie(T cookie);
}
@Service
public class ChocolateCookieService implements CookieService<ChocolateCookie> {
@Override
public ChocolateCookie findCookie(long cookieId) {
// TODO Load a stored cookie and return it.
return new ChocolateCookie();
}
@Override
public void eatCookie(ChocolateCookie cookie) {
// TODO Eat cookie;
}
}
Остальные контроллеры:
public abstract class CookieApi<T extends Cookie> {
private final CookieService<T> cookieService;
public CookieApi(CookieService<T> cookieService) {
this.cookieService = cookieService;
Assert.notNull(this.cookieService, "Cookie service must be set.");
}
@PostMapping("/{cookieId}")
public ResponseEntity eatCookie(@PathVariable long cookieId) {
final T cookie = cookieService.findCookie(cookieId); // Cookie service is null
cookieService.eatCookie(cookie);
return ResponseEntity.ok();
}
}
@RestController
@RequestMapping("/chocolateCookies")
public class ChocolateCookieApi extends CookieApi<ChocolateCookie> {
@Autowired
public ChocolateCookieApi(ChocolateCookieService cookieService) {
super(cookieService);
}
@PostMapping
public ResponseEntity<ChocolateCookie> create(@RequestBody ChocolateCookie dto) {
// TODO Process DTO and store the cookie
return ResponseEntity.ok(dto);
}
}
В качестве примечания, если вместо предоставления объекта службы суперклассу я определил абстрактный метод для получения службы по требованиюи реализовав его в подклассе, суперкласс будет функционировать как задумано.
Тот же принцип работает в любом случае, когда @RestController и @RequestMapping не включены в уравнение.
У меня вопрос два-fold:
- Почему это происходит?
- Есть ли способ использовать конструктор или, по крайней мере, не реализовывать методы получения для каждого подкласса и для каждой службы, требуемойсуперкласс?
РЕДАКТИРОВАТЬ 1:
Я попытался воссоздать проблему, но предоставленный код работал нормально, как и предполагали люди.После вмешательства в упрощенный проект мне наконец удалось воспроизвести проблему.Фактическое условие для воспроизведения проблемы состоит в том, что метод конечной точки в суперклассе должен быть недоступен подклассу (пример: классы находятся в разных пакетах, а метод имеет видимость пакета).Это приводит к тому, что Spring создает прокси-класс extensionrBySpringCGLIB с полями, заполненными нулями.
Изменение методов суперкласса для обеспечения защищенной / публичной видимости решило проблему.