Tom Bradshaw
Tom Bradshaw

Reputation: 31

No Qualifying Bean of Type in Pure Java Spring Servlet Configuration

I'm trying to build a small Spring application in pure java using servlets 3 and I'm having trouble with my @Autowired annotations:

Here's the stack trace:

[main] INFO org.springframework.web.servlet.DispatcherServlet - FrameworkServlet 'dispatcher': initialization started
[main] INFO org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Refreshing WebApplicationContext for namespace 'dispatcher-servlet': startup date [Thu Jun 25 21:33:25 BST 2015]; root of context hierarchy
[main] INFO org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Registering annotated classes: [class com.foo.shop.ServletContext]
[main] WARN org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'productController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.foo.shop.database.dao.ProductDao com.foo.shop.webapp.controller.ProductController.productDao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.foo.shop.database.dao.ProductDao] 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:334)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1210)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:755)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
    at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:663)
    at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:535)
    at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:489)
    at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
    at javax.servlet.GenericServlet.init(GenericServlet.java:242)
    at org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:492)
    at org.eclipse.jetty.servlet.ServletHolder.doStart(ServletHolder.java:312)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
    at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:777)
    at org.eclipse.jetty.servlet.ServletHandler.updateMappings(ServletHandler.java:1227)
    at org.eclipse.jetty.servlet.ServletHandler.setServletMappings(ServletHandler.java:1286)
    at org.eclipse.jetty.servlet.ServletHandler.addServletMapping(ServletHandler.java:886)
    at org.eclipse.jetty.servlet.ServletHolder$Registration.addMapping(ServletHolder.java:657)
    at org.springframework.web.servlet.support.AbstractDispatcherServletInitializer.registerDispatcherServlet(AbstractDispatcherServletInitializer.java:97)
    at org.springframework.web.servlet.support.AbstractDispatcherServletInitializer.onStartup(AbstractDispatcherServletInitializer.java:68)
    at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:175)
    at org.eclipse.jetty.plus.annotation.ContainerInitializer.callStartup(ContainerInitializer.java:100)
    at org.eclipse.jetty.annotations.ServletContainerInitializerListener.contextInitialized(ServletContainerInitializerListener.java:99)
    at org.eclipse.jetty.server.handler.ContextHandler.callContextInitialized(ContextHandler.java:764)
    at org.eclipse.jetty.servlet.ServletContextHandler.callContextInitialized(ServletContextHandler.java:406)
    at org.eclipse.jetty.server.handler.ContextHandler.startContext(ContextHandler.java:756)
    at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:242)
    at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1221)
    at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:699)
    at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:454)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
    at org.eclipse.jetty.server.handler.HandlerWrapper.doStart(HandlerWrapper.java:90)
    at org.eclipse.jetty.server.Server.doStart(Server.java:263)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
    at runjettyrun.Bootstrap.main(Bootstrap.java:80)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.foo.shop.database.dao.ProductDao com.foo.shop.webapp.controller.ProductController.productDao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.foo.shop.database.dao.ProductDao] 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$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:561)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
    ... 40 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.foo.shop.database.dao.ProductDao] 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.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1301)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1047)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
    ... 42 more

Here's my initialiser:

package com.foo.shop;

public class ApplicationInitialiser extends AbstractDispatcherServletInitializer implements WebApplicationInitializer {

  @Override
  protected WebApplicationContext createRootApplicationContext() {
    AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
    applicationContext.register(ApplicationContext.class);
    return applicationContext;
  }

  @Override
  protected WebApplicationContext createServletApplicationContext() {
    AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
    applicationContext.register(ServletContext.class);
    return applicationContext;
  }

  @Override
  protected String[] getServletMappings() {
    return new String[] { "/*" };
  }
}

Here's the applicationContext:

package com.foo.shop;

import com.foo.shop.database.dao.ProductDao;
import com.foo.shop.database.jpa.JpaProductDao;

@Configuration
@ComponentScan(basePackages = {"com.foo.shop.database.jpa"})
public class ApplicationContext {

  public static final String PERSISTENCE_UNIT = "com.foo.shop.database";

  @Bean
  public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
      LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
      return entityManagerFactoryBean;
  }

  @Bean
  public JpaTransactionManager JpaTransactionManager() {
      JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
      return jpaTransactionManager;
  }

  @Bean 
  ProductDao getProductDao() {
    return new JpaProductDao();
  }
}

The servlet context:

package com.foo.shop;

@Configuration
@EnableWebMvc
@ComponentScan({
  "com.foo.shop.webapp.controller",
  "com.foo.shop.webapp.rest"})
public class ServletContext extends WebMvcConfigurerAdapter {

