Ссылки между компонентами Spring при использовании NameSpaceHandler - PullRequest
2 голосов
/ 03 июня 2010

Я пытаюсь использовать пространство имен контекста Spring для создания некоторых существующих объектов конфигурации в приложении. Я определил контекст, и, если он работает удовлетворительно, то в значительной степени он есть - однако мне бы хотелось, чтобы один бин, определенный моим пространством имен, неявно ссылался на другой:

Рассмотрим класс с именем 'Node':

public Class Node {
  private String aField;
  private Node nextNode;
  public Node(String aField, Node nextNode) {
  ...
}

Теперь в контексте Spring у меня есть что-то вроде этого:

<myns:container>
  <myns:node aField="nodeOne"/>
  <myns:node aField="nodeTwo"/>
</myns:container>

Теперь я бы хотел, чтобы nodeOne.getNextNode() == nodeTwo было true. Так что nodeOne.getNextNode() и nodeTwo ссылаются на один и тот же экземпляр компонента. Это в значительной степени соответствующие части, которые я имею в моем AbstractBeanDefinitionParser:

public AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
  ...
  BeanDefinitionBuilder containerFactory = BeanDefinitionBuilder.rootBeanDefinition(ContainerFactoryBean.class);
  List<BeanDefinition> containerNodes = Lists.newArrayList();
  String previousNodeBeanName;

  // iterate backwards over the 'node' elements
  for (int i = nodeElements.size() - 1; i >= 0; --i) {
    BeanDefinitionBuilder node = BeanDefinitionBuilder.rootBeanDefinition(Node.class);
    node.setScope(BeanDefinition.SCOPE_SINGLETON);

    String nodeField = nodeElements.getAttribute("aField");
    node.addConstructorArgValue(nodeField);

    if (previousNodeBeanName != null) {
      node.addConstructorArgValue(new RuntimeBeanReference(previousNodeBeanName));
    } else {
      node.addConstructorArgValue(null);
    }

    BeanDefinition nodeDefinition = node.getBeanDefinition();
    previousNodeBeanName = "inner-node-" + nodeField;
    parserContext.getRegistry().registerBeanDefinition(previousNodeBeanName, nodeDefinition);

    containerNodes.add(node);
  }
  containerFactory.addPropertyValue("nodes", containerNodes);
}

Когда создается контекст приложения, мои Node экземпляры создаются и распознаются как одиночные. Кроме того, свойство nextNode заполняется экземпляром Node с предыдущей конфигурацией узлов - однако это не тот же экземпляр.

Если я выведу сообщение журнала в конструкторе Node, я вижу два экземпляра, созданных для каждого определения компонента узла.

Я сам могу придумать несколько обходных путей, но я стремлюсь использовать существующую модель. Так может кто-нибудь сказать мне, как я могу передать эти ссылки bean-компонентов времени выполнения, чтобы я получил правильное поведение синглтона для моих Node экземпляров?

Ответы [ 3 ]

1 голос
/ 03 июня 2010

Я немного смущен вопросом. Когда вы говорите nodeOne.getNode() == nodeTwo вы имеете в виду nodeOne.getNextNode()? Что-то вроде следующего не работает?

<bean id="nodeOne" class="org.foo.Node">
    <constructor-arg index="0" value="nodeOne" />
    <constructor-arg index="1" ref="nodeTwo" />
</bean>
<bean id="nodeTwo" class="org.foo.Node">
    <constructor-arg index="0" value="nodeTwo" />
    <constructor-arg index="1"><null/></constructor-arg>
</bean>

Это потому, что вы не хотите указывать nextNode в Spring и хотите вместо этого иметь настройку связывания в коде?

Как насчет следующего кода? Он просматривает все компоненты типа Node и вызывает на них setNextNode(). Это означает, что вам, конечно, нужно выставить сеттер. Вы также можете захотеть, чтобы ваши Узлы реализовали BeanNameAware, поэтому вам не нужно выполнять aField.

public class NextBeanSetter implements InitializingBean, ApplicationContextAware {
    private ApplicationContext applicationContext;
    public void afterPropertiesSet() throws Exception {
        @SuppressWarnings("unchecked")
        Map<?, Node> beanMap = applicationContext.getBeansOfType(Node.class);
        Node previousNode = null;
        for (Node node : beanMap.values()) {
            node.setNextNode(previousNode);
            previousNode = node;
        }
    }
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
}
1 голос
/ 03 июня 2010

Полагаю, проблема в том, что на самом деле у вас есть два экземпляра каждого определения узла - один как узел верхнего уровня (через registerBeanDefinition), а другой внутри nodes свойства ContainerFactoryBean. Возможно, вы должны поместить RuntimeBeanReference s в узлы в ContainerFactoryBean.

0 голосов
/ 03 июня 2010

Бины объявляются одноэлементными или не одноэлементными (= прототип) Может быть, ваши созданы не одиночными, так что проверьте это с определением в бине:

<bean ... singleton="true" />;

Дополнительная информация: http://static.springsource.org/spring/docs/1.2.9/reference/beans.html#beans-factory-modes

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...