简体   繁体   English

如何在 Spring Boot 中注册自定义转换器?

[英]How to register custom converters in spring boot?

I writing application using spring-boot-starter-jdbc (v1.3.0).我使用 spring-boot-starter-jdbc (v1.3.0) 编写应用程序。

The problem that I met: Instance of BeanPropertyRowMapper fails as it cannot convert from java.sql.Timestamp to java.time.LocalDateTime .我遇到的问题: BeanPropertyRowMapper的实例失败,因为它无法从java.sql.Timestamp转换为java.time.LocalDateTime

In order to copy this problem, I implemented org.springframework.core.convert.converter.Converter for these types.为了复制这个问题,我为这些类型实现了org.springframework.core.convert.converter.Converter

public class TimeStampToLocalDateTimeConverter implements Converter<Timestamp, LocalDateTime> {

    @Override
    public LocalDateTime convert(Timestamp s) {
        return s.toLocalDateTime();
    }
}

My question is: How do I make available TimeStampToLocalDateTimeConverter for BeanPropertyRowMapper .我的问题是:如何为BeanPropertyRowMapper提供TimeStampToLocalDateTimeConverter

More general question, how do I register my converters, in order to make them available system wide?更一般的问题,我如何注册我的转换器,以使它们在系统范围内可用?

The following code bring us to NullPointerException on initialization stage:以下代码将我们带到初始化阶段的NullPointerException

private Set<Converter> getConverters() {
    Set<Converter> converters = new HashSet<Converter>();
    converters.add(new TimeStampToLocalDateTimeConverter());
    converters.add(new LocalDateTimeToTimestampConverter());

    return converters;
}

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

Thank you.谢谢你。

All custom conversion service has to be registered with the FormatterRegistry.所有自定义转换服务都必须在 FormatterRegistry 中注册。 Try creating a new configuration and register the conversion service by implementing the WebMvcConfigurer尝试通过实现 WebMvcConfigurer 创建新配置并注册转换服务

@Configuration
public class WebConfig implements WebMvcConfigurer {
 
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new TimeStampToLocalDateTimeConverter());
    }
}

Hope this works.希望这有效。

I suggest to use @Autowired and the related dependency injection mechanism of spring to use a single ConversionService instance throughout your application.我建议使用 @Autowired 和 spring 的相关依赖注入机制在整个应用程序中使用单个 ConversionService 实例。 The ConversionService will be instantiated within the configuration. ConversionService 将在配置中实例化。

All Converters to be available application wide receive an annotation (eg @AutoRegistered).所有在应用程序范围内可用的转换器都会收到一个注释(例如@AutoRegistered)。 On application start a @Component FormatterRegistrar (Type name itself is a bit misleading, yes it is "...Registrar" as it does the registering. And @Component as it is fully spring managed and requires dependency injection) will receive @AutoRegistered List of all annotated Converters.在应用程序启动时,@Component FormatterRegistrar(类型名称本身有点误导,是的,它是“...Registrar”,因为它进行了注册。而@Component,因为它是完全弹簧管理的并且需要依赖注入)将收到@AutoRegistered List所有带注释的转换器。

See this thread for concrete implementation details .有关具体实现细节,请参阅此线程。 We use this mechanism within our project and it works out like a charm.我们在我们的项目中使用这种机制,它就像一个魅力。

org.springframework.web.servlet.config.annotation.WebMvcConfigurer or any on its implementation is one stop place for any kind of customization in spring boot project. org.springframework.web.servlet.config.annotation.WebMvcConfigurer或其实现是spring boot项目中任何类型定制的一站式场所。 It prvoides various methods, for your Converter requirement.它为您的转换器要求提供了各种方法。

Just create a new Converter by extending org.springframework.core.convert.converter.Converter<S, T> .只需通过扩展org.springframework.core.convert.converter.Converter<S, T>创建一个新的转换器。 Then register it with Spring by your class overriding method org.springframework.web.servlet.config.annotation.WebMvcConfigurer.addFormatters(FormatterRegistry)然后通过您的类覆盖方法org.springframework.web.servlet.config.annotation.WebMvcConfigurer.addFormatters(FormatterRegistry)将其注册到 Spring

Note there are Other types of Converter also which basically starts from ConditionalConverter.请注意,还有其他类型的转换器也基本上从 ConditionalConverter 开始。

I'll copy my answer from https://stackoverflow.com/a/72781591/140707 since I think the two questions are similar (so the answer applies to both).我将从https://stackoverflow.com/a/72781591/140707复制我的答案,因为我认为这两个问题相似(因此答案适用于两者)。


