内置转换器的复合弹簧转换器?

时间:2013-06-26 22:55:39

标签: spring spring-mvc dependency-injection

我有一个复杂的对象需要嵌套的转换器说我有B类,包括A的实例。

 class A {
    ....
   }

   class B {
    A a;
    ....
   }

我为A编写了一个转换器,转换为其他类别的“AA”。 现在我需要编写一个转换器,将B转换为其他类。由于B中有A。我需要将A转换为另一个“AA”。 我正在使用转换器模式。 并看到这个ans: injecting ConversionService into a custom Converter 有比这更好的方法吗?我宁愿不在自定义工厂类中初始化转换器。

5 个答案:

答案 0 :(得分:2)

你可以这样做。创建转换器服务并注入一组您声明的转换器bean。在某些情况下,转换器可能需要访问converterService。您可以使用@Lazy注释来绕过循环引用。如果存在其他转换服务,您可能需要@Primary让spring知道您声明的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();
  }

}

答案 1 :(得分:1)

使用BB类:

class BB {
  ....
  AA aa;
  ....
}

您可以这样做:

public BB convert(B b) {

  BB bb = new BB();
  bb.aa = new AToAAConverter().convert(b.a);
  ....

您仍然可以在AToAAConverter注册ConversionServiceFactoryBean

答案 2 :(得分: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);
    }
}

AConverterBConverter都实现org.springframework.core.convert.converter.Converter<S,T>,因为AConverter将类A转换为AA并且BConverter转换类{ {1}}转到另一个包含B的类。

更新

A构造函数中注入ConversionService更好,可以避免字段注入:

WebConfig

答案 3 :(得分:0)

假设您要将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
  }
}

答案 4 :(得分:0)

我一直在做类似的事情(使用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}