繁体   English   中英

Spring ConversionService 添加转换器

[英]Spring ConversionService adding Converters

我搜索了以下问题,但找不到答案。

我想通过编写实现org.springframework.core.convert.converter.Converter的自定义转换器来使用 spring 的转换服务。

然后我添加我的自定义转换器如下:

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <bean id="StringToLoggerConverter" class="com.citi.spring.converter.LoggerConverter" />
       </list>
   </property>
</bean>

执行上述操作时,我的应用程序初始化失败,因为我覆盖了 bean conversionService 并仅注册了我的自定义转换器。

我怎样才能不覆盖 conversionService 并只将我的自定义转换器添加到转换器列表,同时保留现有转换器?

提前致谢。

对于在问题最初发布 2 年以上现在通过谷歌搜索或类似方法addFormatters(FormatterRegistry)此问题的任何人,通过 Java Config 添加转换器变得更加容易: WebMvcConfigurerAdapter提供了addFormatters(FormatterRegistry)方法,该方法可用于指定额外的自定义转换器。

在尝试不同的方法时,甚至在某些方面遵循 spring 源代码时,我遇到了一件有趣的事情。

我发现使用conversionService 而不用我的自定义转换器覆盖现有转换器的唯一方法是扩展或重新实现conversionService 调用超类的afterPropertiesSet() 方法来注册默认转换器,然后添加自定义转换器。

但即使我使用这种方式,在运行时我也遇到了一个异常,即没有找到适合我的特定类型的转换器(例如,从 String 到 Logger)。

这引发了我的兴趣,我跟着 spring 源代码找出原因,我意识到 spring 试图找到一个在 PropertyEditor 注册的自定义转换器。 我不确定为什么会这样。 我必须在这里补充一点,我的应用程序没有使用 spring mvc,并且可能需要以某种方式注册转换服务,而我没有这样做。

最后,我解决了使用属性编辑器注册自定义转换器的问题。 本文档可作为参考:

http://static.springsource.org/spring/docs/current/spring-framework-reference/html/validation.html

我很想知道为什么 Spring 没有在 convertService 的注册表中找到我注册的自定义转换器(或者至少为什么 spring 没有查看该注册表来找到自定义转换器)。 我错过了任何配置吗?

您还可以使用 DefaultConversionService-ish 类上的 addConverter 方法动态添加它:

DefaultConversionService cs = new <YourClassThatInheritsFromDefaultConversionService or DefaultConversionService>();

cs.addConverter(new MyConverter());

使用 spring > 4 时,不再需要实现自己的 ConversionService 派生。 在 @Configuration 注释类中初始化它,如下所示:

@Configuration
public class ConversionServiceProvider
{
    @Autowired
    private MyConverterImpl myConverter;

    @Bean
    public ConversionService getConversionService()
    {
        ConversionServiceFactoryBean bean = new ConversionServiceFactoryBean();
        bean.setConverters( getConverters() );
        bean.afterPropertiesSet();
        ConversionService object = bean.getObject();
        return object;
    }

    private Set<Converter<?, ?>> getConverters()
    {
        Set<Converter<?, ?>> converters = new HashSet<Converter<?, ?>>();

        converters.add( myConverter );
        // add here more custom converters, either as spring bean references or directly instantiated

        return converters;
    }
}

在 Stackoverflow 中遇到了一种非常有趣的方法 - https://stackoverflow.com/a/12760579/204788

使用名为Collection merging的功能,您基本上可以这样做:

<bean id="customConversionService" class="org.springframework.context.support.ConversionServiceFactoryBean" parent="conversionService">
    <property name="converters">
        <list merge="true">
            <bean id="StringToLoggerConverter" class="com.citi.spring.converter.LoggerConverter" />
       </list>
   </property>
</bean>

