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 3

2 ответа:

Ваш код имеет множество недостатков.

  1. ваша конфигурация повреждена из-за непонимания аннотаций и смешивания ее с XML
  2. Вы должны использовать DataSource для работы с Connection объектами и правильно освобождать их после использования
  3. у вас не должно быть кода обработки SQL в вашем контроллере
  4. вы не должны быть сконструированы 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.

<table>
    <c:forEach items="${passports}" var="passport">
        <tr><td>${passport.number}</td><!-- other columns --></tr>
    </c:forEach>
</table>
Теперь вы отделили свое представление, модель и контроллер. Теперь вы можете даже использовать тот же контроллер для экспорта в PDF, Excel или JSON. Не меняя его.

Последний совет используйте 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>