Inject repository in spring bean created programmatically

I'm trying to write custom JSF converter for entity classes. I use spring-data and spring-boot in my project.

Know i have a lot of classes looks like `this:

 @Component("myFirstConverter")
 class MyFirstConverter implements Converter {
    @Autowired
    private MyFirstRepository myFirstRepository;

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, 
                              String value) {
    return myFirstRepository.findOne(Long.parseLong(value));
    }
    //...

}

I created custom class EntityConverter. It works with CrudRepository. But i can't understand how to programmatically create array of beans and inject into each bean corresponded repository.

Know I tried to write @Configuration class with this code:

 @Configuration
 class MyConfig extends WebMvcConfigurerAdapter  
       @Bean
       public static BeanFactoryPostProcessor registerConverters(ApplicationContext context) {
    Map<String, Class<? extends CrudRepository>> converterBeans = new HashMap<>();
    converterBeans.put("firstEntity", firstEntityRepository.class);
    converterBeans.put("secondEntity", secondEntity.class);
    return factory -> {
        for (val entry : converterBeans.entrySet()) {
            CrudRepository bean = context.getBean(entry.getValue()
            factory.registerSingleton(entry.getKey()
            .concat("Converter"), new EntityConverter(
                    entry.getKey(), bean));
        }
    };

But it doesn't work. Application failed in initialization.

 19:07:59.546 [WARN ] o.s.c.support.AbstractApplicationContext:551  - 
    Exception encountered during context initialization - cancelling 
    refresh attempt: 
    org.springframework.beans.factory.BeanCreationException: Error 
    creating bean with name 'myFirstRepository': Cannot create inner bean 
    '(inner bean)#1fdfafd2' of type 
    [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting 
    bean property 'entityManager'; nested exception is 
    org.springframework.beans.factory.BeanCreationException: Error 
    creating bean with name '(inner bean)#1fdfafd2': Cannot resolve 
    reference to bean 'entityManagerFactory' while setting constructor 
    argument; nested exception is 
    org.springframework.beans.factory.BeanCreationException: Error 
    creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration$$EnhancerBySpringCGLIB$$fe46103]: No default constructor found; nested exception is java.lang.NoSuchMethodException: org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration$$EnhancerBySpringCGLIB$$fe46103.<init>()Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'fumoRepository': Cannot create inner bean '(inner bean)#1fdfafd2' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#1fdfafd2': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration$$EnhancerBySpringCGLIB$$fe46103]: No default constructor found; nested exception is java.lang.NoSuchMethodException: 
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfigurati 
    on$$EnhancerBySpringCGLIB$$fe46103.<init>()

UPD:

Answer WiPU allowed to solve the problem. I changed my registerConverters method like this:

 @Configuration
 class MyConfig extends WebMvcConfigurerAdapter  
       @Bean
       public static BeanFactoryPostProcessor 
     registerConverters(ApplicationContext context) {
        Map<String, Class<? extends CrudRepository>> converterBeans = new HashMap<>();
    converterBeans.put("firstEntity", firstEntityRepository.class);
    converterBeans.put("secondEntity", secondEntity.class);
    return factory -> {
        BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) 
        factory;
        for (val entry : converterBeans.entrySet()) {
              BeanDefinitionBuilder b =

   BeanDefinitionBuilder.genericBeanDefinition(EntityConverter.class)
                            .addPropertyReference("repository", entry.getKey().concat("Repository"))
                    ;
            beanFactory.registerBeanDefinition(entry.getKey().concat("Converter"), b.getBeanDefinition());
            factory.registerSingleton(entry.getKey()
            .concat("Converter"), new EntityConverter(
                    entry.getKey(), bean));
        }
    };

Upvotes: 1

Views: 1151

Answers (1)

WiPU
WiPU

Reputation: 443

Spring is failing to instantiate your Repository Beans according to your shown Exception.

A BeanFactoryPostProcessor is called after all bean definitions are loaded, but not yet instantiated. You can find details here: Link

You can try to register you beans later in the process when all other beans are already correctly instantiated. You can find some details here: Link

At this point all your Repository should be already correctly instantiated and you can just add your additional EntityConverter

As alternative, if you need to inject your EntityConverter into other beans, you can register BeanDefinitons. Then Spring will instantiate your bean when the repositories are already created. Link to JavaDoc

regards, WiPu

Upvotes: 1

Related Questions