Simple and short question: Is there a way to create a handler for custom #RequestParam types in Spring MVC?
I know I can register custom WebArgumentResolvers but then I cannot bind these to parameters. Let me describe my use case:
Consider I have defined a Model Class Account:
public class Account {
private int id;
private String name;
private String email;
}
My request handling method looks as follows:
#RequestMapping("/mycontroller")
public void test(Account account1, Account account2) {
//...
}
If I make a request mydomain.com/mycontroller?account1=23&account2=12 I would like to automatically load the Account objects from the database and return an error if they dont exist.
Yes, you should just register a custom property editor:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(CustomType.class,
new CustomTypePropertyEditor());
}
Update: Since you need to access the DAO, you need the property editor as a spring bean. Something like:
#Component
public class AccountPropertyEditor extends PropertyEditorSupport {
#Inject
private AccountDAO accountDao;
#Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(accountDao.getById(Integer.parseInt(text)));
}
#Override
public String getAsText() {
return String.valueOf(((Account) getValue()).getId());
}
}
And then, when registering the editor, get the editor via injection rather than instantiating it.
Related
In my Spring MVC application I have a 'DrawingController' that accepts MultipartHttpRequests from clients.
A user can upload any type of drawing( for the time being only auto cad and bim drawings) from the front end.
There is an interface called 'DrawingService' and two implementations 'BIMDrawingService' and 'CADDrawingService' as follows.
public interface DrawingService{
public String manageUpload();
}
#Component("bimService")
public class BIMDrawingService implements DrawingService{
public String manageUpload() {//}
}
#Component("cadService")
public class CADDrawingService implements DrawingService{
public String manageUpload() {//}
}
public class DrawingController {
#Autowired
#Qualifier("bimService")
private DrawingService bimService;
#Autowired
#Qualifier("cadService")
private DrawingService cadService;
public void setDrawingService(DrawingService bimService) {
this.bimService= bimService;
}
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public #ResponseBody String handleFileUpload(MultipartHttpServletRequest request){
//if request type == BIM, then ignore the logic how I differentiate
if bimrequest
bimService.manageupload()
else if cadrequest
cadService.manageupload()
}
I feel this is not the good way to do and my question is how I can inject the services dynamically at run time, even If I add new drawing services later, with the minimal changes I should be able to progress. Please suggest me some best design solution.
I created one factory to decide what best implementation should be returned, based in some conditional check.
// Factory
#Component
public class StoreServiceFactory {
#Autowired
private List<StoreService> storeServices;
public StoreService getService(){
if(isActiveSale){
return storeServices.get("PublicStoreService")
}
return storeServices.get("PrivateStoreService")
}
}
//Service Implementations
#Service
#Qualifier("PublicStoreService")
public class PublicStoreService implements StoreService {
public getStoreBalanceScore(){
Do Stuff....
}
}
#Service
#Qualifier("PrivateStoreService")
public class PrivateStoreService implements StoreService {
public getStoreBalanceScore(){
Do Stuff....
}
}
// Controller
#Autowired
StoreServiceFactory storeServiceFactory;
#Override
public StoreData getStoreBalance(String storeId) {
StoreService storeService = storeServiceFactory.getService();
return simulationService.simulate(sellerId, simulation);
}
Is this approach good? If yes, how can i get my service from an elegant way?
I would like to use only annotations, without configurations.
You should use a map instead of a List and pass a string parameter to the getService method.
public class StoreServiceFactory {
#Autowired
private Map<String,StoreService> storeServices = new HashMap<>();
public StoreService getService(String serviceName){
if(some condition...){
// want to return specific implementation on storeServices map, but using #Qualifier os something else
storeServices.get(serviceName)
}
}
}
You can prepopulate the map with supported implementations. You can then get an appropriate service instance as follows :
// Controller
#Autowired
StoreServiceFactory storeServiceFactory;
#Override
public StoreData getStoreBalance(String storeId) {
StoreService storeService = storeServiceFactory.getService("private");//not sure but you could pass storeId as a parameter to getService
return simulationService.simulate(sellerId, simulation);
}
If you don't like using Strings, you can define an enum for the supported implementations and use that as the key for your map.
You don't need to create a list or map on your code. You can retrieve it directly from Spring context using GenericBeanFactoryAccessor. This has various method to retrieve a specific bean like based on name, annotation etc. You can take a look at javadoc here. This avoids unnecessary complexity.
http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/beans/factory/generic/GenericBeanFactoryAccessor.html
Using Spring 3.1 and given this kind of thing:
class Thing {
public Thing() {}
public Thing(String someProperty) {}
}
class ThingEditor extends PropertyEditorSupport{
#Override
public void setAsText(String text) {
if (text != null) {
Thing thing = new Thing(text); // or by using a setter method
setValue(thing);
}
}
}
class SomeController {
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Thing.class, new ThingEditor());
}
}
I found that the registered property editor was not being called unless I removed the constructor that takes a String in Thing - is this right?
Why is it doing this and ignoring the registered editor and how can I make it stop doing this?
By introducing your own constructor, you disable the default constructor generated by compiler. Default constructor is probably required by the framework in order to be able to instantiate your Thing. If you really need your own constructor, you could also provide a version without any parameters for framework's use.
Lock the property name when you register the PropertyEditorSupport:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Thing.class, "someProperty", new ThingEditor());
}
I'm looking for a way to customize the default Spring MVC parameter binding. Take this method as an example:
#RequestMapping(value="/index.html")
public ModelAndView doIndex(#RequestParam String param) {
...
This is easy, when I have just a Stringthat I want to extract from the request. However, I want to populate a more complete object, so that my method looks like this:
#RequestMapping(value="/index.html")
public ModelAndView doIndex(Foo bar) {
...
What I'm looking for is some way to declare a binding like this;
#RequestMapping(value="/index.html")
public ModelAndView doIndex(#FooPopulator Foo bar) {
...
And have some other kind of implementor (determined by the #FooPopulator annotation) that does this:
public void doBind(Foo target, ServletRequest originalRequest) {
target.setX(this.computeStuffBasedOn(originalRequest));
target.sety(y);
}
So far I've found out about the #InitBinderbinder annotaion but I'm unsure whether that's really the right choice for this scenarion.
What's the best way?
It is very easy. You can use Converters (that work like one way PropertyEditors but are stateless).
See chapter 5.5 Spring 3 Type Conversion in Spring reference.
If such an converter is registered once, you do not need any additional information, you can simply use
#RequestMapping(value="/index.html")
public ModelAndView doIndex(#RequestParam Foo param) {
For example a simple converter that load an object by its id:
#Component
#CustomConverter //custom qualifyer
public class BUdToUserConverter implements Converter<String, User> {
#Resource
private UserDao userDao;
#Override
public User convert(String source) {
Integer id = Integer.parse(source);
return this.userDao.getByBusinessId(id);
}
}
A "helper" that registers all Beans with #CustomConverter anntoation
public class ApplicationConversionServiceFactoryBean extends FormattingConversionServiceFactoryBean {
#Resource
#CustomConverter
private List<Converter<?, ?>> customConverter;
#Override
protected void installFormatters(final FormatterRegistry registry) {
super.installFormatters(registry);
for (Converter<?, ?> converter : customConverter) {
registry.addConverter(converter);
}
}
}
How to use it
UserController {
...
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ModelAndView show(#PathVariable("id") User user) {
return new ModelAndView("users/show", "user", user);
}
}
just a quick thank you and the info, that I've found the "correct" solution to the problem. Spring already provides the WebArgumentResolver for this scenario.
http://sergialmar.wordpress.com/2011/03/29/extending-handler-method-argument-resolution-in-spring-mvc/
http://scottfrederick.blogspot.com/2011/03/customizing-spring-3-mvcannotation.html
I need to pass a UUID instance via http request parameter. Spring needs a custom type converter (from String) to be registered. How do I register one?
Please see chapter 5 of the spring reference manual here: 5.4.2.1. Registering additional custom PropertyEditors
I have an MVC controller with RequestMapping annotations. One method has a parameter of type UUID.
Thanks toolkit, after reading about WebDataBinder, I figured that I need a method like this in my controller:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(UUID.class, new UUIDEditor());
}
UUIDEditor simply extends PropertyEditorSupport and overrides getAsText() and setAsText().
Worked for me nicely.
In extenstion to the previous example.
Controller class
#Controller
#RequestMapping("/showuuid.html")
public class ShowUUIDController
{
#InitBinder
public void initBinder(WebDataBinder binder)
{
binder.registerCustomEditor(UUID.class, new UUIDEditor());
}
public String showuuidHandler (#RequestParam("id") UUID id, Model model)
{
model.addAttribute ("id", id) ;
return "showuuid" ;
}
}
Property de-munger
class UUIDEditor extends java.beans.PropertyEditorSupport
{
#Override
public String getAsText ()
{
UUID u = (UUID) getValue () ;
return u.toString () ;
}
#Override
public void setAsText (String s)
{
setValue (UUID.fromString (s)) ;
}
}
Not sure what you are asking?
Spring comes with a CustomEditorConfigurer to supply custom String <-> Object converters.
To use this, just add the CustomEditorConfigurer as bean to your config, and add the custom converters. However, these converters are typically used when converting string attributes in the config file into real objects.
If you are using Spring MVC, then take a look at the section on annotated MVC
Specifically, have a look at the #RequestParam and the #ModelAttribute annotations?
Hope this helps?