Spring 3有一个很好的功能,如类型转换。它提供了一个转换器SPI(Converter<S, T>
),用于实现差分转换逻辑。
Converter类型的子类允许定义单向转换(仅从S到T),因此如果我还希望从T到S执行转换,我需要定义另一个实现Converter<T, S>
的转换器类。如果我有许多可以转换的类,我需要定义许多转换器。
是否有可能在一个转换器中定义双向转换逻辑(从S到T和从T到S)?以及它将如何使用?
PS。现在我通过ConversionServiceFactoryBean
在配置文件中定义/注入它们来使用我的转换器
答案 0 :(得分:9)
你是对的,如果你想直接使用org.springframework.core.convert.converter.Converter
界面,你需要实现两个转换器,每个方向一个。
但是春季3还有其他两个选择:
如果您的转换不是对象到对象,而是对象到字符串(和返回),那么您可以实现org.springframework.format.Formatter
。格式化程序注册为GenericConverters(请参阅http://static.springsource.org/spring-webflow/docs/2.3.x/reference/html/ch05s07.html#converter-upgrade-to-spring-3)
否则,您可以实现自己的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>
...