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>
Your code is flawed in multiple ways.
DataSource
to work with Connection
objects and properly release them after useYou 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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With