Announcement Announcement Module
Collapse
No announcement yet.
Custom beans and nesting Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Custom beans and nesting

    We are using beans instantiated via custom XML elements/schema. We have some nested tags, similar to the example shown in B.7.1 in your documentation.

    For parsing the nested tags, however, we do the following

    Code:
    BeanDefinitionParserDelegate delegate = parserContext.getDelegate();
    NamespaceHandler handler = delegate.getReaderContext().getNamespaceHandlerResolver().resolve(namespaceUri);
    handler.parse(childElement, new ParserContext(delegate.getReaderContext(), delegate, null));
    The issue we're seeing is that if another bean is configured to have one of these child beans injected, that child bean seems to be re-instantiated (instead of the instance already injected in the parent bean being used). Is this because the child beans is created as child definition of the parent? if so, is it possible to specifically make those child beans root bean definitions in their own right?

  • #2
    The problem is not clear for me. You say that you have a bean which has a parent bean name defined at the 'parent' attribute and that parent bean has a local bean set as a one of its properties, right? And instance of that aggregated bean is created two times during child bean instantiation? If so, that's correct behavior unless you specified abstract='true' for the parent bean. Parent-child feature adjusts only bean definition (look at org.springframework.beans.factory.support.Abstract BeanDefinition.overrideFrom() for implementation details). So, if you have a two beans that should be instantiated (parent and child) and both of their definitions contain information about local bean as the property, two distinct local beans objects are created.

    Comment


    • #3
      No, sorry, let me see if I can be more specific. Basically I have XML that looks like this:

      HTML Code:
      <SomeRoot>
         <ChainingComponent id="MainComponent">
            <FileComponent id="File" />
            <HttpComponent id="HTTP" />
            <SVNComponent id="SVN" />
         </ChainingComponent>
      
         <SomeOtherBean id="foo" componentRef="MainComponent" />
      
         <SomeOtherBean id="bar" componentRef="File" />
      <SomeRoot>
      So, the beans behind Chaining, File, Http, and SVNComponent all implement a common interface. The custom parser for ChainingComponent delegates the parsing and creation of the BeanDefinitions for the child components using the code given in my initial post. Looking at the Spring debug output I can see that it's treating File, HTTP, and SVNComponents as child bean definitions of ChainingComponent. Which makes sense. The custom parser for SomeRoot uses that same code to get BeanDefinitionParsers for ChainingComponent and SomeOtherBean.

      Now, the behavior I'm seeing, that I didn't expect, is that *two* instances of ChainingComponent are being created. According to the Spring debug output one is being created as a root definition with ID "MainComponent". That was the expected behavior. Then a second one is created with some name about it being inner child bean #X of SomeRoot.

      I see something similar when the FileComponent is parsed except this time I see three get created. The third one is created and then injected in to the SomeOtherBean instance that references that FileComponent.

      I've ensured that the scope of all of these beans is explicitly set to Singleton, though my understanding from the documentation and the code is that that is the default scope anyways.

      So my thought was that part of the problem may be that this inner-bean relationship might be the root cause. Do you think that might be the case, and if so, is there a way I can change my parsing code such that those beans are treated, and registered, as root bean definitions?

      Comment


      • #4
        Ok, I understand the problem now

        It seems that the problem root is parsing of the 'componentRef' attribute during bean definition construction. Standard Spring config parser uses RuntimeBeanReference objects for that:

        org.springframework.beans.factory.xml.BeanDefiniti onParserDelegate.parsePropertyValue()
        Code:
        ...
        		if (hasRefAttribute) {
        			String refName = ele.getAttribute(REF_ATTRIBUTE);
        			if (!StringUtils.hasText(refName)) {
        				error(elementName + " contains empty 'ref' attribute", ele);
        			}
        			RuntimeBeanReference ref = new RuntimeBeanReference(refName);
        			ref.setSource(extractSource(ele));
        			return ref;
        		}
        ...
        Such properties are correctly resolved later. However, your custom parser seems to define referenced bean as an independent aggregated bean.

        Comment


        • #5
          Okay, I had wondered if it was something like this as I use the bean references in other locations. However, I couldn't see the proper way to create the bean reference object, specifically how to get the bean name. Is there a way to interrogate the BeanDefinition, created by the NamespaceHandler#parser method, or do I have to have some custom code that reads the child DOM Elements and pulls the IDs from there? I'd really prefer not to do the later because this code already exists in my custom AbstractBeanDefinitionParser#resolveId method.

          Comment


          • #6
            Alright, I was able to get this working for the exact use case I posted before. However, the inability to access to the ID for a bean is a problem with a related use case. Take a slightly modified example

            HTML Code:
            <SomeRoot>
               <ChainingComponent id="MainComponent">
                  <FileComponent file="/path/to/some/file" />
                  <HttpComponent url="http://example.org/file/to/something" />
               </ChainingComponent>
            
               <SomeOtherBean id="foo" componentRef="MainComponent" />
            
               <SomeOtherBean id="bar" componentRef="File" />
            <SomeRoot>
            In this case the child components do not have an attribute that serves as an ID. Instead they may construct and ID or be marked to auto-generate an ID. So, for this case, the method you had suggested wouldn't work.

            Is there a way to get the ID for a bean given its definition?

            Comment


            • #7
              You want to be able to use a non-Spring-standard syntax for bean relations definitions. Custom xml namespace is introduced for that. Hence, it's your responsibility to provide a NamespaceHandler and BeanDefinitionParser that are able to resolve the relations in the way you want. I pointed you to the exact underlying mechanism that should be used for registering bean as dependencies at the bean definition objects (RuntimeBeanReference). So, you only need to program the 'xml -> bean definitions' stuff.

              Comment


              • #8
                Actually, you didn't give the information.

                As I've already said, I have the custom parsers and namespace handling finished. You're example works fine if
                • The child element has an explicit ID attribute
                • You don't mind replicating code for extracting that information from the element in every location that element may be used

                At the time of parsing the containing bean you have to the BeanDefinitionBuilder and current ParserContext. From the ParserContext you can gain access to the BeanRegistry and NamespaceHandler and that in turn can give you the BeanDefinition for the inner beans.

                As best as I can tell from the Javadocs, none of those gives you access to the single piece of data you need to create a reference for that child bean, namely the ID of that bean.

                So, that's what I was asking about in the second, slightly modified, use case. How do you get access to the ID if the child element does not have an explicit ID attribute?

                Comment

                Working...
                X