Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Spring @Autowired not working as expected

My application is not running as expected due to @Autowired. Would it because of the codes below?

@PropertySource("WEB-INF/config.properties")
public class DBQuery {
    @Autowired
    private Environment env;

I had tried with lots of available solutions and examples for 2 days yet no hope in getting my application running properly. Would greatly appreciate those who can correct me with my codes.

Error Messages

2016-12-15 14:23:04 WARN  XmlWebApplicationContext:487 - Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dbQuery' defined in ServletContext resource [/WEB-INF/test-servlet.xml]: Unsatisfied dependency expressed through constructor argument with index 0 of type [java.lang.String]: : No qualifying bean of type [java.lang.String] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}

Updated Full Stack Error Messages after tried the solution below

<bean id="dbQuery" class="com.cdmDP.db.DBQuery" >
    <constructor-arg type="java.lang.String" value="1"/>
</bean>

2016-12-15 15:46:49 ERROR ContextLoader:331 - Context initialization failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'DBQuery' defined in file [D:\program_files\apache-tomcat-8.0.39\webapps\test\WEB-INF\classes\com\test\db\DBQuery.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [java.lang.String]: : No qualifying bean of type [java.lang.String] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)
        at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:185)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1139)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1042)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
        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.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
        at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
        at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
        at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4853)
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5314)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
        at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:753)
        at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:729)
        at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717)
        at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:940)
        at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1816)
        at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
        at java.util.concurrent.FutureTask.run(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at java.lang.Thread.run(Unknown Source)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
        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.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:813)
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
        ... 28 more

com/test/ViewAllData.java

@Controller
public class ViewAllData {
    DBQuery dbQuery;

    @Autowired
    public ViewAllData(DBQuery dbQuery){
        this.dbQuery = dbQuery;
    }

    @RequestMapping("/viewAllData")
    public ModelAndView viewData() throws SQLException, ClassNotFoundException{
        String dataTable;
        ResultSet rs = dbQuery.getAllData();
        //processing

        return new ModelAndView("viewAllData", "message", dataTable);
    }
}

com/test/db/DBQuery.java

@Controller
@Service
@PropertySource("WEB-INF/config.properties")
public class DBQuery {
    @Autowired
    private Environment env;
    String dbUsername = env.getProperty("db.username");
    String dbPassword = env.getProperty("db.password");
    String dbUrl = env.getProperty("db.url");

    String start;
    @Autowired
    public DBQuery(String start) throws ClassNotFoundException, SQLException{
        this.start = start;
    }

    public ResultSet getAllDataPassport() throws SQLException, ClassNotFoundException{
        Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");  
        Connection conn = DriverManager.getConnection(dbUrl, dbUsername, dbPassword);

        Statement statement = conn.createStatement();
        String sql = "select * from testing_table";
        ResultSet rs = statement.executeQuery(sql);

        return rs;
    }

}

test-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">

   <context:component-scan base-package="com.test, com.test.db" />
   <context:annotation-config />

    <bean id="viewAllData" class="com.test.ViewAllData"/>
    <bean id="dbQuery" class="com.test.db.DBQuery" >
        <property name="start" value="1"/>
    </bean>

    <bean class="org.springframework.context.support.ResourceBundleMessageSource" id="messageSource">
        <property name="basename" value="messages" />
    </bean>

    <bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>

</beans>
like image 719
user1841495 Avatar asked Sep 06 '25 02:09

user1841495


1 Answers

Your code is flawed in multiple ways.

  1. your configuration is flawed due to not understanding annotations and mixing it with XML
  2. You should be using a DataSource to work with Connection objects and properly release them after use
  3. You shouldn't have SQL processing code in your controller
  4. You shouldn't be constructed HTML in your controller

You have defined your DBQuery class with a constructor that takes an argument, you have annotated this constructor with @Autowired. Which means Spring will try to create an instance of DBQuery (due to your <context:component-scan /> ) and try to find a bean of the type String in your context. That bean isn't there thus it fails.

Also you end up with multiple instances of DBQuery due to component scanning and the fact you have one in XML. Remove the XML configuration. Also a component is either a @Service or a @Controller but not both. And @PropertySource on a non @Configuration class doesn't do anything.

That being said, and assuming you largely want to use annotations first remove your @Autowired constructor and simply create a setStart method. It is also more of a @Repository then anything else.

@Repository
public class DBQuery {

    @Autowired
    private Environment env;
    String dbUsername = env.getProperty("db.username");
    String dbPassword = env.getProperty("db.password");
    String dbUrl = env.getProperty("db.url");
    String start; 

