Как связать бины из xml c бинами, созданными вручную (в коде)?

127
23 марта 2018, 12:22

У меня есть следующий файл 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" lazy-init="true">
                <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" lazy-init="true">
            <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" lazy-init="true">
            <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>

И я пытаюсь создать deleteButton и addNewButton бины вручную, в коде:

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("deleteButtonActionListener");
deleteButton.setActionListener(deleteActionListener);
AutowireCapableBeanFactory factory = ctx.getAutowireCapableBeanFactory();
factory.autowireBean(addNewButton);
factory.initializeBean(addNewButton, "addNewButton");
factory.autowireBean(deleteButton);
factory.initializeBean(deleteButton, "deleteButton");
ctx.getBean("mainFrame");

Бины addNewButtonActionListener и addNewButtonActionListener, от которых зависят beanButton и addNewButton, определяются с помощью аннотаций:

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

Но при запуске приложения я получаю NoSuchBeanDefinitionException с описанием "No bean named 'deleteButton' available":

14:04:49.677 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'itemScrollPane'
14:04:49.677 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'itemScrollPane'
14:04:49.680 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'itemTable'
14:04:49.701 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Eagerly caching bean 'itemScrollPane' to allow for resolving potential circular references
14:04:49.708 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'itemScrollPane'
14:04:49.708 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'itemTable'
14:04:49.708 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'itemList'
14:04:49.709 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
14:04:49.779 [main] DEBUG o.s.c.s.ClassPathXmlApplicationContext - Unable to locate LifecycleProcessor with name 'lifecycleProcessor': using default [org.springframework.context.support.DefaultLifecycleProcessor@6eda5c9]
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

Насколько я понимаю, вышеописанный код:

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("deleteButtonActionListener");
deleteButton.setActionListener(deleteActionListener);
AutowireCapableBeanFactory factory = ctx.getAutowireCapableBeanFactory();
factory.autowireBean(addNewButton);
factory.initializeBean(addNewButton, "addNewButton");
factory.autowireBean(deleteButton);
factory.initializeBean(deleteButton, "deleteButton");

возвращает созданные с помощью аннотаций экземпляры бинов addNewButtonActionListener и deleteButtonActionListener, но при этом не создаёт экземпляров бинов addNewButton и deleteButton. Как можно создать эти бины программно (или через аннотации), без определения их в xml, и добавить их в контейнер Spring? Можно ли это вообще сделать с помощью AutowireCapableBeanFactory?

Answer 1

Я нашла ответ на свой вопрос в вопросе SO. После изменения кода на:

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

приложение запустилось.

READ ALSO
Selenium WebDriver запуск группы тестов в одном экземпляре браузера

Selenium WebDriver запуск группы тестов в одном экземпляре браузера

Сейчас мои класс с тестами выглядит примерно такТо есть браузер запускается снова и снова для каждого теста Можно ли как-то сделать так, что...

144
Spring MVC. Tomcat отдает 404

Spring MVC. Tomcat отдает 404

Скачал готовый качественный Maven-проект с github'а, удалось его запустить, но Tomcat относительно всех адресов отдает 404

176
Суть платформонезависимости Java

Суть платформонезависимости Java

Обычно, когда новичкам рассказывают про преимущества Java, упоминают платформонезависимостьЯ правильно понял, что разработав Java-приложение,...

156
Как сохранить String в PDF (Java)

Как сохранить String в PDF (Java)

Делаю некую задачу, после отправки POST запроса приходит ответ, его я сохраняю в String-е и дальше хочу сохранить в PDF(результат POST запроса PDF файл)

166