  @Override
  public void configureViewResolvers(ViewResolverRegistry registry) {
    InternalResourceViewResolver jspResolver = new InternalResourceViewResolver();
    jspResolver.setViewClass(JstlView.class);
    registry.viewResolver(jspResolver);
  }

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/static/**").addResourceLocations("/WEB-INF/static/");
    registry.addResourceHandler("/template/**").addResourceLocations("WEB-INF/static/template/");
    registry.addResourceHandler("/css/**").addResourceLocations("/WEB-INF/css/");
    registry.addResourceHandler("js/**").addResourceLocations("WEB-INF/js/vendor/");
  }
}

The GenericDao interface:

package com.foo.shop.database;

import java.io.Serializable;
import java.util.List;

public interface GenericDao<T, PK extends Serializable> {

  public T getById(PK id);

  public List<T> getAll();

  public void update(T entity);

  public void save(T entity);

  public void delete(T entity);
}

The GenericDao implementation

package com.foo.shop.database;

public abstract class GenericDaoImpl<T, PK extends Serializable> implements GenericDao<T, PK> {

@PersistenceContext(unitName = "com.foo.shop.database")
private EntityManager entityManager;

private Class<T> entityType;


public GenericDaoImpl(Class<T> entityType) {
  this.entityType = entityType;
}

@Override
public T getById(PK id) {
  return entityManager.find(entityType, id);
}

@Override
public List<T> getAll() {
  entityManager.getCriteriaBuilder().createQuery(entityType);
  CriteriaQuery<T> cq = entityManager.getCriteriaBuilder().createQuery(entityType);
  Root<T> product = cq.from(entityType);
  cq.select(product);
  TypedQuery<T> q = entityManager.createQuery(cq);
  return q.getResultList();
}

@Override
public void save(T entity) {
  this.entityManager.persist(entity);
}

@Override
public void update(T entity) {
  this.entityManager.merge(entity);
}

@Override
public void delete(T entity) {
  this.entityManager.remove(entity);
  }
}

The ProductDao interface

package com.foo.shop.database.dao;

import com.foo.shop.database.GenericDao;
import com.foo.shop.domain.Product;

public interface ProductDao extends GenericDao<Product, Long>{
}

The Implementation of the product DAO.

package com.foo.shop.database.jpa;

@Repository("productDao")
public class JpaProductDao extends GenericDaoImpl<Product, Long> implements ProductDao {

  @PersistenceContext(unitName="com.foo.shop.database")
  private EntityManager entityManager;

  public JpaProductDao() {
    super(Product.class);
  }

}

And finally, the component class that's trying to be instantiated:

package com.foo.shop.webapp.controller;

import com.foo.shop.database.dao.ProductDao;

@Controller
public class ProductController {

  @Autowired
  private ProductDao productDao;

  @RequestMapping(value = { "/product" }, method = RequestMethod.GET)
  public ModelAndView productPage(
      @RequestParam(value = "name", defaultValue = "Guy") String name) {

      // Model and view stuff
  }
}

I have no idea what I'm screwing up here, but I have an inkling it's related to the DAO beans being in the root context, and the servlet just taking care of itself. Any help on this is appreciated as all the config I have been able to find relates mostly to xml based config.

Thanks,

Upvotes: 2

Views: 1393

Answers (2)

FGreg
FGreg

Reputation: 15330

Not 100% sure this is causing your problem but...

The Spring Reference documentation says that when using Java configuration you extend AbstractAnnotationConfigDispatcherServletInitializer to initialize your Spring Application Context, and you should override the getRootConfigClasses() and getServletConfigClasses() methods.

The AbstractDispatcherServletInitializer class, createRootApplicationContext(), and createServletApplicationContext() methods are intended for XML configuration.

So your Initializer should look like this:

public class ApplicationInitialiser extends AbstractAnnotationConfigDispatcherServletInitializer { //No need to also implement WebApplicationInitializer here


    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{ApplicationContext.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { ServletContext.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/*" };
    }
}

Another not about your code (that may or may not be causing the problem)...

You are component scanning your JPA package:

@Configuration
@ComponentScan(basePackages = {"com.foo.shop.database.jpa"})
public class ApplicationContext {
    ...
}

And you are also constructing the bean manually:

@Bean 
ProductDao getProductDao() {
    return new JpaProductDao();
}

You only need to do one or the other, not both.

Upvotes: 0

dgofactory
dgofactory

Reputation: 348

You are creating two instances of JpaProductDao

First:

package com.foo.shop.database.jpa;

@Repository("productDao")
public class JpaProductDao extends GenericDaoImpl<Product, Long> implements ProductDao {

Second:

  @Bean 
  ProductDao getProductDao() {
    return new JpaProductDao();
  }

Thats why the error saids you have more than one instance and the container does not know which one to inject.

Upvotes: 1

Related Questions