Question:
I need to register a custom interface that extended from JpaRepository. I found a way using bytebuddy, how to create an interface that extended from JpaRepository, but I didn’t find, how to register him as a Bean. AnnotationDescription name = AnnotationDescription.Builder.ofType(Column.class)
.define("name", "type")
.build();
AnnotationDescription id = AnnotationDescription.Builder.ofType(Id.class)
.build();
AnnotationDescription entity = AnnotationDescription.Builder.ofType(Entity.class)
.build();
AnnotationDescription table = AnnotationDescription.Builder.ofType(Table.class)
.define("name", "generated_view")
.build();
Class<?> type = new ByteBuddy()
.subclass(Object.class)
.name("GeneratedModelX")
.annotateType(entity)
.annotateType(table)
.defineField("id", Integer.class, Visibility.PRIVATE)
.annotateField(id)
.defineField("name", String.class, Visibility.PRIVATE)
.annotateField(name)
.defineMethod("getId", Integer.class, Visibility.PUBLIC)
.intercept(FieldAccessor.ofBeanProperty())
.defineMethod("setId", void.class, Visibility.PUBLIC)
.withParameter(Integer.class)
.intercept(FieldAccessor.ofBeanProperty())
.defineMethod("getName", String.class, Visibility.PUBLIC)
.intercept(FieldAccessor.ofBeanProperty())
.defineMethod("setName", void.class, Visibility.PUBLIC)
.withParameter(String.class)
.intercept(FieldAccessor.ofBeanProperty())
.make()
.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
AnnotationDescription repositoryAnnotation = AnnotationDescription.Builder.ofType(Repository.class)
.build();
TypeDescription.Generic genericType = TypeDescription.Generic.Builder
.parameterizedType(JpaRepository.class, type, Long.class)
.annotate(repositoryAnnotation)
.build();
Class<? extends Object> repository = new ByteBuddy()
.makeInterface()
.implement(genericType)
.name("CustomRepository")
.make()
.load(Controller.class.getClassLoader())
.getLoaded();
Answer:
Once you have properly created your custom entity and corresponding custom JPA repository – use ByteBuddy to transform the service class autowiring the newly loaded entity.builder = builder.defineField("customRepository", repo, Visibility.PUBLIC)
.annotateField(AnnotationDescription.Builder.ofType(Autowired.class)
.build());
Now, since this is an interface with us not providing any concrete implementation [ Spring will provide proper implementation ], we will need a BeanFactoryPostProcessor.@Slf4j
@Configuration
public class DynamicJpaBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition(JpaRepositoryFactoryBean.class).addConstructorArgValue("your.custom.repository.classname");
((DefaultListableBeanFactory) configurableListableBeanFactory).registerBeanDefinition("your.custom.repository.classname", beanDefinitionBuilder.getBeanDefinition());
log.info("Registered the {} bean for {} successfully", JpaRepositoryFactoryBean.class.getSimpleName(),
"your.custom.repository.classname");
}
}
Hopefully this helps you. Of-course you will need to transform your service class to add methods utilizing injected repository bean.If you have better answer, please add a comment about this, thank you!
Leave a Review