    public ResultSet getAllDataPassport() throws SQLException, ClassNotFoundException{
        Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");  
        Connection conn = DriverManager.getConnection(dbUrl, dbUsername, dbPassword);

        Statement statement = conn.createStatement();
        String sql = "select * from testing_table";
        ResultSet rs = statement.executeQuery(sql);

        return rs;
    }
}

And of course remove the DBQuery bean from your XML as well as the bean for ViewAllData bean. Those are detected and created by <context:component-scan />

Now instead of using the DriverManager directly you really should be using a DataSource in your code and inject that into your class.

Add the following to your XML configuration.

<context:property-placeholder location="WEB-INF/config.properties" />

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
    <property name="url" value="${db.url}" />
    <property name="username" value="${db.username}" />
    <property name="password" value="${db.password}" />
</bean>

Note: Although the DriverManagerDataSource is a DataSource don't use it in production. Use a proper connection pool like HikariCP in that case. The beauty here is that you can just configure that and leave your DBQuery as is.

Now instead of injecting the Environment and all the properties simply inject a DataSource into your DBQuery object.

@Repository
public class DBQuery {

    private final DataSource dataSource;

    @Autowired    
    public DBQuery(DataSource dataSource) {
        this.dataSource=dataSource;
    }

    public ResultSet getAllDataPassport() throws SQLException {

        Connection conn = this.dataSource.getConnection();
        Statement statement = conn.createStatement();
        String sql = "select * from testing_table";
        ResultSet rs = statement.executeQuery(sql);
        return rs;
    }
}

However this code is still flawed as you are not closing the Connection and ResultSet (unless you do that in your controller which would make it even more flawed). Your getAllDataPassport should probably be returning a List of Passport objects (or DataPassport). So you would need to move part of the processing/conversion of the controller to your DBQuery.

To make working with JDBC easier Spring has the [JdbcTemplate] you probably want to write your code using that instead of the DataSource as that will manage the resource closing and exception handling for you. You can quite easily use a RowMapper to convert each row into a Passport object.

In your xml add the following

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource" />
</bean>

And inject that into your DBQuery instead of the DataSource.

@Repository
public class DBQuery {

    private final JdbcTemplate jdbcTemplate;

    @Autowired    
    public DBQuery(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public List<Passport> getAllDataPassport() {

        String sql = "select * from testing_table";
        return this.jdbcTemplate.query(sql, new PassportRowMapper());
    }
}

The code above will execute your query, obtain and close the resources and convert the rows into Passport object. You of course need to write the PassportRowMapper to convert each row into a Passport.

public class PassportRowMapper implements RowMapper<Passport> {

    public Passport mapRow(ResultSet rs, int rowNum) throws SQLException {
        Passport p = new Passport();
        p.setNumber(rs.getString(1));
        // further logic to get row information
        return p;
    }
}

Note: Don't do rs.next in the RowMapper the JdbcTemplate takes care of that. You only need to convert a single row into a Passport.

Now your controller is freed of the burden to do conversion and not tied to SQL anymore. For testing you could now easily mock the DBQuery and unit test your ViewAllData controller.

@Controller
public class ViewAllData {

    private final DBQuery dbQuery;

    @Autowired
    public ViewAllData(DBQuery dbQuery){
        this.dbQuery = dbQuery;
    }

    @RequestMapping("/viewAllData")
    public ModelAndView viewData() {
        String dataTable;
        List<Passport> passports = dbQuery.getAllDataPassports();
        // List to whatever it needs to be
        return new ModelAndView("viewAllData", "message", dataTable);
    }
}

It also looks like in your controller you are converting the List<Passport into a String which I fear contains a HTML table. You shouldn't be doing tings like that, you should have that logic in the view where it belongs.

In your controller add the list of items to the model.

    @RequestMapping("/viewAllData")
    public ModelAndView viewData() {
        String dataTable;
        List<Passport> passports = dbQuery.getAllDataPassports();
        // List to whatever it needs to be
        return new ModelAndView("viewAllData", "passports", passports);
    }

Now in your view you can use JSTL to iterate over the collection and create the HTML table.

<table>
    <c:forEach items="${passports}" var="passport">
        <tr><td>${passport.number}</td><!-- other columns --></tr>
    </c:forEach>
</table>

Now you have decoupled your View, Model and Controller. You could now even use the same controller to export to PDF, Excel or JSON. Without changing it.

Final tip use the InternalResourceViewResolver instead of the plain UrlBasedViewResolver. Has a bit more functionality and auto detection of JSTL saves you some configuration.

<bean id="viewResolver" class="org.springframework.web.servlet.view. InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/" />
    <property name="suffix" value=".jsp" />
</bean>
like image 199
M. Deinum Avatar answered Sep 07 '25 21:09

M. Deinum