弹簧双向转换器

时间:2012-06-12 12:15:33

标签: java spring-3

Spring 3有一个很好的功能,如类型转换。它提供了一个转换器SPI(Converter<S, T>),用于实现差分转换逻辑。 Converter类型的子类允许定义单向转换(仅从S到T),因此如果我还希望从T到S执行转换,我需要定义另一个实现Converter<T, S>的转换器类。如果我有许多可以转换的类,我需要定义许多转换器。 是否有可能在一个转换器中定义双向转换逻辑(从S到T和从T到S)?以及它将如何使用?

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

3 个答案:

答案 0 :(得分:9)

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

但是春季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();
        }
    }
    

答案 1 :(得分:3)

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

答案 2 :(得分:1)

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

package org.springframework.format;

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

使用此接口可以实现与Barry Pitman所说的相同但使用更少的代码,如果您要格式化为String,这是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>
...