Java Spring @Autowired没有按预期工作

时间:2016-12-15 06:59:06

标签: 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_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 /测试/ 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 /测试/分贝/ 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;
    }

}

测试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>

2 个答案:

答案 0 :(得分:3)

您的代码有多种方式存在缺陷。

  1. 由于不理解注释并将其与XML混合,您的配置存在缺陷
  2. 您应该使用DataSource来处理Connection个对象,并在使用后正确释放它们
  3. 您的控制器中不应该有SQL处理代码
  4. 您不应该在控制器中构建HTML
  5. 您已使用带参数的构造函数定义了DBQuery类,并使用@Autowired注释了此构造函数。这意味着Spring将尝试创建DBQuery的实例(由于您的<context:component-scan />)并尝试在您的上下文中找到类型为String的bean。那豆不存在因此失败了。

    由于组件扫描以及XML中的一个实例,您最终会得到DBQuery的多个实例。删除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;
        }
    }
    

    当然,从XML中删除DBQuery bean以及ViewAllData bean的bean。这些是由<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>
    

    注意:虽然DriverManagerDataSourceDataSource,但请勿在生产中使用它。在这种情况下,请使用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;
        }
    }
    

    但是,由于您没有关闭ConnectionResultSet,因此此代码仍有缺陷(除非您在控制器中执行此操作,否则会使其更加有缺陷)。您的getAllDataPassport应该返回ListPassport个对象(或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。您只需将一行转换为DBQuery即可。

    现在您的控制器可以免于转换的负担而不再依赖于SQL。为了进行测试,您现在可以轻松模拟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转换为 @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); } ,我担心其中包含HTML表格。你不应该这样做,你应该在它所属的视图中拥有那个逻辑。

    在您的控制器中,将项目列表添加到模型中。

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

    现在在您的视图中,您可以使用JSTL迭代集合并创建HTML表。

    InternalResourceViewResolver

    现在,您已将视图,模型和控制器分离。您现在甚至可以使用相同的控制器导出为PDF,Excel或JSON。不改变它。

    最终提示使用UrlBasedViewResolver而非普通<bean id="viewResolver" class="org.springframework.web.servlet.view. InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> 。有更多的功能和自动检测JSTL节省了一些配置。

    row < 10 then small font
    row > 10 and row < 20 then medium font
    row > 20 then large font
    

答案 1 :(得分:0)

问题在于test-servlet.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>