如何在弹簧启动中注册自定义转换器?

2022-09-02 14:09:13

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

我遇到的问题: 实例失败,因为它无法从 转换为 。BeanPropertyRowMapperjava.sql.Timestampjava.time.LocalDateTime

为了复制这个问题,我为这些类型实现了。org.springframework.core.convert.converter.Converter

public class TimeStampToLocalDateTimeConverter implements Converter<Timestamp, LocalDateTime> {

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

我的问题是:我如何使.TimeStampToLocalDateTimeConverterBeanPropertyRowMapper

更一般的问题是,如何注册我的转换器,以便使它们在系统范围内可用?

以下代码将我们带入初始化阶段: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();
}    

谢谢。


答案 1

所有自定义转换服务都必须在格式化程序注册表中注册。尝试创建新配置并通过实现 WebMvcConfigurer 注册转换服务

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

希望这有效。


答案 2

我将从 https://stackoverflow.com/a/72781591/140707 复制我的答案,因为我认为这两个问题是相似的(所以答案适用于两者)。


现有的答案对我不起作用:

  • 通过(或简单地注释转换器)仅在WebMvc上下文中起作用,我希望我的自定义转换器在任何地方都可用,包括对任何bean的注入。WebMvcConfigurerAdapter.addFormatters@Component@Value
  • 定义 Bean(via 或 )会导致 Spring Boot 将 Bean 工厂上的默认值替换为您定义的自定义 Bean,该自定义 Bean 可能基于 (in )。问题是Spring Boot添加了一些方便的转换器,例如 中的标准设置,因此通过替换它,您将丢失这些转换。如果您不使用它们,这可能不是您的问题,但这意味着该解决方案并不适合所有人。ConversionServiceConversionServiceFactoryBean@Bean@ComponentApplicationConversionServiceSpringApplicationDefaultConversionServiceAbstractApplicationContext.finishBeanFactoryInitializationStringToDurationConverterDefaultConversionService

我创建了以下类,它为我完成了这个技巧。它基本上将自定义转换器添加到 使用的实例(然后将其传递到 )。这样可以保持尽可能多的向后兼容性,同时仍将自定义转换器添加到正在使用的转换服务中。@ConfigurationConversionServiceEnvironmentBeanFactory

@Configuration
public class ConversionServiceConfiguration {

    @Autowired
    private ConfigurableEnvironment environment;

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

显然,如果您希望该过程更加自动化,则可以将自定义转换器列表自动连接到此配置类中,并循环访问它们以将其添加到转换服务中,而不是上面的硬编码方式。

要确保在实例化任何可能需要将转换器添加到 的 Bean 之前运行此配置类,请在 spring 应用程序的调用中添加它作为主要来源ConversionServicerun()

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

如果不这样做,它可能会起作用,也可能不起作用,这取决于你的类在 Spring Boot JAR 中的结束顺序,这决定了扫描它们的顺序。(我发现这一点很困难:它在使用Oracle JDK进行本地编译时有效,但在使用Azul Zulu JDK的CI服务器上不起作用。

请注意,为了在 s 中工作,我还必须将此配置类与我的 Spring Boot 应用程序类组合成:@WebMvcTest@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 {
   // ...
}

推荐