Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Autoconfiguration for RestRepositoryResource

I would like to achieve autoconfiguration for a RestRepositoryResource (and some additional standard functionality) for an entity. I am trying to achieve it through an annotation on a @Configuration or @SpringBootApplication annotated class.

something like this:

@EnableRestRepo(single="foo", collection="foos",entity=Foo.class, id=String.class)
@SpringBootApplication
public class App{
    public void main(String[] args){
        SpringApplication.run(App.class,args);
    }
}


@Entity
public class Foo{
    String id;
    String bar;   
    ... getters & setters   
}

this should then setup a (or functionality similar too, I'm fine with creating my own endpoints if need be) @RestRepositoryResource like this:

@RestRepositoryResource(itemResourceRel = "foo", collectionResourceRel = "foos")
public interface Repo extends CrudRepository<Foo,String> {

    @RestResource(rel = "foo")
    Foo findOneById(@Param("id") String id);
}

The goal here is to reduce some boiler plate on configuring some basic functionality. Obviously this example will be extended with some more autoconfiguration stuff, but that should work in a similar way.

The question is not so much about the RestRepositoryResource as it is about autoconfiguration with annotations which require arguments and generic type classes. I would not mind spending some time implementing this, however I have no idea where to start.

Is something like this even possible and if so, how?

like image 947
p.streef Avatar asked Feb 03 '19 14:02

p.streef


1 Answers

Not sure if I understood you 100 %, but the example code here runs fine and creates beans runtime based on an annotation. Annotation also has som metadata.

The generic interface, will be proxied later:

public interface GenericRepository<T extends GenericType, Long> extends JpaRepository<GenericType, Long> {

}

Annotation to put on different entities:

@Target(ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@OverrideAutoConfiguration(enabled = false)
@ImportAutoConfiguration
@Import({RestResourceAutoConfiguration.class})
public @interface EnableRestRepo {
  Class<?> entity();
  String id();
}

A configuration class that can register beans in runtime:

@Configuration
@AutoConfigureAfter(value = WebMvcAutoConfiguration.class)
@ConditionalOnClass({CrudRepository.class})
public class RestResourceAutoConfiguration implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
      Reflections reflections = new Reflections("jav");
      Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(EnableRestRepo.class);
      for (Class<?> c : annotated) {
        EnableRestRepo declaredAnnotation = c.getDeclaredAnnotation(EnableRestRepo.class);
        Class<?> entity = declaredAnnotation.entity();
        String id = declaredAnnotation.id();
        Supplier<GenericRepository> genericRepositorySupplier = () -> (GenericRepository) Proxy.newProxyInstance( // register a proxy of the generic type in spring context
                c.getClassLoader(),
                new Class[]{GenericRepository.class},
                new MyInvocationHandler(entity));
            beanDefinitionRegistry.registerBeanDefinition(id + "-" + UUID.randomUUID().toString(),
                    new RootBeanDefinition(GenericRepository.class, genericRepositorySupplier)
        );
    }
}

spring.factories under META-INF

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
jav.RestResourceAutoConfiguration
like image 75
thomas77 Avatar answered Oct 16 '22 11:10

thomas77