简体   繁体   English

Spring ConversionService 添加转换器

[英]Spring ConversionService adding Converters

I searched for the below problem, but couldn't find an answer.我搜索了以下问题,但找不到答案。

I want to use spring's conversion service by writing my custom converter that implements org.springframework.core.convert.converter.Converter .我想通过编写实现org.springframework.core.convert.converter.Converter的自定义转换器来使用 spring 的转换服务。

Then i add my custom converter as below:然后我添加我的自定义转换器如下:

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

When doing the above my application initialization fails, because i am overriding bean conversionService and registering only my custom converter.执行上述操作时,我的应用程序初始化失败,因为我覆盖了 bean conversionService 并仅注册了我的自定义转换器。

How can i not override the conversionService and only add my custom converter to the list of converters, at the same time keeping the existing ones?我怎样才能不覆盖 conversionService 并只将我的自定义转换器添加到转换器列表,同时保留现有转换器?

Thanks in advance.提前致谢。

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

While experimenting with different ways and even following spring source code in some i came across with an interesting thing.在尝试不同的方法时,甚至在某些方面遵循 spring 源代码时,我遇到了一件有趣的事情。

The only way i found to use conversionService without overriding the existing converters with my custom ones was either to extend or re-implement the conversionService calling the super class's afterPropertiesSet() method to register the default converters and then add the custom ones.我发现使用conversionService 而不用我的自定义转换器覆盖现有转换器的唯一方法是扩展或重新实现conversionService 调用超类的afterPropertiesSet() 方法来注册默认转换器,然后添加自定义转换器。

But even if i was using that way, at runtime i was getting an exception that no converter was found for my specific types (eg from String to Logger).但即使我使用这种方式,在运行时我也遇到了一个异常,即没有找到适合我的特定类型的转换器(例如,从 String 到 Logger)。

That triggered my interest and i followed spring source code to find out why and i realized that spring was trying to find a custom converter registered with PropertyEditor.这引发了我的兴趣,我跟着 spring 源代码找出原因,我意识到 spring 试图找到一个在 PropertyEditor 注册的自定义转换器。 I am not sure why this is happening.我不确定为什么会这样。 I have to add here that my application is not using spring mvc and conversionService might somehow need to be registered and i didn't do it.我必须在这里补充一点,我的应用程序没有使用 spring mvc,并且可能需要以某种方式注册转换服务,而我没有这样做。

Finally, i solved the issue with registering a custom converter using Property editor.最后,我解决了使用属性编辑器注册自定义转换器的问题。 This documentation can be viewed as reference:本文档可作为参考:

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

I would be very interested in knowing why Spring was not finding my registered custom converters in the conversionService's registry (or at least why spring was not looking at that registry to find the custom converters).我很想知道为什么 Spring 没有在 convertService 的注册表中找到我注册的自定义转换器(或者至少为什么 spring 没有查看该注册表来找到自定义转换器)。 Was i missing any configuration?我错过了任何配置吗?

You can also add it dinamically using addConverter method on your DefaultConversionService-ish class:您还可以使用 DefaultConversionService-ish 类上的 addConverter 方法动态添加它:

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

cs.addConverter(new MyConverter());

With spring > 4 it is no more necessary to implement an own derivation of the ConversionService.使用 spring > 4 时,不再需要实现自己的 ConversionService 派生。 Initialize it in a @Configuration annotated class as follows:在 @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;
    }
}

Came across a very interesting way to do this in Stackoverflow - https://stackoverflow.com/a/12760579/204788在 Stackoverflow 中遇到了一种非常有趣的方法 - https://stackoverflow.com/a/12760579/204788

Using a feature called Collection merging , you can essentially do this:使用名为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>

It's enough to override the ConversionServiceFactoryBean.afterPropertiesSet() and set the ConversionService object to your converters there.覆盖 ConversionServiceFactoryBean.afterPropertiesSet() 并将 ConversionService 对象设置为您的转换器就足够了。 Let your converters implement some interface that allows to set a ConversionService object, say ConversionServiceAware.让您的转换器实现一些允许设置 ConversionService 对象的接口,比如 ConversionServiceAware。 The only problem is to get access to the registered converters, so you'll also have to override the 'setConverters()' method.唯一的问题是访问已注册的转换器,因此您还必须覆盖“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);
            }
        }
    }
}

This variant works for me.这个变体对我有用。 If you use java configuration, you can add your converters to existing GenericConversionService如果使用 java 配置,则可以将转换器添加到现有的GenericConversionService

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Autowired.html 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);
    }
}

To resolve any circular dependencies, here the steps I followed (Spring Boot v5.2.1):为了解决任何循环依赖,这里是我遵循的步骤(Spring Boot v5.2.1):

Register a simple conversionService注册一个简单的转换服务

@Configuration
public class ConverterProvider {

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

Inject your custom converters注入您的自定义转换器

@Component
public class ConvertersInjection {

    @Autowired
    private GenericConversionService conversionService;

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

The converter can even autowire your conversionService转换器甚至可以自动连接您的转换服务

@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;
    }
}

write a custom conversionService编写自定义转换服务

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

then change your xml然后改变你的xml

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

I think this will help you...我想这会帮助你...

My very very old code had the converters added in this class. To fix my issue I had to add one more converter just by adding extra lines in the same class's addFormatters (overridden) method.我非常非常旧的代码在这个 class 中添加了转换器。为了解决我的问题,我不得不通过在同一类的addFormatters (覆盖)方法中添加额外的行来添加一个转换器。 WebMvcContext implements WebMvcConfigurer

Lines:线路:

@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