Java Spring @Autowired работает не так, как ожидалось
Мое приложение не работает должным образом из-за @Autowired. Будет ли это из-за кодов ниже?
@PropertySource("WEB-INF/config.properties")
public class DBQuery {
@Autowired
private Environment env;
Я пытался с большим количеством доступных решений и примеров в течение 2 дней, но не было никакой надежды на получение моего приложения работает должным образом. Буду очень признателен тем, кто может поправить меня с моими кодами.
Сообщения Об Ошибках
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: {}
Обновленные сообщения об ошибках полного стека после попытки решения ниже
<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_filesapache-tomcat-8.0.39webappstestWEB-INFclassescomtestdbDBQuery.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;
}
}
Тест-сервлет.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>
2 ответа:
Ваш код имеет множество недостатков.
- ваша конфигурация повреждена из-за непонимания аннотаций и смешивания ее с XML
- Вы должны использовать
DataSource
для работы сConnection
объектами и правильно освобождать их после использования- у вас не должно быть кода обработки SQL в вашем контроллере
- вы не должны быть сконструированы HTML в вашем контроллере
Вы определили свой класс
DBQuery
с помощью конструктора, который принимает аргумент, вы аннотировали этот конструктор с помощью@Autowired
. Это означает, что Spring попытается создать экземплярDBQuery
(из-за вашего<context:component-scan />
) и попытается найти Боб типаString
в вашем контексте. Этого Боба нет, поэтому он терпит неудачу.Также вы получаете несколько экземпляров
DBQuery
из-за сканирования компонентов и того факта, что у вас есть один в XML. Удалите конфигурацию XML. Также компонент является либо@Service
, либо@Controller
, но не обоими. И@PropertySource
на не@Configuration
классе ничего не делает.Как говорится, и предполагая, что вы в основном хотите использовать аннотации, сначала удалите свой конструктор
@Autowired
и просто создайте методsetStart
. Это также больше@Repository
, чем что-либо другое.@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; } }
И, конечно, удалите Боб
DBQuery
из вашего XML, а также Боб для БобаViewAllData
. Они обнаруживаются и создаются<context:component-scan />
Теперь вместо того, чтобы использовать
DriverManager
непосредственно, вы действительно должны использоватьDataSource
в своем коде и ввести его в свой класс.Добавьте в конфигурацию XML следующее.
<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>
Примечание: хотя
Теперь вместо того, чтобы вводитьDriverManagerDataSource
являетсяDataSource
, не используйте его в производстве. В этом случае используйте правильный пул соединений, например HikariCP. Красота здесь в том, что вы можете просто настроить это и оставить вашDBQuery
как есть.Environment
и все свойства, просто введитеDataSource
в ваш объектDBQuery
.@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; } }
Однако этот код все еще несовершенен, как и вы закрытие
Connection
иResultSet
(Если вы не сделаете этого в своем контроллере, что сделает его еще более ущербным). ВашgetAllDataPassport
, вероятно, должен возвращатьList
объектовPassport
(илиDataPassport
). Таким образом, вам нужно будет переместить часть обработки/преобразования контроллера в вашDBQuery
.Для облегчения работы с JDBC Spring имеет [
JdbcTemplate
], вероятно, вы захотите написать свой код, используя его вместоDataSource
, поскольку это будет управлять закрытием ресурсов и обработкой исключений для вас. Вы можете довольно легко использоватьRowMapper
для преобразования каждой строки в объектPassport
.В xml добавьте следующее
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource" /> </bean>
И введи это в свой
DBQuery
вместо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()); } }
Passport
. Вам, конечно, нужно написатьPassportRowMapper
, чтобы преобразовать каждую строку в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; } }
Примечание: не делайте
rs.next
вRowMapper
JdbcTemplate
заботится о тот. Вам нужно только преобразовать одну строку вPassport
.Теперь ваш контроллер освобожден от бремени преобразования и больше не привязан к SQL. Для тестирования теперь вы можете легко издеваться над
DBQuery
и юнит-тестировать ваш контроллерViewAllData
.@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); } }
Также похоже, что в вашем контроллере вы преобразуете
List<Passport
вString
, который, я боюсь, содержит таблицу HTML. Вы не должны делать такие вещи, вы должны иметь эту логику в представлении, где она принадлежит.В контроллере добавьте список элементов в модель.
@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); }
Теперь в вашем представлении вы можете использовать JSTL для перебора коллекции и создания таблицы HTML.
Теперь вы отделили свое представление, модель и контроллер. Теперь вы можете даже использовать тот же контроллер для экспорта в PDF, Excel или JSON. Не меняя его.<table> <c:forEach items="${passports}" var="passport"> <tr><td>${passport.number}</td><!-- other columns --></tr> </c:forEach> </table>
Последний совет используйте
InternalResourceViewResolver
вместо простогоUrlBasedViewResolver
. Имеет немного больше функциональности и автоматическое обнаружение JSTL экономит вам немного конфигурация.<bean id="viewResolver" class="org.springframework.web.servlet.view. InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean>
Задача находится в тест-сервлете.XML dbQuery bean, вместо этого используйте зависимость на основе конструктора "
<bean id="dbQuery" class="com.test.db.DBQuery" > <property name="start" value="1"/> </bean>
Вам нужно изменить его на:
<bean id="dbQuery" class="com.test.db.DBQuery" > <constructor-arg type="java.lang.String" value="1"/> </bean>