3

我一直在寻找一种方法来以某种方式减少在我的 Spring MVC 控制器中重复出现细微差异的代码量,但是到目前为止搜索 SO 问题只产生了一些没有任何令人满意的答案的问题。

我要删除的重复示例是这样的,其中用户创建页面和角色创建页面有相似之处:

  @RequestMapping(value = "user/create", method = RequestMethod.GET)
  public String create(@ModelAttribute("user") User user, BindingResult errors) {
    LOG.debug("Displaying user creation page.");
    return "user/create";
  }

  @RequestMapping(value = "role/create", method = RequestMethod.GET)
  public String create(@ModelAttribute("role") Role role, BindingResult errors) {
    LOG.debug("Displaying role creation page.");
    return "role/create";
  }

我想删除的一个稍微复杂的重复变体是发布创建表单的变体:

  @RequestMapping(value = "user/create", method = RequestMethod.POST)
  public String save(@ModelAttribute("user") User user, BindingResult errors) {
    LOG.debug("Entering save ({})", user);
    validator.validate(user, errors);
    validator.validatePassword(user, errors);
    validator.validateUsernameAvailable(user, errors);
    String encodedPassword = encoder.encode(user.getPassword());
    user.setPassword(encodedPassword);
    if (errors.hasErrors()) {
      return create(user, errors);
    } else {
      service.save(user);
    }
    return "redirect:/user/index/1";
  }

  @RequestMapping(value = "role/create", method = RequestMethod.POST)
  public String save(@ModelAttribute("role") Role role, BindingResult errors) {
    LOG.debug("Entering save({})", role);
    validator.validate(role, errors);
    if (errors.hasErrors()) {
      return create(role, errors);
    } else {
      service.save(role);
    }
    return "redirect:/index";
  }

此示例包括验证,如果正确则保存,如果事情没有按计划进行,则重定向到错误页面。

如何删除这个重复?

4

2 回答 2

1

Spring 使用您的处理程序方法参数类型从请求参数或主体创建类实例。因此,没有办法创建一个处理程序 ( @RequestMapping) 方法,该方法可以接受Object并检查它是 aRole还是 a User。(从技术上讲,您可以同时拥有两个参数,然后检查哪个不是null,但这是糟糕的设计)。

因此,您需要为每个处理程序方法。这是有道理的,因为即使逻辑相似,它仍然特定于您尝试创建的模型对象的确切类型。您执行不同的验证,调用不同的服务方法,并返回不同的视图名称。

我说你的代码很好。

于 2013-11-04T15:43:07.847 回答
0

以为我会提供我确定的解决方案,希望它可以帮助某人。我的 gf 建议我使用实体的名称作为控制器的路径变量,事实证明这为手头的问题提供了一个非常好的解决方案。

这两种方法现在看起来像这样:

@RequestMapping(value = "{entityName}/create", method = RequestMethod.GET)
public String create(@PathVariable("entityName") String entityName, @ModelAttribute("entity") BaseEntity entity, BindingResult errors) {
  LOG.debug("Displaying create page for entity named: [{}]", entityName);
  return handlerFactory.getHandler(entityName).getCreateView();
}

@RequestMapping(value = "{entityName}/create", method = RequestMethod.POST)
public String save(@PathVariable("entityName") String entityName, @ModelAttribute("entity") BaseEntity entity, BindingResult errors) {
  LOG.debug("Saving entity of type {}", entityName);
  CrudHandler handler = handlerFactory.getHandler(entityName);
  handler.getCreateValidator().validate(entity, errors);
  if (errors.hasErrors()) {
    return create(entityName, entity, errors);
  }
  handler.preSave(entity);
  handler.getService().save(entity);
  return "redirect:" + DASHBOARD_URL;
}

CrudHandler接口具有每个实体的实现,并为控制器提供它需要的实体特定类,例如服务和验证器。一个示例CrudHandler实现对我来说是这样的:

@Component
public class RoleCrudHandler implements CrudHandler {

  private static final String ENTITY_NAME = "role";
  public static final String CREATE_VIEW = "role/create";
  public static final String EDIT_VIEW = "role/edit";
  @Resource
  private RoleService roleService;
  @Resource
  private RoleValidator validator;
  @Resource
  private CrudHandlerFactory handlerFactory;

  @PostConstruct
  public void init() {
    handlerFactory.register(ENTITY_NAME, this);
  }

  @Override
  public GenericService getService() {
    return roleService;
  }

  @Override
  public Validator getCreateValidator() {
    return validator;
  }

  @Override
  public Validator getUpdateValidator() {
    return validator;
  }

  @Override
  public BaseEntity createEntity() {
    return new Role();
  }

  @Override
  public void preSave(BaseEntity entity) {
  }

  @Override
  public String getCreateView() {
    return CREATE_VIEW;
  }

  @Override
  public String getUpdateView() {
    return EDIT_VIEW;
  }
}

如果有人看到一些改进方法,请随时分享。希望这对某人有用。

于 2013-11-07T14:09:04.687 回答