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
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.
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.
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.
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.
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; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With