Spring Autowire fails with No qualifying bean of type found for dependency error

autowiredspringstruts2

Here's my scenario. My project is using Spring (3.2.3.RELEASE), Struts2 (2.3.14.3) and JPA (2.0). We have a project (let's call it project A) that contains various entities and common classes. We use project A to generate a .jar file so that other projects can use these classes. In project A we've used the Spring stereotypes: @Component. @Service or @Repository for those classes we want Spring to inject. Whenever we try to inject beans from the jar, we get an error similar to:

  1. No qualifying bean of type
    [com.ceiwc.bc.commonsql.service.CommonSQLService] 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)}

  2. Could not autowire field: private com.ceiwc.bc.commonsql.service.CommonSQLService
    com.ceiwc.ma.mvc.action.LoginAction.commonSQLService; nested exception
    is org.springframework.beans.factory.NoSuchBeanDefinitionException: No
    qualifying bean of type
    [com.ceiwc.bc.commonsql.service.CommonSQLService] 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)}

  3. Error creating bean with name 'com.ceiwc.ma.mvc.action.LoginAction': Injection of autowired
    dependencies failed; nested exception is
    org.springframework.beans.factory.BeanCreationException: Could not
    autowire field: private
    com.ceiwc.bc.commonsql.service.CommonSQLService
    com.ceiwc.ma.mvc.action.LoginAction.commonSQLService; nested exception
    is org.springframework.beans.factory.NoSuchBeanDefinitionException: No
    qualifying bean of type
    [com.ceiwc.bc.commonsql.service.CommonSQLService] 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)}

  4. Unable to instantiate Action, com.ceiwc.ma.mvc.action.LoginAction, defined for 'doLogin' in namespace '/Login'Error creating bean with
    name 'com.ceiwc.ma.mvc.action.LoginAction': Injection of autowired
    dependencies failed; nested exception is
    org.springframework.beans.factory.BeanCreationException: Could not
    autowire field: private
    com.ceiwc.bc.commonsql.service.CommonSQLService
    com.ceiwc.ma.mvc.action.LoginAction.commonSQLService; nested exception
    is org.springframework.beans.factory.NoSuchBeanDefinitionException: No
    qualifying bean of type
    [com.ceiwc.bc.commonsql.service.CommonSQLService] 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)}

We are using Java configuration for Spring. Here is our configuration class:

package com.zzz.bc.config;

@Configuration
@ImportResource({"/WEB-INF/spring/spring-config.xml"})
@ComponentScan(basePackages = "com.zzz")
public class ApplicationConfig {
    @Bean
    public JavaMailSender mailSender() {
        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
        mailSender.setHost(${app.mail.host});
        return mailSender;
    }
}

package com.zzz.bc.config;

@Configuration
@EnableTransactionManagement
@Import(ApplicationConfig.class)
@PropertySource({"classpath:db.properties"})
public class DataConfig {

    @Autowired
    Environment environment;

    @Bean
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactoryBean().getObject());
        return transactionManager;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
        String[] packagesToScan =
                {environment.getProperty("db.packagesToScan1"), environment.getProperty("db.packagesToScan2")};

        Map<String, Object> jpaProperties = new HashMap<String, Object>();
        jpaProperties.put("eclipselink.weaving", "false");

        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource());
        entityManagerFactoryBean.setPackagesToScan(packagesToScan);
        entityManagerFactoryBean.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
        entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter());
        entityManagerFactoryBean.setJpaPropertyMap(jpaProperties);

        return entityManagerFactoryBean;
    }

    @Bean
    public DriverManagerDataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(environment.getProperty("db.driver"));
        dataSource.setUrl(environment.getProperty("db.url"));
        dataSource.setUsername(environment.getProperty("db.username"));
        dataSource.setPassword(environment.getProperty("db.password"));
        return dataSource;
    }

    @Bean
    public JpaVendorAdapter vendorAdapter() {
        EclipseLinkJpaVendorAdapter vendorAdapter = new EclipseLinkJpaVendorAdapter();
        vendorAdapter.setDatabase(Database.ORACLE);
        vendorAdapter.setShowSql(true);
        return vendorAdapter;
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }
}

We have @ComponentScan set to check to bean in the package of com.zzz. In project B, we have a Struts2 Action that we want to inject a bean from project A. Here is a snippet of the class from project B:

@SuppressWarnings("rawtypes")
@Service("CommonSQLService")
public class CommonSQLServiceImpl implements CommonSQLService {

    @Autowired
    CommonSQLDao cdao;

    @Override
    public Policy getPolicyById(String policyNo) {
        return cdao.getPolicyById(policyNo);
    }

    @Override
    public Policy getPolicy(Policy parmRec) {
        return cdao.getPolicy(parmRec);
    }

    @Override
    public ArrayList<Policy> getPolicies(String policyNo) {
        return cdao.getPolicies(policyNo);
    }
    @Override
     public List<Policy> getPolicies(Policy parmRec){
        return cdao.getPolicies(parmRec);
    }
}

Here is our action (removed getters/setters):

package com.zzz.ma.mvc.action;

@SuppressWarnings("serial")
@Component
@Scope("prototype")
@Namespace("/Login")
public class LoginAction extends ActionParent {
    @Autowired
    private IUserService userService; //Contained in project B

    @Autowired
    private CommonSQLService commonSQLService; //What's autowired from project A

    private User user;
    private String updateFlag;
    private Integer parentMenuId;
    private IwifWebNavmenuItem record;

    public static final String LOGIN_STR = "login";

    @Action(value = "doLogin", results = { @Result(name = "success", location = "/pages/login.jsp") })
    public String login() {
        updateFlag = "Y";
        return SUCCESS;
    }

    @Action(value = "validate", results = {
            @Result(name = "success", location = "/BrowseOptions/showOptions", type = "redirect"),
            @Result(name = "login", location = "/pages/login.jsp") })
    public String validateUser() {
        if (updateFlag == null) {
            session.remove(LOGIN_STR);
            return LOGIN;
        }

        if (userService.validateUser(user.getUsername(), user.getPassword()) == null) {
            addActionError(getText("invalid.user"));
            return LOGIN;
        }

        session.put(LOGIN_STR, user.getUsername());

        return SUCCESS;
    }

    @Action(value = "logout", results = { @Result(name = "success", location = "/pages/login.jsp") })
    public String logout() {
        updateFlag = StringUtils.EMPTY;
        session.invalidate();

        return SUCCESS;
    }
}

Whenever we try to do this autowire, we get an error like the one listed above. What are we missing? Can't classes contained in a jar file with Spring Stereotypes be autowired?

Thanks for you help in advance!

Best Answer

Change @Service("CommonSQLService") to @Service("commonSQLService") or you can just use @Service if you're not implementing the CommonSQLService interface anywhere else.

Related Topic