无法在 ApplicationContext.xml 中声明 Bean

Cannot Declare Beans inside ApplicationContext.xml

答对者有奖 我的实现如下:

NextShipmentDaoImpl.java

public class NextShipmentDaoImpl implements NextShipmentDao{

    private DataSource dataSource;

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

LoginController.java

@Controller  
public class LoginController {  

      @Autowired
      private NextShipmentDao nextShipmentDao;

      public void setNextShipmentDao(NextShipmentDao nextShipmentDao) {
        this.nextShipmentDao = nextShipmentDao;
     }

在像这样创建所需的上述 bean 时:

<bean id="nextShipmentDao" class="com.ibrahim.dao.NextShipmentDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>

ApplicationContext.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"  
 xmlns:tx="http://www.springframework.org/schema/tx"  
 xsi:schemaLocation=" 
http://www.springframework.org/schema/beans  
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
http://www.springframework.org/schema/context  
http://www.springframework.org/schema/context/spring-context-3.0.xsd  
http://www.springframework.org/schema/tx  
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">  
<property name="driverClassName" value="com.mysql.jdbc.Driver" />  
<property name="url" value="jdbc:mysql://localhost:3306/board" />  
<property name="username" value="root" />  
<property name="password" value="root" />  
</bean>

<bean id="nextShipmentDao" class="com.ibrahim.dao.NextShipmentDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>

 <security:http auto-config="true" >  
 <security:intercept-url pattern="/index*" access="ROLE_USER" />  
 <security:form-login login-page="/login.htm" default-target-url="/index.htm"  
  authentication-failure-url="/loginerror.htm" />  
 <security:logout logout-success-url="/logout.htm" />  
 </security:http> 


<context:component-scan base-package="com.ibrahim.controller,com.ibrahim.domain,com.ibrahim.dao" />  

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

一切正常。但是当我尝试创建一个 bean 时,这会显示错误

SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loginController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.ibrahim.dao.NextShipmentDao com.ibrahim.controller.LoginController.nextShipmentDao; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nextShipmentDao' defined in ServletContext resource [/WEB-INF/board-dao.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.ibrahim.dao.NextShipmentDaoImpl]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Property 'dataSource' is required
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:288)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1116)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getObject(AbstractBeanFactory.java:295)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:628)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:389)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:294)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4728)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5166)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1409)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1399)
    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.BeanCreationException: Could not autowire field: private com.ibrahim.dao.NextShipmentDao com.ibrahim.controller.LoginController.nextShipmentDao; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nextShipmentDao' defined in ServletContext resource [/WEB-INF/board-dao.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.ibrahim.dao.NextShipmentDaoImpl]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Property 'dataSource' is required
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:514)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:285)
    ... 22 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nextShipmentDao' defined in ServletContext resource [/WEB-INF/board-dao.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.ibrahim.dao.NextShipmentDaoImpl]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Property 'dataSource' is required
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1007)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:953)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:487)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getObject(AbstractBeanFactory.java:295)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:912)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:855)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:770)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:486)
    ... 24 more
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.ibrahim.dao.NextShipmentDaoImpl]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Property 'dataSource' is required
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:163)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1000)
    ... 35 more
Caused by: java.lang.IllegalArgumentException: Property 'dataSource' is required
    at org.springframework.jdbc.support.JdbcAccessor.afterPropertiesSet(JdbcAccessor.java:134)
    at org.springframework.jdbc.core.JdbcTemplate.<init>(JdbcTemplate.java:165)
    at com.ibrahim.dao.NextShipmentDaoImpl.<init>(NextShipmentDaoImpl.java:28)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:148)
    ... 37 more

May 07, 2015 12:21:48 PM org.apache.catalina.core.StandardContext startInternal
SEVERE: Error listenerStart
May 07, 2015 12:21:48 PM org.apache.catalina.core.StandardContext startInternal
SEVERE: Context [/13.1SecuringUrlAcces] startup failed due to previous errors
May 07, 2015 12:21:48 PM org.apache.catalina.core.ApplicationContext log
INFO: Closing Spring root WebApplicationContext
May 07, 2015 12:21:48 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-nio-8080"]
May 07, 2015 12:21:48 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-nio-8009"]
May 07, 2015 12:21:48 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 12402 ms

