繁体   English   中英

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

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

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

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

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

public class TimeStampToLocalDateTimeConverter implements Converter<Timestamp, LocalDateTime> {

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

我的问题是:如何为BeanPropertyRowMapper提供TimeStampToLocalDateTimeConverter

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

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

谢谢你。

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

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

希望这有效。

我建议使用 @Autowired 和 spring 的相关依赖注入机制在整个应用程序中使用单个 ConversionService 实例。 ConversionService 将在配置中实例化。

所有在应用程序范围内可用的转换器都会收到一个注释(例如@AutoRegistered)。 在应用程序启动时,@Component FormatterRegistrar(类型名称本身有点误导,是的,它是“...Registrar”,因为它进行了注册。而@Component,因为它是完全弹簧管理的并且需要依赖注入)将收到@AutoRegistered List所有带注释的转换器。

有关具体实现细节,请参阅此线程。 我们在我们的项目中使用这种机制,它就像一个魅力。

org.springframework.web.servlet.config.annotation.WebMvcConfigurer或其实现是spring boot项目中任何类型定制的一站式场所。 它为您的转换器要求提供了各种方法。

只需通过扩展org.springframework.core.convert.converter.Converter<S, T>创建一个新的转换器。 然后通过您的类覆盖方法org.springframework.web.servlet.config.annotation.WebMvcConfigurer.addFormatters(FormatterRegistry)将其注册到 Spring

请注意,还有其他类型的转换器也基本上从 ConditionalConverter 开始。

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


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

  • 通过WebMvcConfigurerAdapter.addFormatters进行自定义(或简单地使用@Component注释转换器)仅适用于 WebMvc 上下文,我希望我的自定义转换器在任何地方都可用,包括在任何 bean 上的@Value注入。
  • 定义ConversionService bean(通过ConversionServiceFactoryBean @Bean@Component )会导致 Spring Boot 用您定义的自定义 bean 替换SpringApplication bean 工厂上的默认ApplicationConversionService ,这可能基于DefaultConversionService (在AbstractApplicationContext.finishBeanFactoryInitialization中)。 问题是 Spring Boot 向DefaultConversionService中的标准集添加了一些方便的转换器,例如StringToDurationConverter ,因此通过替换它会丢失这些转换。 如果您不使用它们,这对您来说可能不是问题,但这意味着该解决方案不适用于所有人。

我创建了以下@Configuration类,它对我有用。 它基本上将自定义转换器添加到Environment使用的ConversionService实例(然后传递给BeanFactory )。 这保持尽可能多的向后兼容性,同时仍将您的自定义转换器添加到正在使用的转换服务中。

@Configuration
public class ConversionServiceConfiguration {

    @Autowired
    private ConfigurableEnvironment environment;

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

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

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

@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 服务器上无效。)

请注意,要使其在@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 {
   // ...
}

尝试添加

@Converter(autoApply = true)

它需要放在转换器类上。 如果 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());
    }
}

这现在在与 DB 交互时自动应用。

暂无
暂无

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

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