Spring Rest Controller继承

Nik*_*tić 9 spring spring-mvc spring-boot

我有一个类型化的抽象RestController,它包含一些用于处理该类型所有对象的通用逻辑.处理服务是通过构造函数提供的.

在子类的bean实例化期间,使用非null参数调用两个构造函数,并成功传递超类非null断言.

调用API端点(URI路径是子类和超类路径的组合)使用正确识别的参数调用正确的方法.但是,endpoint方法抛出空指针异常,因为提供的服务(传递非null断言的服务)为null.在检查其方法被调用的bean的子类和超类的所有属性时,将所有属性报告为null.

这是一个简化的例子:

模型:

public class Cookie {
    public long id;
}

public class ChocolateCookie extends Cookie {
    public long chipCount;
}
Run Code Online (Sandbox Code Playgroud)

服务:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

休息控制器:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

需要注意的是,如果不是向超类提供服务对象,而是定义了一个抽象方法,用于按需获取服务并在子类中实现它,超类将按预期运行.

在任何情况下,同样的原理都适用于@RestController和@RequestMapping不包括在等式中.

我的问题是双重的:

  1. 为什么会发生?
  2. 有没有办法使用构造函数,或者至少不必为每个子类和超类所需的每个服务实现getter方法?

编辑1:

我尝试重新创建问题,但提供的代码工作正常,正如人们建议的那样.在篡改简化项目后,我终于设法重现了这个问题.重现问题的实际条件是子类中的端点方法必须是子类不可访问的(例如:类在不同的包中,并且该方法具有包可见性).这会导致spring创建一个EnhancerBySpringCGLIB代理类,其中填充的字段为零.

修改超类方法以获得受保护/公共可见性解决了该问题.

Kar*_*arl 2

尼古拉,

我不知道为什么你的代码不能在你的系统中工作,我在一个项目中创建了相同的类并且它工作正常,我什至添加了另一个 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)
Run Code Online (Sandbox Code Playgroud)

在邮递员中测试控制器 在此输入图像描述

在此输入图像描述

正如 @Domingo 提到的,您的应用程序中可能存在一些配置问题,因为从 OOP 和 Spring IoC 的角度来看,您的代码看起来很好并且运行没有问题。

注意:我使用 SpringBoot 2.0.5、Java 8、Eclipse 运行这些控制器

我把我的项目发布在GitHub上供大家参考。 https://github.com/karl-codes/cookie-monster

干杯!

  • 正如人们所建议的那样,提供的代码运行良好。重现该问题的实际条件是子类必须无法访问超类中的端点方法(例如:类位于不同的包中并且该方法具有包可见性)。这会导致 spring 创建一个填充字段为零的增强器BySpringCGLIB 代理类。修改超类方法以具有受保护/公共可见性解决了该问题。接受这个答案,因为它在技术上是正确的,并且努力最终使我找到了最终的解决方案。 (4认同)