【问题标题】:NoSuchBeanDefinitionException while wiring xml beans with manually created beans使用手动创建的 bean 连接 xml bean 时出现 NoSuchBeanDefinitionException
【发布时间】:2018-08-31 18:01:42
【问题描述】:

我在 app-context.xml 文件中有以下 bean 定义:

<bean id="buttonPanel" class="todo.ui.BoxLayoutPanel" init-method="init" depends-on="deleteButton, addNewButton">
        <property name="axis">
          <!--  "0" corresponds to BoxLayout.X_AXIS -->
          <value>0</value>
        </property>
        <property name="panelComponents">
          <list>
            <ref bean="deleteButton"/>
            <ref bean="addNewButton"/>
          </list>
        </property>
</bean>

我正在尝试通过代码手动创建 deleteButtonaddNewButton bean:

    String[] contextPaths = new String[] {"todo/app-context.xml"};
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(contextPaths);

    ActionListenerButton addNewButton = new ActionListenerButton();
    addNewButton.setText("Add New");
    System.out.println("setText(Add New)");
    ListTableActionListener addActionListener = (ListTableActionListener) ctx.getBean("addNewButtonActionListener");
    addNewButton.setActionListener(addActionListener);

    ActionListenerButton deleteButton = new ActionListenerButton();
    deleteButton.setText("Delete");
    System.out.println("setText(Delete)");
    ActionListener deleteActionListener = (ActionListener) ctx.getBean("addNewButtonActionListener");
    deleteButton.setActionListener(deleteActionListener);

    AutowireCapableBeanFactory factory = ctx.getAutowireCapableBeanFactory();
    factory.autowireBean(addNewButton);
    factory.initializeBean(addNewButton, "addNewButton");
    factory.autowireBean(deleteButton);
    factory.initializeBean(deleteButton, "deleteButton");

deleteButtonaddNewButton bean 所依赖的 Bean addNewButtonActionListeneraddNewButtonActionListener 使用注释定义:

@Component("addNewButtonActionListener")
public class AddNewButtonActionListener extends ListTableActionListener {

    @Resource(name="itemList")
    public void setList(List list) {
        this.list = list;
    }

    @Resource(name="itemTable")
    public void setTable(JTable itemTable) {
        this.itemTable = itemTable;
    }

    public void actionPerformed(ActionEvent e) {
        list.add("New Item");
        itemTable.revalidate();  
    }
}

@Component("deleteButtonActionListener")
public class DeleteButtonActionListener extends ListTableActionListener {

    @Resource(name="itemList")
    public void setList(List list) {
        this.list = list;
    }

    @Resource(name="itemTable")
    public void setTable(JTable itemTable) {
        this.itemTable = itemTable;
    }

    public void actionPerformed(ActionEvent e) {
        int selectedRow = itemTable.getSelectedRow(); 

        if (selectedRow == -1) {
            // if there is no selected row, don't do anything
            return;
        }

        if (itemTable.isEditing()) { 
            // if we are editing the table, don't do anything
            return;
        }

        if (selectedRow < list.size()) {
            list.remove(selectedRow);
            itemTable.revalidate(); 
        }        
    }
}

ActionListenerButton 类看起来像这样:

public class ActionListenerButton extends JButton {
    String text;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    private ActionListener actionListener;

    public void setActionListener(ActionListener actionListener) {
        this.actionListener = actionListener;
    }

    public ActionListener getActionListener() {
        return actionListener;
    }

    public void init() {
        this.addActionListener(actionListener);
    }
}

但似乎 bean buttonPanel(在 xml 中定义)是在 addNewButtondeleteButton bean(应该由工厂初始化,通过代码)之前创建的,因为我得到 NoSuchBeanDefinitionException 的描述 “没有可用的名为 'deleteButton' 的 bean”:

