1

I have been struggling with this issue for a while and have browsed a lot of articles, but couldnt figure out the solution. Appreciate your help with the issue below.

I need to be able to autowire the EntityManager in the service class, but throws an exception. The default constructor probably has issues because of type erasure and so I tried using a constructor with parameters to set the Type. How do I autowire EntityManager of type User?

public interface IEntityManager<T extends IDomain<ID>, ID extends Serializable> {

    T findById(ID id, boolean lock);

    T save(T entity);

    void delete(T entity);
}

public class EntityManager<T extends IDomain<ID>, ID extends Serializable> 
        implements IEntityManager<T, ID> {

    private Class<T> entity;

    @Autowired(required=true)
    private SessionFactory sessionFactory;

    /*
    @SuppressWarnings("unchecked")
    public EntityManager() {
        this.entity = (Class<T>) ((ParameterizedType) getClass()
                            .getGenericSuperclass()).getActualTypeArguments()[0];

    } */

    @Autowired(required=true)
    public EntityManager(Class<T> entity) {
        this.entity = entity;
    }
}

@Service("UserService")
@Transactional
public class UserServiceImpl implements IUserService {

    @Autowired  
    EntityManager<User, Integer> entityManager;
}

Here is the exception:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'UserService': Injection of autowired dependencies failed; 
nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.test.dummy.persistence.manager.EntityManager com.test.dummy.service.UserServiceImpl.entityManager; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.test.dummy.persistence.manager.EntityManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:292)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
forgivenson
  • 4,394
  • 2
  • 19
  • 28
mrbean
  • 33
  • 2
  • 7

2 Answers2

3

Spring cannot deduce that it need to Inject User.class in the constructor of EntityManager to satisfy the depdenency in UserServiceImpl.

If you are using Spring 4, You can to define a bean of type EntityManager<User, Integer>:

@Configuration
public class Config {
      @Bean 
      public EntityManager<User, Integer> userEntityManager() {
            new EntityManager(User.class);
      } 
}

Edit: Or define a BeanDefinitionRegistryPostProcessor that will check for beans that require EntityManager as dependency, identify the required type, construct the new EntityManager bean for the required type and add it to the bean registry.

 @Component
 public class MyEntityManagerBeanDefinitionRegistryPostProcessor implements
                                                       BeanDefinitionRegistryPostProcessor {


   @Override
   public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory)
       throws BeansException {
     for (String beanName : beanFactory.getBeanDefinitionNames()) {
       final BeanDefinition beanDefinition = getOriginatingBeanDefinition(
           beanFactory.getBeanDefinition(beanName));
       final Class<?> beanClass = getClass(beanDefinition.getBeanClassName());
       if (beanClass != null) {
         ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
           public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {

             if (field.isAnnotationPresent(Autowired.class) || field.isAnnotationPresent(
                 Inject.class)) {
               if (field.getGenericType() instanceof ParameterizedType) {
                 final ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
                 final Class<?> rawType = (Class) parameterizedType.getRawType();
                 if (rawType.equals(EntityManager.class)) {
                   final Class<?> typeArgument = (Class<?>) parameterizedType.getActualTypeArguments()[0];
                   beanFactory.registerSingleton(field.getName(), new EntityManager(typeArgument));
                 }
               }
             }
           }
         });
       }
     }
   }

   private Class<?> getClass(String className) throws BeansException {
     if (className != null) {
       try {
         return Class.forName(className);
       } catch (ClassNotFoundException e) {
         throw new BeanInitializationException("beanClass not found", e);
       }
     }
     return null;
   }

   private BeanDefinition getOriginatingBeanDefinition(BeanDefinition beanDefinition) {
     while(beanDefinition.getOriginatingBeanDefinition() != null) {
       beanDefinition = beanDefinition.getOriginatingBeanDefinition();
     }
     return beanDefinition;
   }


   @Override
   public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {  }
 }

Finally, it looks like what you actually need is to use Spring Data Jpa which, in essence, lets you define a parameterized DAO by defining an interface like this:

package com.myapp.repository; 

@Repository
public interface UserRepository extends JpaRepository<User, Long> {}

You enable it:

@Configuration
@EnableJpaRepositories(basePackages = "com.myapp.repository")
public class Config {


}

You then inject UserRepository:

@Autowired
private UserRepository userRepository;

And Spring will inject a basic DAO implementation.

Ricardo Veguilla
  • 3,107
  • 1
  • 18
  • 17
  • 1
    This is a great answer! However I think that you should mention that if there a multiple `EntityManager` beans, you need to either add `@Qualifier` along with `@Autowired`, or use Spring 4 which can deduce the correct bean based on the generic arguments. – geoand Aug 15 '14 at 18:36
  • Spring /can/ deduce types, but you need spring 4 – Software Engineer Aug 15 '14 at 18:37
  • @EngineerDollery Spring 4 can deduce type for injection against already define beans. It cannot deduce that it need to pass User.class to new EntityManager, unless you also define User.class as a bean. – Ricardo Veguilla Aug 15 '14 at 18:40
  • The problem is I would have 20+ such service classes. Would I have to define a configuration for each of those service classes? Is there a generic solution or is that what you mean by using a BeanPostProcessor? – mrbean Aug 15 '14 at 19:13
  • Yes, in theory you could implement a `BeanPostProcessor` to do that for you. Or you could use `Spring Data Jpa` which I think already provides what you are trying to implement (see updated answer). – Ricardo Veguilla Aug 15 '14 at 19:15
  • Unfortunately, I cannot use Spring Data JPA, but do you have an example of the BeanPostProcessor to give me an instance of a specific Type? I have a lot of service classes that needs to use the entity manager and would like to avoid creating repetitive code – mrbean Aug 15 '14 at 23:41
  • @mrbean Check the updated answer for the `BeanDefinitionRegistryPostProcessor` that provide the missing functionality. – Ricardo Veguilla Aug 20 '14 at 02:27
  • Thank you very much Ricardo for your help on this. I will try this out. – mrbean Aug 21 '14 at 13:20
-1

You might be missing @Component annotation on your EntityManager class.

The autowired constructor requires a class entity which is not injected.

@Autowired(required=true)
public EntityManager(Class<T> entity) {
    this.entity = entity;
}

try

@Autowired(required=true)
    public EntityManager() {

    }
Liam
  • 2,837
  • 23
  • 36
  • I tried that and I now get a different error:Unsatisfied dependency expressed through constructor argument with index 0 of type [java.lang.Class]: : No qualifying bean of type [java.lang.Class] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency – mrbean Aug 15 '14 at 17:51
  • There is no direct dependency for you Class type which is why its failing. – Liam Aug 15 '14 at 18:01
  • I didnt downvote it. I am not sure if someone else did it. I just added my comment..I apologize if I did something by mistake. – mrbean Aug 15 '14 at 19:09