Two-way converter in spring

强颜欢笑 提交于 2019-12-07 07:27:18

问题


Spring 3 has such a nice feature as type conversion. It provides a converter SPI(Converter<S, T>) to be used to implement differenet conversion logic. The subclass of Converter type allow to define one-way conversion(only from S to T), so if I want a conversion also to be performed from T to S I need to define another converter class that implement Converter<T, S>. If I have many classes which are subject to conversion, i need to define many converters. Is there any posibility to define two-way conversion logic(from S to T and from T to S) in one converter? and how it will be used?

PS. now I'm using my converters via ConversionServiceFactoryBean defining/injecting them in configuration file


回答1:


You are correct, if you want to use the org.springframework.core.convert.converter.Converter interface directly, you'll need to implement two converters, one for each direction.

But spring 3 has a couple of other options:

  1. If your conversion is not object-to-object but rather object-to-string (and back), then you can implement a org.springframework.format.Formatter instead. Formatters get registered as GenericConverters (see http://static.springsource.org/spring-webflow/docs/2.3.x/reference/html/ch05s07.html#converter-upgrade-to-spring-3)

  2. Otherwise you could implement your own org.springframework.core.convert.converter.GenericConverter, which makes it easy to create TwoWayConverter implementations using reflection.

    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 has just such an interface for this purpose: TwoWayConverter. see the following: http://static.springsource.org/spring-webflow/docs/2.0.x/javadoc-api/org/springframework/binding/convert/converters/TwoWayConverter.html




回答3:


You can use Spring Formatter to format object of type T to String and vice versa.

package org.springframework.format;

public interface Formatter<T> extends Printer<T>, Parser<T> {
}

Using this interface you can achieve the same as the Barry Pitman says but with less code and this is the preferable way by the Spring documentation if you waht to format to String and vice versa. So the Barry's UserIdConverter class is going to look like this:

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 you should include this in your XML config:

...
<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>
...

! NOTE that this class can be used only for formatting from some type T to String and vice versa. You can not format from type T to some other type T1 for example. If you have this case you should go with the Spring GenericConverter and use the Barry Pitman answer:

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();
    }
}

And add to your XML config:

...
<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>
...


来源:https://stackoverflow.com/questions/10996461/two-way-converter-in-spring

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!