【问题标题】:Spring ConversionService adding ConvertersSpring ConversionService 添加转换器
【发布时间】:2012-09-28 13:29:46
【问题描述】:

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

我想通过编写实现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,只将我的自定义转换器添加到转换器列表中,同时保留现有的?

提前致谢。

【问题讨论】:

    标签: java spring type-conversion


    【解决方案1】:

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

    【讨论】:

    • 查看WebMvcConfigurerAdapter 中的extendMessageConverters(List&lt;HttpMessageConverter&lt;?&gt;&gt; converters) 方法,将您的转换器添加到默认转换器之上。
    【解决方案2】:

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

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

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

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

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

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

    我很想知道为什么 Spring 没有在转换服务的注册表中找到我注册的自定义转换器(或者至少为什么 Spring 没有查看该注册表来查找自定义转换器)。我是否缺少任何配置?

    【讨论】:

      【解决方案3】:

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

      DefaultConversionService cs = new <YourClassThatInheritsFromDefaultConversionService or DefaultConversionService>();
      
      cs.addConverter(new MyConverter());
      

      【讨论】:

        【解决方案4】:

        使用 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;
            }
        }
        

        【讨论】:

        • 您可以注入某种类型的所有对象,在这种情况下为Converter,使用Spring:@Autowired Set&lt;Converter&gt;
        【解决方案5】:

        在 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>
        

        【讨论】:

        • 此解决方案似乎不再起作用:org.springframework.beans.NotWritablePropertyException:bean 类 [org.springframework.core.convert.support.DefaultConversionService] 的无效属性“转换器”:Bean 属性'converters' 不可写或具有无效的 setter 方法。 setter的参数类型和getter的返回类型是否匹配?
        【解决方案6】:

        重写 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);
                    }
                }
            }
        }
        

        【讨论】:

          【解决方案7】:

          为了解决任何循环依赖,这里是我遵循的步骤(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;
              }
          }
          

          【讨论】:

            【解决方案8】:

            编写自定义转换服务

            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>
            

            我想这会对你有所帮助...

            【讨论】:

              【解决方案9】:

              这个变体对我有用。 如果你使用 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);
                  }
              }
              

              【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2020-08-06
              • 1970-01-01
              • 1970-01-01
              • 2012-07-01
              • 2015-05-01
              • 2019-02-22
              • 2013-09-10
              • 2010-12-22
              相关资源
              最近更新 更多