【发布时间】:2019-07-16 08:26:49
【问题描述】:
我有一个类型化的抽象 RestController,其中包含一些用于处理该类型的所有对象的通用逻辑。处理服务是通过构造函数提供的。
在子类的bean实例化过程中,两个构造函数都被非空参数调用,超类非空断言成功通过。
调用 API 端点(URI 路径是子类和超类路径的组合)调用正确的方法,并带有正确标识的参数。 但是,端点方法会引发空指针异常,因为提供的服务(通过非空断言的服务)为空。在检查其方法被调用的 bean 的子类和超类的所有属性后,报告所有属性为空。
这是一个简化的例子:
型号:
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 不包括在等式中的任何情况。
我的问题有两个:
- 为什么会这样?
- 有没有办法使用构造函数,或者至少不必为每个子类和超类所需的每个服务实现 getter 方法?
编辑 1:
我尝试重新创建问题,但提供的代码按照人们的建议运行良好。在篡改了简化的项目之后,我终于设法重现了这个问题。 重现问题的实际条件是子类必须无法访问超类中的端点方法(例如:类在不同的包中,并且该方法具有包可见性)。 这会导致 spring 创建一个带有零填充字段的 enhancerBySpringCGLIB 代理类。
修改超类方法以具有受保护/公共可见性解决了该问题。
【问题讨论】:
-
非常有趣的问题,这件事暴露得很好。
-
@davidxxx 虽然重现问题的回购也很好,因为显然人们无法重现问题
-
@eis 我同意根据实际答案/评论。 Nikola Antic,您确实应该添加更多信息或提供包含重现该问题的最小项目的 repo。
-
好吧,在阅读了您上次编辑的评论之后,这完全有道理,在您的问题中您错过了提及这些细节,并且由于您发布的所有课程方法都是公开的,我们没有抓住它。幸好你发现了发生了什么。编码愉快!
-
您可以添加自己的答案并接受它,因此这个问题至少会被标记为已回答。要么接受,要么接受本质上说您发布的代码可以正常工作的答案之一。
标签: spring spring-boot spring-mvc