Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
191 views
in Technique[技术] by (71.8m points)

java - Generalizing the functionality of Spring's @ModelAttribute to more than just query parameters

Recently I was working on a little RESTful API using Spring and I came across the ModelAttribute annotation.

I noticed that there is some very interesting behavior associated with it, mainly the fact that you can stick it onto a method and it will get called before the handler for a given request is called, allowing you to do anything before data is bound to the arguments of your handler method.

One usage that comes to mind is default values:

@ModelAttribute("defaultEntity")
public Entity defaultEntity() {
  final var entity = new Entity();
  entity.setName("default name");
  return entity;
}

@PostMapping("/entity")
public Entity createNewEntity(@Valid @ModelAttribute("defaultEntity") Entity entity) {
  dao.saveEntity(entity);
  return entity;
}

In this case, when a POST request comes to /entity, the first thing that will happen is that defaultEntity will get called, creating an entity with some default values pre-filled. Then, Spring will bind the incoming data into it (potentially overwriting the defaults or keeping them as-is) and then pass it into the createNewEntity handler. This is actually pretty nice, IMO.

Another surprising fact is that the annotated method can actually take parameters in much the same way as the handler method. A simple way to do partial entity updates could be something like this:

// first fetch the original entity from the database
@ModelAttribute("originalEntity")
public Entity originalEntity(@PathVariable("id") long id ) {
  return dao.getEntity(id);
}

// then let Spring bind data to the entity and validate it
@PostMapping("/entity/{id}")
public Entity updateEntity(@Valid @ModelAttribute("originalEntity") Entity entity) {
  // and finally we save it
  dao.saveEntity(entity);
  return entity;
}

Again, this is surprisingly easy.

Even more surprising is that different model attributes can depend on each other, so you can have a complicated multi-stage monster if you want:

// first fetch the original entity from the database
@ModelAttribute("originalEntity")
public Entity originalEntity(@PathVariable("id") long id ) {
  return dao.getEntity(id);
}

// then let Spring bind data to the entity, validate it and do some processing to it
@ModelAttribute("boundAndValidatedEntity")
public Entity boundAndValidatedEntity(@Valid @ModelAttribute("originalEntity") Entity entity) {
  processEntity(entity);
  return entity;
}

// finally check that the entity is still valid and then save it
@PostMapping("/entity/{id}")
public Entity updateEntity(@Valid @ModelAttribute(value = "boundAndValidatedEntity", binding = false) Entity entity) {
  dao.saveEntity(entity);
  return entity;
}

Obviously not all of the model attributes have to be of the same type, some can depend on multiple arguments from different places. It's like a mini-DI container within a single controller.

However, there are some drawbacks:

  • as far as I can tell, it only works with query parameters and there is no way to make it work with other kinds of request parameters, such as the request body or path variables
  • all of the ModelAttribute-annotated methods within a single controller will always be called, which can
    • have a performance impact
    • be annoying to work with, since Spring will need to be able to gather all of the method's arguments (which may be impossible, for example when they reference a path variable that doesn't exist in the current request)

So, while ModelAttribute doesn't really seem too useful by itself because of these issues, I feel like the main idea behind it - essentially allowing you to control the construction of a method's parameter before it's bound/validated while being able to easily access other request parameters - is solid and could be very useful.

So, my question is simple - is there anything in Spring that would essentially act like ModelAttribute but without the drawbacks that I mentioned? Or maybe in some 3rd party library? Or maybe I could write something like this myself?

question from:https://stackoverflow.com/questions/66065892/generalizing-the-functionality-of-springs-modelattribute-to-more-than-just-que

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...