在将字符串转换为一组对象的情况下,最好的 Spring 转换器策略是什么?

发布于 2024-09-06 14:32:46 字数 3464 浏览 1 评论 0原文

我的视图之一具有以下(简化的)形式:

<form:form commandName="entry" method="POST">
  <form:input type="text" path="name"/>
  <form:input type="text" path="tags" />
  <input type="submit" value="Submit"/>
</form:form>

它将绑定到以下 JavaBean:

public class Entry {
  private String name;
  private List<Tag> tags = new LinkedList<Tag>();

  // setters and getters omitted
}

因为我想使用 Spring 3 的所有新奇特性,所以我使用注释驱动控制器来接收POST 请求:

@Controller
@RequestMapping("/entry")
public class EntryController {

  @RequestMapping(method = RequestMethod.GET)
  public ModelAndView show() {
    ModelAndView mav = new ModelAndView("entry");
    mav.addObject(new Entry());
    return mav;
  }

  @RequestMapping(method = RequestMethod.POST)
  public String add(@ModelAttribute("entry") @Valid Entry entry, 
                    BindingResult result) {
    // check validation from Binding result
    // execute method on business beans: adding this entry to the system
    // return a view if correct
  }
}

如您所见,我需要将输入文本(看起来像 tag1, tag2, tag3)转换为 Tag 列表,定义如下:

public class Tag {
  private String name;

  // setter and getter omitted
}

