背景 我正在使用Spring MVC创建一系列RESTful服务。当前,我具有以下控制器结构:
@RestController @RequestMapping(path = "myEntity", produces="application/json; charset=UTF-8") public class MyEntityController { @RequestMapping(path={ "", "/"} , method=RequestMethod.POST) public ResponseEntity<MyEntity> createMyEntity( @RequestBody MyEntity myEntity, @RequestHeader("X-Client-Name") String clientName) { myEntity.setClientName(clientName); //resto de declaración del método... } @RequestMapping(path={ "/{id}"} , method=RequestMethod.PUT) public ResponseEntity<MyEntity> updateMyEntity( @PathVariable Long id, @RequestBody MyEntity myEntity, @RequestHeader("X-Client-Name") String clientName) { myEntity.setClientName(clientName); //resto de declaración del método... } @RequestMapping(path={ "/{id}"} , method=RequestMethod.PATCH) public ResponseEntity<MyEntity> partialUpdateMyEntity( @PathVariable Long id, @RequestBody MyEntity myEntity, @RequestHeader("X-Client-Name") String clientName) { myEntity.setClientName(clientName); //resto de declaración del método... } }
可以看出,这三种方法对于标头是相同的参数,@RequestHeader("X-Client-Name") String clientName并且在每种方法中都采用相同的方式:myEntity.setClientName(clientName)。我将创建类似的控制器,对于POST,PUT和PATCH操作,它们将包含类似的代码,但面向其他实体。当前,大多数实体旨在通过父类支持此字段:
@RequestHeader("X-Client-Name") String clientName
myEntity.setClientName(clientName)
public class Entity { protected String clientName; //getters y setters ... } public class MyEntity extends Entity { //... }
注意,我使用拦截器来验证是否已根据请求设置标头。
题 如何避免在控制器和方法中重复相同的代码?有没有一种干净的方法可以实现这一目标,而无需代码重复?还是我注定要在将来的控制器的所有方法中重复参数和同一行代码?
可以使用class删除一些重复的代码org.springframework.web.bind.annotation.ModelAttribute。也就是说:
org.springframework.web.bind.annotation.ModelAttribute
@RestController @RequestMapping(path = "myEntity", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public class MyEntityController { @ModelAttribute("model") public MyEntity populate( @RequestBody MyEntity myEntity, @RequestHeader("X-Client-Name") String clientName) { myEntity.setClientName(clientName); return myEntity; } @RequestMapping(path = {"", "/"}, method = RequestMethod.POST) public ResponseEntity<MyEntity> createMyEntity( @ModelAttribute("model") MyEntity myEntity) { ⋮ } @RequestMapping(path = {"/{id}"}, method = RequestMethod.PUT) public ResponseEntity<MyEntity> updateMyEntity( @PathVariable Long id, @ModelAttribute("model") MyEntity myEntity) { ⋮ } @RequestMapping(path = {"/{id}"}, method = RequestMethod.PATCH) public ResponseEntity<MyEntity> partialUpdateMyEntity( @PathVariable Long id, @ModelAttribute("model") MyEntity myEntity) { ⋮ } }
该方法populate将在每次请求到该控制器调用与注释的方法之前被调用@RequestMapping。
由于始终在每个请求上执行此方法,因此即使对于那些不使用此类数据的方法,也需要有效负载和标头(”X-Client-Name”),并且如果它们未在请求中传播,则将引发异常。您可以使用属性修改该要求。也就是说:required = false
@ModelAttribute("model") public MyEntity populate( @RequestBody(required = false) MyEntity myEntity, @RequestHeader(required = false, value = "X-Client-Name") String clientName) { if (myEntity != null) { myEntity.setClientName(clientName); } return myEntity; }
基于@PaulVargas的答案和@jasilva的想法(使用继承),我能够针对自己的需求提出更强大的解决方案。简而言之,它分为两个部分:
对控制器使用基类。我将其称为“BaseController<E extends Entity>帖子”,这Entity是我实体的主要课程。在这个课堂上我将捕捉参数的值@RequestBody E entity,并@ModelAttribute在为他的答案解释通过@PaulVargas分配给它。泛型的力量进入这一部分。
BaseController<E extends Entity>
@RequestBody E entity
@ModelAttribute
在我的控制器,我将延长BaseController<ProperEntity>,其中ProperEntity的实体类,我需要的过程是如此的注入是自动的。
BaseController<ProperEntity>
ProperEntity
在这里,我显示了所描述设计的代码:
//1. public abstract class BaseController<E extends Entity> { @ModelAttribute("entity") public E populate( @RequestBody(required=false) E myEntity, @RequestHeader("X-Client-Name") String clientName) { if (myEntity != null) { myEntity.setCreatedBy(clientName); } return myEntity; } }
//2. @RestController @RequestMapping(path = "myEntity", produces="application/json; charset=UTF-8") public class MyEntityController extends BaseController<MyEntity> { @RequestMapping(path={ "", "/"} , method=RequestMethod.POST) public ResponseEntity<MyEntity> createMyEntity( @ModelAttribute("entity") MyEntity myEntity) { //resto de declaración del método... } @RequestMapping(path={ "/{id}"} , method=RequestMethod.PUT) public ResponseEntity<MyEntity> updateMyEntity( @PathVariable Long id, @ModelAttribute("entity") MyEntity myEntity) { //resto de declaración del método... } @RequestMapping(path={ "/{id}"} , method=RequestMethod.PATCH) public ResponseEntity<MyEntity> partialUpdateMyEntity( @PathVariable Long id, @ModelAttribute("entity") MyEntity myEntity) { //resto de declaración del método... } }