Existing answers didn't work for me:现有的答案对我不起作用:

  • Customizing via WebMvcConfigurerAdapter.addFormatters (or simply annotating the converter with @Component ) only works in the WebMvc context and I want my custom converter to be available everywhere , including @Value injections on any bean.通过WebMvcConfigurerAdapter.addFormatters进行自定义(或简单地使用@Component注释转换器)仅适用于 WebMvc 上下文,我希望我的自定义转换器在任何地方都可用,包括在任何 bean 上的@Value注入。
  • Defining a ConversionService bean (via ConversionServiceFactoryBean @Bean or @Component ) causes Spring Boot to replace the default ApplicationConversionService on the SpringApplication bean factory with the custom bean you've defined, which will probably be based on DefaultConversionService (in AbstractApplicationContext.finishBeanFactoryInitialization ).定义ConversionService bean(通过ConversionServiceFactoryBean @Bean@Component )会导致 Spring Boot 用您定义的自定义 bean 替换SpringApplication bean 工厂上的默认ApplicationConversionService ,这可能基于DefaultConversionService (在AbstractApplicationContext.finishBeanFactoryInitialization中)。 The problem is that Spring Boot adds some handy converters such as StringToDurationConverter to the standard set in DefaultConversionService , so by replacing it you lose those conversions.问题是 Spring Boot 向DefaultConversionService中的标准集添加了一些方便的转换器,例如StringToDurationConverter ,因此通过替换它会丢失这些转换。 This may not be an issue for you if you don't use them, but it means that solution won't work for everyone.如果您不使用它们,这对您来说可能不是问题,但这意味着该解决方案不适用于所有人。

I created the following @Configuration class which did the trick for me.我创建了以下@Configuration类,它对我有用。 It basically adds custom converters to the ConversionService instance used by Environment (which is then passed on to BeanFactory ).它基本上将自定义转换器添加到Environment使用的ConversionService实例(然后传递给BeanFactory )。 This maintains as much backwards compatibility as possible while still adding your custom converter into the conversion services in use.这保持尽可能多的向后兼容性,同时仍将您的自定义转换器添加到正在使用的转换服务中。

@Configuration
public class ConversionServiceConfiguration {

    @Autowired
    private ConfigurableEnvironment environment;

    @PostConstruct
    public void addCustomConverters() {
        ConfigurableConversionService conversionService = environment.getConversionService();
        conversionService.addConverter(new MyCustomConverter());
    }
}

Obviously you can autowire a list of custom converters into this configuration class and loop over them to add them to the conversion service instead of the hard-coded way of doing it above, if you want the process to be more automatic.显然,如果您希望该过程更加自动化,您可以将自定义转换器列表自动连接到此配置类中并循环它们以将它们添加到转换服务,而不是上面的硬编码方式。

To make sure this configuration class gets run before any beans are instantiated that might require the converter to have been added to the ConversionService , add it as a primary source in your spring application's run() call:为了确保在实例化任何可能需要将转换器添加到ConversionService的 bean 之前运行此配置类,请将其添加为 spring 应用程序的run()调用中的主要源

@SpringBootApplication
public class MySpringBootApplication {
    public static void main(String[] args) {
        SpringApplication.run(new Class<?>[] { MySpringBootApplication.class, ConversionServiceConfiguration.class }, args);
    }
}

If you don't do this, it might work, or not, depending on the order in which your classes end up in the Spring Boot JAR, which determines the order in which they are scanned.如果你不这样做,它可能会起作用,也可能不起作用,这取决于你的类最终在 Spring Boot JAR 中的顺序,这决定了它们被扫描的顺序。 (I found this out the hard way: it worked when compiling locally with an Oracle JDK, but not on our CI server which was using a Azul Zulu JDK.) (我发现这一点很困难:它在使用 Oracle JDK 本地编译时有效,但在我们使用 Azul Zulu JDK 的 CI 服务器上无效。)

Note that for this to work in @WebMvcTest s, I had to also combine this configuration class along with my Spring Boot application class into a @ContextConfiguration :请注意,要使其在@WebMvcTest中工作,我还必须将此配置类与我的 Spring Boot 应用程序类结合到@ContextConfiguration

@WebMvcTest(controllers = MyController.class)
@ContextConfiguration(classes = { MySpringBootApplication.class, ConversionServiceConfiguration.class })
@TestPropertySource(properties = { /* ... properties to inject into beans, possibly using your custom converter ... */ })
class MyControllerTest {
   // ...
}

Trying adding尝试添加

@Converter(autoApply = true)

Its needs to be placed over the convertor class.它需要放在转换器类上。 This works for me in case of Convertor needed for Localdate for interacting to DB.如果 Localdate 需要转换器来与数据库交互,这对我有用。

@Converter(autoApply = true)
public class LocalDateAttributeConverter implements AttributeConverter<LocalDate, Date> {

    @Override
    public Date convertToDatabaseColumn(LocalDate locDate) {
      return (locDate == null ? null : Date.valueOf(locDate));
    }

    @Override
    public LocalDate convertToEntityAttribute(Date sqlDate) {
      return (sqlDate == null ? null : sqlDate.toLocalDate());
    }
}

This is now applied automatically while interacting with DB.这现在在与 DB 交互时自动应用。

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

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