有几种策略可以使用Spring 3.0:(

抱歉,帖子很长,问题以粗体显示

最简单的

编程新属性 tagsAsText 以将 getter/setter 作为字符串:

public class Entry {
  // ...

  public void setTagsAsText(String tags) {
    // convert the text as a list of tags 
  }

  public String getTagsAsText() {
    // convert list of tags to a text
  }
}

这种方法有两个缺点:

  • < b>我将转换逻辑包含在我的域对象中,这是一个问题吗?
  • 如果字符串出现错误,我可以在哪里访问 BindingResult >

使用 BeanInfo

我还可以为我的 bean 使用 BeanInfo:

public class EntryBeanInfo extends SimpleBeanInfo {

  public PropertyDescriptor[] getPropertyDescriptors() {
    try {
      @Override
      PropertyDescriptor tagsDescriptor = new PropertyDescriptor("tags", Entry.class) {
        @Override  
        public PropertyEditor createPropertyEditor(Object bean) {
                return new EntryTagListEditor(Integer.class, true);
            };
        };
        // omitting others PropertyDescriptor for this object (for instance name)
        return new PropertyDescriptor[] { tagListDescriptor };
    }
    catch (IntrospectionException ex) {
        throw new Error(ex.toString());
    }
  }
}

并声明一个转换器

public class EntryTagListEditor extends PropertyEditorSupport {

  public void setAsText(String text) {
    // convert the text to a list of Tag
  }

  public String getAsText() {
    // convert the list of Tag to a String
  }
}

这种方法也有两个缺点:

  • 每次添加/更改 Entry 类时,我都需要编辑 BeanInfo。或者有什么方法可以有一个简单的方法来定义我的 BeanInfo (例如“对于此属性,使用它,否则就像平常一样”
  • 我可以在哪里访问BindingResult 在字符串错误的情况下?

使用转换器

转换器使用 Java 5 的通用机制:

final class StringToTagList implements Converter<String, List<Tag>> {
  public List<Tag> convert(String source) {
    // convert my source to a list of Tag
  }
}

这种方法看起来更优雅,但仍然有两个缺点:

  • 似乎我重新定义了所有默认转换器如果我在 ConversionServiceFactoryBean 的属性中配置此转换器,有什么方法可以保留默认转换器?
  • (再次)我在哪里可以访问 BindingResult 如果字符串出现错误?

I have the following (simplified) form in one of my view:

<form:form commandName="entry" method="POST">
  <form:input type="text" path="name"/>
  <form:input type="text" path="tags" />
  <input type="submit" value="Submit"/>
</form:form>

Which is going to be bind to the following JavaBean:

public class Entry {
  private String name;
  private List<Tag> tags = new LinkedList<Tag>();

  // setters and getters omitted
}

because I want to take use all new fancy features of Spring 3, I'm using annotation-driven controller to receive the POST request:

@Controller
@RequestMapping("/entry")
public class EntryController {

  @RequestMapping(method = RequestMethod.GET)
  public ModelAndView show() {
    ModelAndView mav = new ModelAndView("entry");
    mav.addObject(new Entry());
    return mav;
  }

  @RequestMapping(method = RequestMethod.POST)
  public String add(@ModelAttribute("entry") @Valid Entry entry, 
                    BindingResult result) {
    // check validation from Binding result
    // execute method on business beans: adding this entry to the system
    // return a view if correct
  }
}

As you can see, I need to convert my input text (which look like tag1, tag2, tag3) as a list of Tag, define like this:

public class Tag {
  private String name;

  // setter and getter omitted
}

There is several strategies to do this with Spring 3.0:

(Sorry long post, questions are in bold)

The simplest

Programming a new property tagsAsText to have a getter/setter as String:

public class Entry {
  // ...

  public void setTagsAsText(String tags) {
    // convert the text as a list of tags 
  }

  public String getTagsAsText() {
    // convert list of tags to a text
  }
}

This approach has two drawbacks:

  • I include the conversion logic in my domain object, is it a problem ?
  • Where can i access to the BindingResult in the case of error in the string ?

Using BeanInfo

I can also use a BeanInfo for my bean:

public class EntryBeanInfo extends SimpleBeanInfo {

  public PropertyDescriptor[] getPropertyDescriptors() {
    try {
      @Override
      PropertyDescriptor tagsDescriptor = new PropertyDescriptor("tags", Entry.class) {
        @Override  
        public PropertyEditor createPropertyEditor(Object bean) {
                return new EntryTagListEditor(Integer.class, true);
            };
        };
        // omitting others PropertyDescriptor for this object (for instance name)
        return new PropertyDescriptor[] { tagListDescriptor };
    }
    catch (IntrospectionException ex) {
        throw new Error(ex.toString());
    }
  }
}

And declare one converter

public class EntryTagListEditor extends PropertyEditorSupport {

  public void setAsText(String text) {
    // convert the text to a list of Tag
  }

  public String getAsText() {
    // convert the list of Tag to a String
  }
}

This approach has also two drawbacks:

  • I need to edit my BeanInfo every time I add / change my Entry class. or is there any way to have a simple way to define my BeanInfo (like "for this property, use this, else just do as usual")
  • Where can i access to the BindingResult in the case of error in the string ?

Using Converter

Converter uses the generic mechanism of Java 5:

final class StringToTagList implements Converter<String, List<Tag>> {
  public List<Tag> convert(String source) {
    // convert my source to a list of Tag
  }
}

This approach looks more elegant but still two drawbacks:

  • It seems I redefine all default Converters if I configure this converter in the Property of ConversionServiceFactoryBean, is there any way to keep the default Converters ?
  • (again) Where can i access to the BindingResult in the case of error in the string ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦行七里 2024-09-13 14:32:46

这是一个经过深思熟虑的问题,即使它会吓跑大多数人:)

无论如何,我认为选项(2)是最接近实际解决方案的。我的第一个建议是将标签列表封装到它自己的模型类中。这将为数据绑定框架提供一个具体的注册类型,而 ListString 则过于笼统。

因此,您将拥有模型类:

public class Entry {
  private String name;
  private TagList tagList;
}


public class TagList {

   private final List<Tag> tags;

   public TagList(List<Tag> tags) {
      this.tags = tags;
   }

   public List<Tag> getTags() {
      return tags;
   }
}

然后您将拥有一个知道如何与 TagList 相互转换的 PropertyEditor

public class TagListEditor extends PropertyEditorSupport {

   @Override
   public void setAsText(String text) throws IllegalArgumentException {
      TagList tagList = // parse from the text value
      setValue(tagList);
   }

   @Override
   public String getAsText() {
      TagList tagList = (TagList) getValue();
      return tagList.toString(); // or whatever
   }
}

最后,您需要告诉控制器使用Converter:

@Controller
public class EntryController {

   @InitBinder
   public void initBinder(WebDataBinder binder) {
      binder.registerCustomEditor(TagList.class, new TagListEditor());
   }

   // request mappings here
}

我相当确定新的 Spring 3 Converter 框架会产生更优雅的解决方案,但我还没有弄清楚:)但是,我知道这种方法是有效的。

A well-considered question, even it'll scare most people off :)

Anyway, I think option (2) is the closest to a practical solution. My first suggestion is that you encapsulate the list of tags into its own model class. This will give the data binding framework a concrete type to register against, whereas List and String are much too general.

So you would have the model classes:

public class Entry {
  private String name;
  private TagList tagList;
}


public class TagList {

   private final List<Tag> tags;

   public TagList(List<Tag> tags) {
      this.tags = tags;
   }

   public List<Tag> getTags() {
      return tags;
   }
}

You then have a PropertyEditor that knows how to convert to and from a TagList:

public class TagListEditor extends PropertyEditorSupport {

   @Override
   public void setAsText(String text) throws IllegalArgumentException {
      TagList tagList = // parse from the text value
      setValue(tagList);
   }

   @Override
   public String getAsText() {
      TagList tagList = (TagList) getValue();
      return tagList.toString(); // or whatever
   }
}

And finally, you need to tell the controller to use the converter:

@Controller
public class EntryController {

   @InitBinder
   public void initBinder(WebDataBinder binder) {
      binder.registerCustomEditor(TagList.class, new TagListEditor());
   }

   // request mappings here
}

I'm fairly sure the new Spring 3 Converter framework would produce a more elegant solution, but I haven't figured it out yet :) This approach, however, I know works.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文