Наследование Spring Rest Controller - PullRequest
0 голосов
/ 22 февраля 2019

У меня есть типизированный абстрактный 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. Почему это происходит?
  2. Есть ли способ использовать конструктор или, по крайней мере, не реализовывать методы получения для каждого подкласса и для каждой службы, требуемойсуперкласс?

РЕДАКТИРОВАТЬ 1:

Я попытался воссоздать проблему, но предоставленный код работал нормально, как и предполагали люди.После вмешательства в упрощенный проект мне наконец удалось воспроизвести проблему.Фактическое условие для воспроизведения проблемы состоит в том, что метод конечной точки в суперклассе должен быть недоступен подклассу (пример: классы находятся в разных пакетах, а метод имеет видимость пакета).Это приводит к тому, что Spring создает прокси-класс extensionrBySpringCGLIB с полями, заполненными нулями.

Изменение методов суперкласса для обеспечения защищенной / публичной видимости решило проблему.

Ответы [ 3 ]

0 голосов
/ 22 февраля 2019

ваш пример в целом выглядит нормально, и внедрение зависимостей должно работать с пружиной.

Если вы хотите получить доступ к сервису, ссылка на который находится в родительском абстрактном классе, ссылка должна быть не закрытой, а защищенной.

-

Spring инициализирует все RestControllers как Singleton Beans в контексте приложения, внедряя существующие Service Beans, если нет компонента для внедрения приложения, запуск не удастся.Если вызывающая оконечная точка доступа обращается к контроллеру, который не имеет ссылки на службу, он не совпадает с тем, который был инициализирован со службой в нем (что я не знаю, как это может произойти), или что-то не так с вашей конфигурацией.

Надень это на git hub.

0 голосов
/ 26 февраля 2019

Никола,

Я не уверен, почему ваш код не работает в вашей системе, я создал те же классы в проекте, и он работает нормально, я даже добавил еще один тип Cookie, сервис и классы API..

Журнал SpringBoot (можно увидеть 4 инициализированных конечных точки):

2019-02-26 14:39:07.612  INFO 86060 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/chocolateCookies],methods=[POST]}" onto public org.springframework.http.ResponseEntity<cookie.ChocolateCookie> cookie.ChocolateCookieApi.create(cookie.ChocolateCookie)
2019-02-26 14:39:07.613  INFO 86060 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/chocolateCookies/{cookieId}],methods=[POST]}" onto public org.springframework.http.ResponseEntity<?> cookie.CookieApi.eatCookie(long)
2019-02-26 14:39:07.615  INFO 86060 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/oatmeal-raisin-cookie],methods=[POST]}" onto public org.springframework.http.ResponseEntity<cookie.OatmealRaisinCookie> cookie.OatmealRaisingCookieApi.create(cookie.OatmealRaisinCookie)
2019-02-26 14:39:07.615  INFO 86060 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/oatmeal-raisin-cookie/{cookieId}],methods=[POST]}" onto public org.springframework.http.ResponseEntity<?> cookie.CookieApi.eatCookie(long)

Тестирование контроллеров в почтальоне enter image description here

enter image description here

Как уже упоминалось @Domingo, в вашем приложении могут возникнуть проблемы с настройкой, поскольку с точки зрения OOP и Spring IoC ваш код выглядит нормально и работает без проблем.

ПРИМЕЧАНИЕ: Я использую эти контроллеры, используя SpringBoot 2.0.5, Java 8, Eclipse

. Я разместил свой проект в GitHub для справки.https://github.com/karl-codes/cookie-monster

Ура!

0 голосов
/ 22 февраля 2019

вы можете определить абстрактный метод в вашем абстрактном классе и автоматически подключить правильный сервис для каждой реализации:

public abstract class CookieApi<T extends Cookie> {

    protected abstract CookieService<T> getCookieService();

    @RequestMapping("/cookieId")
    public void eatCookie(@PathVariable long cookieId) {
        final T cookie = cookieService.findCookie(cookieId); // Cookie service is null
        this.getCookieService().eatCookie(cookie);
    }
}

@RestController
@RequestMapping("/chocolateCookies")
public class ChocolateCookieApi extends CookieApi<ChocolateCookie> {

    @Autowired
    private ChocolateCookie chocolateCookie;

    @Override
    protected CookieService<T> getCookieService() {
        return this.chocolateCookie;
    }

    @PostMapping
    public ResponseEntity<ChocolateCookie> create(@RequestBody ChocolateCookie dto) {
        // TODO Process DTO and store the cookie
        return ResponseEntity.ok(dto);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...