【问题标题】:Two-way converter in spring弹簧两路转换器
【发布时间】:2012-06-12 12:15:32
【问题描述】:

Spring 3 有一个很好的特性,比如类型转换。它提供了一个转换器 SPI(Converter<S, T>) 用于实现不同的转换逻辑。 Converter 类型的子类允许定义单向转换(仅从 S 到 T),所以如果我也想从 T 到 S 执行转换,我需要定义另一个实现 Converter<T, S> 的转换器类。如果我有许多需要转换的类,我需要定义许多转换器。 是否可以在一个转换器中定义双向转换逻辑(从 S 到 T 和从 T 到 S)?以及如何使用?

PS。现在我通过ConversionServiceFactoryBean 在配置文件中定义/注入它们来使用我的转换器

【问题讨论】:

    标签: java spring-3


    【解决方案1】:

    你是对的,如果你想直接使用org.springframework.core.convert.converter.Converter接口,你需要实现两个转换器,每个方向一个。

    但是 spring 3 有几个其他选项:

    1. 如果您的转换不是对象到对象,而是对象到字符串(并返回),那么您可以改为实现 org.springframework.format.Formatter。格式化程序注册为 GenericConverters(请参阅 http://static.springsource.org/spring-webflow/docs/2.3.x/reference/html/ch05s07.html#converter-upgrade-to-spring-3

    2. 否则,您可以实现自己的 org.springframework.core.convert.converter.GenericConverter,这使得使用反射创建 TwoWayConverter 实现变得容易。

      public abstract class AbstractTwoWayConverter<S, T> implements GenericConverter {
      
          private Class<S> classOfS;
          private Class<T> classOfT;
      
          protected AbstractTwoWayConverter() {
              Type typeA = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
              Type typeB = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
              this.classOfS = (Class) typeA;
              this.classOfT = (Class) typeB;
          }
      
          public Set<ConvertiblePair> getConvertibleTypes() {
              Set<ConvertiblePair> convertiblePairs = new HashSet<ConvertiblePair>();
              convertiblePairs.add(new ConvertiblePair(classOfS, classOfT));
              convertiblePairs.add(new ConvertiblePair(classOfT, classOfS));
              return convertiblePairs;
          }
      
          public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
              if (classOfS.equals(sourceType.getType())) {
                  return this.convert((S) source);
              } else {
                  return this.convertBack((T) source);
              }
          }
      
          protected abstract T convert(S source);
      
          protected abstract S convertBack(T target);
      
      }
      
      /** 
       * converter to convert between a userId and user.
       * this class can be registered like so: 
       * conversionService.addConverter(new UserIdConverter (userDao));
       */ 
      public class UserIdConverter extends AbstractTwoWayConverter<String, User> {
      
          private final UserDao userDao;
      
          @Autowired
          public UserIdConverter(UserDao userDao) {
              this.userDao = userDao;
          }
      
          @Override
          protected User convert(String userId) {
              return userDao.load(userId);
          }
      
          @Override
          protected String convertBack(User target) {
              return target.getUserId();
          }
      }
      

    【讨论】:

      【解决方案2】:

      Spring 为此目的提供了这样一个接口:TwoWayConverter。 请参阅以下内容: http://static.springsource.org/spring-webflow/docs/2.0.x/javadoc-api/org/springframework/binding/convert/converters/TwoWayConverter.html

      【讨论】:

      • 它符合 spring 2.x 风格(没有泛型)。我在 spring 3 中还没有找到这个转换器。
      【解决方案3】:

      您可以使用Spring Formatter 将 T 类型的对象格式化为字符串,反之亦然。

      package org.springframework.format;
      
      public interface Formatter<T> extends Printer<T>, Parser<T> {
      }
      

      使用此接口,您可以实现与 Barry Pitman 所说的相同,但代码更少,如果您想格式化为字符串,这是 Spring 文档的首选方式,反之亦然。所以 Barry 的 UserIdConverter 类将如下所示:

      public class UserIdConverter implements Formatter<User> {
      
          private final UserDao userDao;
      
          @Autowired
          public UserIdConverter(UserDao userDao) {
              this.userDao = userDao;
          }
      
          @Override
          public User parse(String userId, Locale locale) {
              return userDao.load(userId);
          }
      
          @Override
          public String print(User target, Locale locale) {
              return target.getUserId();
          }
      }
      

      To register this Formatter 你应该在你的 XML 配置中包含这个:

      ...
      <mvc:annotation-driven conversion-service="conversionService"/>
      
      <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" >
          <property name="formatters">
              <set>
                  <bean class="com.x.UserIdConverter"/>
              </set>
          </property>
      </bean>
      ...
      

      !请注意,此类只能用于从某种类型 T 格式化为 String ,反之亦然。例如,您不能将类型 T 格式化为其他类型 T1。如果您遇到这种情况,您应该使用 Spring GenericConverter 并使用 Barry Pitman 的答案:

      public abstract class AbstractTwoWayConverter<S, T> implements GenericConverter {
      
          private Class<S> classOfS;
          private Class<T> classOfT;
      
          protected AbstractTwoWayConverter() {
              Type typeA = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
              Type typeB = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
              this.classOfS = (Class) typeA;
              this.classOfT = (Class) typeB;
          }
      
          public Set<ConvertiblePair> getConvertibleTypes() {
              Set<ConvertiblePair> convertiblePairs = new HashSet<ConvertiblePair>();
              convertiblePairs.add(new ConvertiblePair(classOfS, classOfT));
              convertiblePairs.add(new ConvertiblePair(classOfT, classOfS));
              return convertiblePairs;
          }
      
          public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
              if (classOfS.equals(sourceType.getType())) {
                  return this.convert((S) source);
              } else {
                  return this.convertBack((T) source);
              }
          }
      
          protected abstract T convert(S source);
      
          protected abstract S convertBack(T target);
      
      }
      
      /** 
       * converter to convert between a userId and user.
       * this class can be registered like so: 
       * conversionService.addConverter(new UserIdConverter (userDao));
       */ 
      public class UserIdConverter extends AbstractTwoWayConverter<String, User> {
      
          private final UserDao userDao;
      
          @Autowired
          public UserIdConverter(UserDao userDao) {
              this.userDao = userDao;
          }
      
          @Override
          protected User convert(String userId) {
              return userDao.load(userId);
          }
      
          @Override
          protected String convertBack(User target) {
              return target.getUserId();
          }
      }
      

      并添加到您的 XML 配置中:

      ...
      <mvc:annotation-driven conversion-service="conversionService"/>
      
      <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean" >
          <property name="converters">
              <set>
                  <bean class="com.x.y.UserIdConverter"/>
              </set>
          </property>
      </bean>
      ...
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-25
        • 1970-01-01
        • 1970-01-01
        • 2019-02-11
        • 2013-07-14
        相关资源
        最近更新 更多