13:15:33.524 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.event.internalEventListenerFactory'
13:15:33.524 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.event.internalEventListenerFactory' to allow for resolving potential circular references
13:15:33.526 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.event.internalEventListenerFactory'
13:15:33.526 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'mainFrame'
13:15:33.526 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'mainFrame'
13:15:33.543 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Eagerly caching bean 'mainFrame' to allow for resolving potential circular references
13:15:33.558 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'mainPanel'
13:15:33.558 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'mainPanel'
13:15:33.563 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Eagerly caching bean 'mainPanel' to allow for resolving potential circular references
13:15:33.577 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'itemScrollPane'
13:15:33.577 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'itemScrollPane'
13:15:33.581 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'itemTable'
13:15:33.598 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Eagerly caching bean 'itemScrollPane' to allow for resolving potential circular references
13:15:33.604 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'itemScrollPane'
13:15:33.605 [main] WARN  o.s.c.s.ClassPathXmlApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mainFrame' defined in class path resource [todo/app-context.xml]: Cannot resolve reference to bean 'mainPanel' while setting bean property 'mainPanel'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mainPanel' defined in class path resource [todo/app-context.xml]: Cannot resolve reference to bean 'buttonPanel' while setting bean property 'panelComponents' with key [1]; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'deleteButton' available
13:15:33.605 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@77f99a05: defining beans [addNewButtonActionListener,deleteButtonActionListener,itemTableModel,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,mainFrame,mainPanel,itemScrollPane,itemTable,buttonPanel,itemList]; root of factory hierarchy
13:15:33.605 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Retrieved dependent beans for bean 'itemList': [addNewButtonActionListener, itemTableModel, deleteButtonActionListener]
13:15:33.606 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Retrieved dependent beans for bean 'itemTableModel': [itemTable]
13:15:33.606 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Retrieved dependent beans for bean 'itemTable': [deleteButtonActionListener, itemScrollPane]
13:15:33.606 [main] DEBUG o.s.b.f.s.DisposableBeanAdapter - Invoking destroy() on bean with name 'itemList'

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mainFrame' defined in class path resource [todo/app-context.xml]: Cannot resolve reference to bean 'mainPanel' while setting bean property 'mainPanel'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mainPanel' defined in class path resource [todo/app-context.xml]: Cannot resolve reference to bean 'buttonPanel' while setting bean property 'panelComponents' with key [1]; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'deleteButton' available

如何让 Spring 在 buttonPanel bean 之前实例化 deleteButtonaddNewButton bean?

P.S. 完整的 app-context.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:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-2.5.xsd
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util.xsd">

        <context:component-scan base-package="todo"/>

        <bean id="mainFrame" class="todo.ui.MainFrame" init-method="init">
            <property name="mainPanel">
              <ref bean="mainPanel"/>
            </property>
            <property name="title">
              <value>My To Do List</value>
            </property>
        </bean>   

      <bean id="mainPanel" class="todo.ui.BoxLayoutPanel" 
            init-method="init">
        <property name="axis"> 
           <!-- "1" corresponds to BoxLayout.Y_AXIS
           Spring can access constants, but it's more complex -->
          <value>1</value>
        </property>
        <property name="panelComponents">
          <list>
            <ref bean="itemScrollPane"/>
            <ref bean="buttonPanel"/>
          </list>
        </property>
      </bean>

      <bean id="itemScrollPane" class="javax.swing.JScrollPane">
        <constructor-arg>
          <ref bean="itemTable"/>
        </constructor-arg>
      </bean>

      <bean id="itemTable" class="javax.swing.JTable" depends-on="itemTableModel">
        <property name="model">
          <ref bean="itemTableModel"/>
        </property>
      </bean>

      <bean id="buttonPanel" class="todo.ui.BoxLayoutPanel" init-method="init" depends-on="deleteButton">
        <property name="axis">
          <!--  "0" corresponds to BoxLayout.X_AXIS -->
          <value>0</value>
        </property>
        <property name="panelComponents">
          <list>
            <ref bean="deleteButton"/>
            <ref bean="addNewButton"/>
          </list>
        </property>
      </bean>

      <!-- <bean id="deleteButton" class="todo.ui.button.ActionListenerButton" 
            init-method="init">
        <property name="actionListener">
          <ref bean="deleteButtonActionListener"/>
        </property>
        <property name="text">
          <value>Delete</value>
        </property>
      </bean> -->

      <!-- <bean id="deleteButtonActionListener" 
            class="todo.ui.button.DeleteButtonActionListener">
        <property name="list">
          <ref bean="itemList"/>
        </property>
        <property name="table">
          <ref bean="itemTable"/>
        </property>
      </bean> -->

      <!-- <bean id="addNewButton" class="todo.ui.button.ActionListenerButton" 
            init-method="init">
        <property name="actionListener">
          <ref bean="addNewButtonActionListener"/>
        </property>
        <property name="text">
          <value>Add New</value>
        </property>
      </bean> -->

      <!-- <bean id="addNewButtonActionListener" 
            class="todo.ui.button.AddNewButtonActionListener">
        <property name="list">
          <ref bean="itemList"/>
        </property>
        <property name="table">
          <ref bean="itemTable"/>
        </property>
      </bean> -->    

     <util:list id="itemList">
            <value>Item 1</value>
            <value>Item 2</value>
            <value>Item 3</value>
     </util:list>
