Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to customize parameter names when binding Spring MVC command objects?

I have a command object:

public class Job {     private String jobType;     private String location; } 

Which is bound by spring-mvc:

@RequestMapping("/foo") public String doSomethingWithJob(Job job) {    ... } 

Which works fine for http://example.com/foo?jobType=permanent&location=Stockholm. But now I need to make it work for the following url instead:

http://example.com/foo?jt=permanent&loc=Stockholm 

Obviously, I don't want to change my command object, because the field names have to remain long (as they are used in the code). How can I customize that? Is there an option to do something like this:

public class Job {     @RequestParam("jt")     private String jobType;     @RequestParam("loc")     private String location; } 

This doesn't work (@RequestParam can't be applied to fields).

The thing I'm thinking about is a custom message converter similar to FormHttpMessageConverter and read a custom annotation on the target object

like image 590
Bozho Avatar asked Jan 24 '12 12:01

Bozho


People also ask

Does Spring MVC have customizable data binding?

In the last section, we saw how to bind data submitted by an HTML form or by query string parameters to a form-backing bean. In order to do the binding, Spring MVC internally uses a special binding object called WebDataBinder ( org. springframework.

What do you need to use in Spring MVC for binding a list of objects to a JSP view?

If you want to iterate the list, show the object fields and want to bind the List of objects again to modelAttribute then you can use the following JSP. In this JSP Spring form tags are used for Spring MVC form fields and for looping the List JSTL tag is used.

What is Formatter in Spring MVC?

Examples of formatters are DateFormatter , for parsing String to java. util. Date and formatting a Date . In addition, formatters' messages can be localized. Conclusion: formatters are suitable in the web environment, such as a Spring MVC application.

What is binding in Spring MVC?

Data binding is useful for allowing user input to be dynamically bound to the domain model of an application (or whatever objects you use to process user input). Spring provides the so-called DataBinder to do exactly that.


1 Answers

This solution more concise but requires using RequestMappingHandlerAdapter, which Spring use when <mvc:annotation-driven /> enabled. Hope it will help somebody. The idea is to extend ServletRequestDataBinder like this:

 /**  * ServletRequestDataBinder which supports fields renaming using {@link ParamName}  *  * @author jkee  */ public class ParamNameDataBinder extends ExtendedServletRequestDataBinder {      private final Map<String, String> renameMapping;      public ParamNameDataBinder(Object target, String objectName, Map<String, String> renameMapping) {         super(target, objectName);         this.renameMapping = renameMapping;     }      @Override     protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) {         super.addBindValues(mpvs, request);         for (Map.Entry<String, String> entry : renameMapping.entrySet()) {             String from = entry.getKey();             String to = entry.getValue();             if (mpvs.contains(from)) {                 mpvs.add(to, mpvs.getPropertyValue(from).getValue());             }         }     } } 

Appropriate processor:

/**  * Method processor supports {@link ParamName} parameters renaming  *  * @author jkee  */  public class RenamingProcessor extends ServletModelAttributeMethodProcessor {      @Autowired     private RequestMappingHandlerAdapter requestMappingHandlerAdapter;      //Rename cache     private final Map<Class<?>, Map<String, String>> replaceMap = new ConcurrentHashMap<Class<?>, Map<String, String>>();      public RenamingProcessor(boolean annotationNotRequired) {         super(annotationNotRequired);     }      @Override     protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest nativeWebRequest) {         Object target = binder.getTarget();         Class<?> targetClass = target.getClass();         if (!replaceMap.containsKey(targetClass)) {             Map<String, String> mapping = analyzeClass(targetClass);             replaceMap.put(targetClass, mapping);         }         Map<String, String> mapping = replaceMap.get(targetClass);         ParamNameDataBinder paramNameDataBinder = new ParamNameDataBinder(target, binder.getObjectName(), mapping);         requestMappingHandlerAdapter.getWebBindingInitializer().initBinder(paramNameDataBinder, nativeWebRequest);         super.bindRequestParameters(paramNameDataBinder, nativeWebRequest);     }      private static Map<String, String> analyzeClass(Class<?> targetClass) {         Field[] fields = targetClass.getDeclaredFields();         Map<String, String> renameMap = new HashMap<String, String>();         for (Field field : fields) {             ParamName paramNameAnnotation = field.getAnnotation(ParamName.class);             if (paramNameAnnotation != null && !paramNameAnnotation.value().isEmpty()) {                 renameMap.put(paramNameAnnotation.value(), field.getName());             }         }         if (renameMap.isEmpty()) return Collections.emptyMap();         return renameMap;     } } 

Annotation:

/**  * Overrides parameter name  * @author jkee  */  @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ParamName {      /**      * The name of the request parameter to bind to.      */     String value();  } 

Spring config:

<mvc:annotation-driven>     <mvc:argument-resolvers>         <bean class="ru.yandex.metrika.util.params.RenamingProcessor">             <constructor-arg name="annotationNotRequired" value="true"/>         </bean>     </mvc:argument-resolvers> </mvc:annotation-driven>  

And finally, usage (like Bozho solution):

public class Job {     @ParamName("job-type")     private String jobType;     @ParamName("loc")     private String location; } 
like image 82
jkee Avatar answered Oct 21 '22 17:10

jkee