【问题标题】:Complex Spring Converter with Converter inside?内部带有转换器的复杂弹簧转换器?
【发布时间】:2013-06-26 22:55:38
【问题描述】:
我有一个复杂的对象,需要嵌套转换器说我有包含 A 实例的 B 类。
class A {
....
}
class B {
A a;
....
}
我为 A 编写了一个转换器,它可以转换为其他类,比如“AA”。
现在我需要编写一个转换器,将 B 也转换为其他类。因为B里面有A。我需要将 A 转换为另一个东西“AA”。
我正在使用转换器模式。
并看到了这个答案:
injecting ConversionService into a custom Converter
还有比这更好的方法吗?我不希望在自定义工厂类中初始化转换器。
【问题讨论】:
标签:
spring
spring-mvc
dependency-injection
【解决方案1】:
找到了不写自定义conversionService的解决方案,使用@Lazy注解解决循环依赖。
Configuration 类如:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
@Lazy
private ConversionService mConversionService;
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(aConverter());
registry.addConverter(bConverter());
}
@Bean
public AConverter aConverter() {
return new AConverter();
}
@Bean
public BConverter bConverter() {
return new BConverter(mConversionService);
}
}
AConverter 和 BConverter 都实现了org.springframework.core.convert.converter.Converter<S,T>,因为AConverter 将A 类转换为AA 和BConverter 将B 类转换为另一个包含A 的类。
更新
在WebConfig构造函数中注入ConversionService比较好,可以避免字段注入:
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final ConversionService mConversionService;
@Autowired
public WebConfig(@Lazy ConversionService conversionService) {
mConversionService = conversionService;
}
}
【解决方案2】:
与 BB 类类似:
class BB {
....
AA aa;
....
}
你可以这样做:
public BB convert(B b) {
BB bb = new BB();
bb.aa = new AToAAConverter().convert(b.a);
....
您仍然可以在ConversionServiceFactoryBean 中注册您的AToAAConverter。
【解决方案3】:
你可以做这样的事情。创建一个转换器服务并注入一组您声明的转换器 bean。在某些情况下,您的转换器可能需要访问 converterService。您可以使用 @Lazy 注释绕过循环引用。您可能需要@Primary 让spring 知道如果存在其他conversionServices,您声明的converterService 应该被优先注入。在某些情况下,让转换器可以访问转换服务很有用。您可以通过将转换服务从当前转换器内部分派到正确的转换器来保持松散耦合。此外,这允许您从其他转换器重复使用常见的转换器,从而消除代码重复。
/**
* @author vsutskever 3/22/18
**/
@Configuration
public class ConverterConfig {
/**
* Circular reference. This beans depends on Conversion service. Using @Lazy to resolve.
* @param service
* @return
*/
@Bean
public Converter dispositionMessageConverter(
@Lazy ConversionService service){
return new DispositionMessageRequestToOutgoingMessage(service);
}
/**
* Circular reference. This beans depends on Conversion service. Using @Lazy to resolve.
* @param service
* @return
*/
@Bean
public Converter contentMessageRequestToOutgoingMessageConverter(
@Lazy ConversionService service){
return new ContentMessageRequestToOutgoingMessageConverter(service);
}
@Bean
public Converter incomingRcsMessageToBaseMessageConverter(){
return new IncomingRcsMessageToBaseMessageConverter();
}
@Bean
public Converter contactToAddressConverter(){
return new ContactToAddressConverter();
}
@Bean
@Primary
public ConversionService conversionService(List<Converter> converters){
ConversionServiceFactoryBean factory = new ConversionServiceFactoryBean();
factory.setConverters(new HashSet<>(converters));
factory.afterPropertiesSet();
return factory.getObject();
}
}
【解决方案4】:
我一直在做类似的事情(使用 Spring 3.1.1),对 spring 来说相当新,但经过一些试验和错误,这对我有用。它支持嵌套转换器,并且转换器使用 spring 注释,而不必在 @Configuration 类中全部设置。
------ConversionFactoryBean.java-------
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.support.ConversionServiceFactoryBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.stereotype.Component;
@Component
@Qualifier("conversionService")
public class ConversionFactoryBean extends ConversionServiceFactoryBean {
// spring will inject beans by type
@Autowired
public Set <CustomConverter<?, ?>> customConverters;
@Override
public void afterPropertiesSet() {
// Set the custom converters (default converters will be handled by parent).
setConverters(customConverters);
// registers the custom converters
super.afterPropertiesSet();
// set the conversionService on the converters
final ConversionService conversionService = getObject();
for (Object converter : customConverters) {
((CustomConverter<?, ?>) converter).setConversionService(conversionService);
}
}
}
------CustomConverter.java-------
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
/**
* Base class of converters, providing access to conversionservice.
* @param <S> the source object to convert, which must be an instance of S
* @param <T> the converted object, which must be an instance of T
*/
public abstract class CustomConverter<S, T> implements Converter<S, T> {
private ConversionService conversionService;
/**
* constructor
*/
public CustomConverter() {
super();
}
public ConversionService getConversionService() {
return conversionService;
}
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
}
------ConsumerSummaryConverter.java-------
import org.springframework.stereotype.Component;
@Component
public class ConsumerSummaryConverter
extends CustomConverter <CustomerDetail, ConsumerSummary> {
public ConsumerSummaryConverter() {
super();
}
@Override
public ConsumerSummary convert(CustomerDetail source) {
// use nested converter
Individual ind = getConversionService().convert(source, Individual.class);
// do rest of conversion...
}
}
-----ServiceConfig.java------
@Configuration
@ComponentScan({"uk.co.my.custom.converter"})
public class ServiceConfig { // empty at the moment}
【解决方案5】:
假设您想将B 转换为BB 为什么不将Converter<A, AA> 注入BToBbConverter?
@Component
public class BToBbConverter implements Converter<B, BB> {
private final Converter<A, AA> aToAaConverter;
public BToBbConverter(Converter<A, AA> aToAaConverter) {
this.aToAaConverter = aToAaConverter;
}
@Nullable
@Override
public BB convert(B source) {
// use aToAaConverter here
}
}