</beans>

UPD

或者也许我错了,我的 AutowireCapableBeanFactory 代码并没有真正创建 bean 并将它们添加到 Spring 容器中...... 因为当我将 xml 中的 lazy-init="true" 属性添加到 mainFramemainPanelbuttonPanel bean 并在工厂代码后调用 mainFrame 时:

AutowireCapableBeanFactory factory = ctx.getAutowireCapableBeanFactory();
factory.autowireBean(addNewButton);
factory.initializeBean(addNewButton, "addNewButton");
factory.autowireBean(deleteButton);
factory.initializeBean(deleteButton, "deleteButton");

ctx.getBean("mainFrame");

我在控制台中得到以下输出:

14:04:49.780 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'
14:04:49.783 [main] DEBUG o.s.c.e.PropertySourcesPropertyResolver - Could not find key 'spring.liveBeansView.mbeanDomain' in any property source
setText(Add New)
14:04:49.785 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'addNewButtonActionListener'
setText(Delete)
14:04:49.786 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'deleteButtonActionListener'
14:04:49.812 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'mainFrame'
14:04:49.812 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'mainFrame'
14:04:49.828 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Eagerly caching bean 'mainFrame' to allow for resolving potential circular references
14:04:49.846 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'mainPanel'
14:04:49.846 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'mainPanel'
14:04:49.850 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Eagerly caching bean 'mainPanel' to allow for resolving potential circular references
14:04:49.869 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'itemScrollPane'
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mainFrame' defined in class path resource [todo/app-context.xml]: Cannot resolve reference to bean 'mainPanel' while setting bean property 'mainPanel'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mainPanel' defined in class path resource [todo/app-context.xml]: Cannot resolve reference to bean 'buttonPanel' while setting bean property 'panelComponents' with key [1]; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'deleteButton' available

即,Spring 仅返回缓存的单例 bean 实例 addNewButtonActionListenerdeleteButtonActionListener,bot 不会创建和返回 bean 实例 addNewButtondeleteButton

【问题讨论】:

    标签: java spring


    【解决方案1】:

    我在related SO question 中找到了答案。修改代码后:

    AutowireCapableBeanFactory factory = ctx.getAutowireCapableBeanFactory();
    
    BeanDefinition definition = new RootBeanDefinition(ActionListenerButton.class);
    BeanDefinitionRegistry registry = (BeanDefinitionRegistry) factory;
    registry.registerBeanDefinition("addNewButton", definition);
    registry.registerBeanDefinition("deleteButton", definition);
    factory.initializeBean(addNewButton, "addNewButton");
    factory.initializeBean(deleteButton, "deleteButton");
    

    应用程序已成功启动。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-04
      • 1970-01-01
      • 2012-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-24
      • 2020-02-23
      相关资源
      最近更新 更多