覆盖 ConversionServiceFactoryBean.afterPropertiesSet() 并将 ConversionService 对象设置为您的转换器就足够了。 让您的转换器实现一些允许设置 ConversionService 对象的接口,比如 ConversionServiceAware。 唯一的问题是访问已注册的转换器,因此您还必须覆盖“setConverters()”方法。

public class MyFormattingConversionServiceFactoryBean extends FormattingConversionServiceFactoryBean {

    private Set<?> cachedConverters = new LinkedHashSet<>();

    @Override
    public void setConverters(Set<?> converters) {
        super.setConverters(converters);
        this.cachedConverters = new LinkedHashSet<>(converters);

    }

    @Override
    public void afterPropertiesSet() {
        super.afterPropertiesSet();
        FormattingConversionService conversionService = getObject();
        for (Object converter : cachedConverters) {
            if (converter instanceof ConversionServiceAware) {
                ((ConversionServiceAware) converter).setConversionService(conversionService);
            }
        }
    }
}

这个变体对我有用。 如果使用 java 配置,则可以将转换器添加到现有的GenericConversionService

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Autowired.html

Config methods may have an arbitrary name and any number of arguments; each of those arguments will be autowired with a matching bean in the Spring container. Bean property setter methods are effectively just a special case of such a general config method. Such config methods do not have to be public.

    @Configuration
    class MyConfig {

    @Autowired
    void conversionService(GenericConversionService genericConversionService) {
        genericConversionService.addConverter(String.class, UUID.class, UUID::fromString);
        genericConversionService.addConverter(String.class, DateTime.class, DateTime::parse);
        genericConversionService.addConverter(String.class, EnumState.class, EnumState::valueOf);
    }
}

为了解决任何循环依赖,这里是我遵循的步骤(Spring Boot v5.2.1):

注册一个简单的转换服务

@Configuration
public class ConverterProvider {

    @Bean
    public ConversionService conversionService() {
        ConversionService conversionService = new GenericConversionService();
        return conversionService;
    }
}

注入您的自定义转换器

@Component
public class ConvertersInjection {

    @Autowired
    private GenericConversionService conversionService;

    @Autowired
    private void converters(Set<Converter> converters) {
        converters.forEach(conversionService::addConverter);
    }
}

转换器甚至可以自动连接您的转换服务

@Component
public class PushNotificationConverter implements Converter<PushNotificationMessages.PushNotification, GCM> {
    @Lazy
    @Autowired
    private ConversionService conversionService;

    @Override
    public GCM convert(PushNotificationMessages.PushNotification source) {
        GCM gcm = new GCM();
        if (source.hasContent()) {
            PushNotificationMessages.PushNotification.Content content = source.getContent();
            if (content.hasData()) {
                conversionService.convert(content.getData(), gcm.getData().getClass());
            } else if (content.hasNotification()) {
                conversionService.convert(content.getNotification(), gcm.getNotification().getClass());
            }
        }
        return gcm;
    }
}

编写自定义转换服务

public class CustomerConversionServiceFactoryBean extends ConversionServiceFactoryBean {

    List<Converter<Object, Object>> converters = new ArrayList<>();

    public CustomerConversionServiceFactoryBean() {
        super();

    DefaultConversionService conversionservice = (DefaultConversionService)  super.getObject();

        for(int i=0;i<converters.size();i++){
            conversionservice.addConverter(converters.get(i));
        }
    }
}

然后改变你的xml

<bean id="conversionService"
    class="CustomerConversionServiceFactoryBean" >
    <property name="converters" >
        <list>
            <bean class="CustomConverter"  />
        </list>
    </property>
</bean>

我想这会帮助你...

我非常非常旧的代码在这个 class 中添加了转换器。为了解决我的问题,我不得不通过在同一类的addFormatters (覆盖)方法中添加额外的行来添加一个转换器。 WebMvcContext implements WebMvcConfigurer

线路:

@Override
public void addFormatters(FormatterRegistry registry) {
.....
registry.addConverter( SourceType.class, TargetType.class, converter);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM