0

I have the following bean definition in app-context.xml file:

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

And I'm trying to create deleteButton and addNewButton beans manually, by code:

    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");

Beans addNewButtonActionListener and addNewButtonActionListener, which deleteButton and addNewButton beans depend on, are defined with annotations:

@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(); 
        }        
    }
}

And ActionListenerButton class looks like this:

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);
    }
}

But it seems that bean buttonPanel (which is defined in xml) are created before addNewButton and deleteButton beans (which should be initialized by factory, by code), because I get NoSuchBeanDefinitionException with description "No bean named 'deleteButton' available":

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

How to make Spring to instantiate deleteButton and addNewButton beans before buttonPanel bean?

P.S. Full app-context.xml now looks like this:

<?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

Or maybe I was wrong, and my code with AutowireCapableBeanFactory didn't really create beans and add them in Spring container at all... Because when I added lazy-init="true" attribute in xml to mainFrame, mainPanel and buttonPanel beans and invoked mainFrame after code with factory:

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

ctx.getBean("mainFrame");

I get the following output in console:

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

I.e., Spring returns only cached instances of singleton beans addNewButtonActionListener and deleteButtonActionListener, bot doesn't create and return instances of beans addNewButton and deleteButton.

Ksenia
  • 3,453
  • 7
  • 31
  • 63

1 Answers1

0

I found answer in the related SO question. After changing code on:

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");

application was successfully launched.

Ksenia
  • 3,453
  • 7
  • 31
  • 63