spring-projects / spring-data-rest

Simplifies building hypermedia-driven REST web services on top of Spring Data repositories
https://spring.io/projects/spring-data-rest
Apache License 2.0
916 stars 563 forks source link

custom RepositoryRestConfigurer not initialized when lazy-initialization is set to true #2238

Open ilseva opened 1 year ago

ilseva commented 1 year ago

Hi! We need to register custom converters from our repositories exposed as RepositoryRestResource. We implement a class, annotated with @Configuration, that extends the RepositoryRestConfigurer and we implement the configureConversionService method. When the application starts with spring.main.lazy-initialization at true the custom converters are not loaded.

Spring Boot version: 2.7.7

odrotbohm commented 1 year ago

Thanks for bringing this to our attention. Any chance you can provide a minimal reproducer (ideally Java, Maven, as few as possible, additional dependencies)?

spring-projects-issues commented 1 year ago

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

spring-projects-issues commented 1 year ago

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

Guschtel commented 6 months ago

I can confirm this. It also happened to us on Spring Boot 3.2.4 with

spring:
  main:
    lazy-initialization: true

The workaround ist to not load the relevant @Configurations lazily by adding @Lazy(false). I'm not sure if this is expected behaviour or not.

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(RepositoryRestProperties.class)
@Lazy(value = false)
public class OurRepositoryRestMvcConfiguration implements RepositoryRestConfigurer {

  private final BeanFactory beanFactory;
  private final Jackson2ObjectMapperBuilder objectMapperBuilder;
  private final RepositoryRestProperties properties;

  OurRepositoryRestMvcConfiguration(
    BeanFactory beanFactory,
    Jackson2ObjectMapperBuilder objectMapperBuilder,
    RepositoryRestProperties properties
  ) {
    this.beanFactory = beanFactory;
    this.objectMapperBuilder = objectMapperBuilder;
    this.properties = properties;
  }

  @Override
  public void configureConversionService(ConfigurableConversionService conversionService) {
    final var annotatedTypeScanner = new AnnotatedTypeScanner(Component.class);
    Set<Class<?>> types = annotatedTypeScanner.findTypes(
      "com.example.ourpackage.for.converters"
    );
    types.forEach(type -> {
      try {
        Object bean = beanFactory.getBean(Class.forName(type.getName()));
        if (bean instanceof Converter) {
          conversionService.addConverter((Converter<?, ?>) bean);
        }
      } catch (ClassNotFoundException e) {
         // log error
      }
    });
  }

  @Override
  public void configureRepositoryRestConfiguration(
    RepositoryRestConfiguration config,
    CorsRegistry cors
  ) {
    this.properties.applyTo(config);
  }

  @Override
  public void configureJacksonObjectMapper(ObjectMapper objectMapper) {
    this.objectMapperBuilder.configure(objectMapper);
  }
}