这是我的 NextShipmentDaoImpl.java

public class NextShipmentDaoImpl implements NextShipmentDao{

    private DataSource dataSource;

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    JdbcTemplate jdbcTemplate= new JdbcTemplate(dataSource);

    @Override
    public List<NextShipment> display() {

        String sql = "SELECT * FROM NEXTSHIPMENT";

        List<NextShipment> shipments = new ArrayList<NextShipment>();
        List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql);
        for (Map row : rows) {
            NextShipment shipment = new NextShipment();
            shipment.setOid((String) row.get("OID"));
            shipment.setAddress((String) row.get("ADDRESS"));
            shipment.setPack_Date((Date) row.get("PACK_DATE"));
            shipment.setIsAvailable((Boolean) row.get("ISAVAILABLE"));
            shipment.setAssignShipper((String) row.get("ASSIGNSHIPPER"));
            shipment.setInTransit((Boolean) row.get("InTransit"));
            shipments.add(shipment);
        }
        return shipments;
    }
}

基于XML的配置时代已经结束。

我强烈推荐你使用Annotated Configuration

像这样:

@Configuration
@EnableWebMvc
@EnableTransactionManagement
@ComponentScan(basePackages = "com.myapp.*")
public class AppConfiguration extends WebMvcConfigurerAdapter {

    @Bean
    public DataSource myDataSource() {

        DriverManagerDataSource dataSource = new DriverManagerDataSource();

        dataSource.setDriverClassName("<full.driver.class.name>");
        dataSource.setUrl("url");
        dataSource.setUsername("dit");
        dataSource.setPassword("pass");

        return dataSource;
    }

    [...] other Beans like TransactionManager....

}

堆栈跟踪显示:

  • 您在 loginController
  • 中正确地自动装配了 nextShipmentDAO
  • 但是 dataSource 属性 是 null 是 NextShipmentDAOImpl bean
  • 异常发生在JdbcTemplate初始化

NextShipmentDAOImpl 给出了原因:您正确地注入了数据源,但在构建时初始化了 JdbcTemplate。所以这就是发生的事情:

  • jvm 创建一个 NextShipmentDaoImpl 对象
  • jvm初始化对象的所有字段包括jdbcTemplate
  • spring 设置数据源字段...太迟了!

[@M.Deinum在评论中提到了,但我没有给予足够的重视]

永远不会在创建时使用注入字段,因为那时它还没有被注入。在设置所有属性后,您应该使用 spring 调用的初始化方法:

public class NextShipmentDaoImpl implements NextShipmentDao{

    private DataSource dataSource;

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    JdbcTemplate jdbcTemplate;

    void init(void) {
        jdbcTemplate = new JdbcTemplate(dataSource);
    }

并以这种方式声明 bean :

<bean id="nextShipmentDao" class="com.ibrahim.dao.NextShipmentDaoImpl" 
    init-method="init">
<property name="dataSource" ref="dataSource"></property>
</bean>

NextShipmentDaoImpl.java 看起来像这样

public class NextShipmentDaoImpl implements NextShipmentDao{

    private DataSource dataSource;

    JdbcTemplate jdbcTemplate;

    public NextShipmentDaoImpl(DataSource dataSource) {
        this.dataSource = dataSource;
    }

在上面的代码行中,我在其实现函数本身中声明了 JdbcTemplate。用在什么地方。

我声明的相关 bean 是:

<bean id="nextShipmentDao" class="com.ibrahim.dao.NextShipmentDaoImpl">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>

登录控制器保持不变:

@Controller  
public class LoginController {  

     @Autowired
      private NextShipmentDao nextShipmentDao;

       public void setNextShipmentDao(NextShipmentDao nextShipmentDao) {
        this.nextShipmentDao = nextShipmentDao;
     }

我已将 spring 和 spring 安全罐从 3.2.4 转移到 3.2.7 问题就这样解决了。