Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Please improve BeanFactory support for containers & collections [SPR-230] #4961

Closed
spring-issuemaster opened this issue Jul 20, 2004 · 24 comments
Closed

Comments

@spring-issuemaster
Copy link
Collaborator

@spring-issuemaster spring-issuemaster commented Jul 20, 2004

Ben Hutchison opened SPR-230* and commented

Objects that contain other objects (common examples include Swing Containers and the Collections classes) frequently cannot have their initial state specified through setting javabeans properties alone.

This is a request for the spring core to include extensions to the syntax of the applicationContext.xml to allow such objects to be setup completely from XML. Some reasonably conveinient syntax is needed that allows methods like "add()" to be invoked on the bean at construction time. Ideally, I think the syntax should allow any arbritrary methods to be called on a bean during preparation.

There is a clear precendent to be found in the design of the java.beans.XMLEncoder/Decoder classes, designed to support long term bean perisstence. Consider the extract below generated by XML-serializing a List:

<?xml version="1.0" encoding="UTF-8" ?>
<java version="1.4.2_03" class="java.beans.XMLDecoder">
<object class="java.util.ArrayList">
<void method="add">
<string>item</string>
</void>
</object>
</java>

Evidently, it was recognized as an important feature as its present from the first release of the peristence mechanism.

Consider what is not readily doable from XML without this feature:

  • Cannot create any Swing component heirarchies.
  • Cannot initialize custom collections (ie anything not a vanilla set/map/list impl), unless it provides a copy-constructor.
  • Cannot init arbritrary state on objects which do not observe the get/setXXX convention (and legacy code is littered with them).

Affects: 1.0.2

Attachments:

Issue Links:

  • #7195 Allow setting of properties without a javabean setter ("is duplicated by")
  • #7222 Support JavaBean Eventing methods in Spring ("is duplicated by")
  • #7488 "adder" injection in addition to "setter" (invoke addXX() instead of setXX()) ("is duplicated by")
  • #5727 support "add"-style List injection ("is duplicated by")

32 votes, 28 watchers

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jul 22, 2004

Andreas Senft commented

I would appreciate such a feature since it could allow for even better configuration. Especially registration of listeners does often require invocation of an addListener() or registerListener() method. This could then also be supported by spring configuration directly.

My proposal for the xml structure is like that:

<bean id="MyBean" class="mypkg.MyClass">
<method name="addListener">
<method-args>
<method-arg>...</method-arg>
</method-args>
</method>
</bean>

  • the method-arg tag behaves similar to a constructor-arg tag
    (either also allowing type and index specification or be assigned by index of occurence)

  • the method-args tag should be capable of containing multiple arguments in order to invoke methods with more than one parameter.
    This is sometimes useful for more complex registrations.

  • the method tag could be allowed to contain more than one (but at least one) method-args tag. This would make things more concise in the case of multiple invocations

In my opinion, that would be a useful and elegant extension.

Regards,
Andreas

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jul 22, 2004

Rod Johnson commented

I can see valid usages for this. I've considered it from time to time. My main reservation is that I don't think that people should program in XML. But of course we are still dealing with configuration.

I think the best approach may be to allow for configuration in bean definition via scripting, in Groovy or another language. I would prefer that to an XML mechanism. Thoughts?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jul 22, 2004

Andreas Senft commented

I see your point, but I am not sure if I interpret the scripting proposal correctly.

Does it mean to have some kind of tag within the bean definition containing scripting code?
That would indeed be more flexible, yet It would invite even more to perform programming within the configuration (although not using xml directly for that).
Further it has to be considered how to reference values and references of the "normal" configuration from within the cripting code.

Andreas

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jul 23, 2004

Ben Hutchison commented

I would prefer to use XML. I dont know groovy and I dont want to learn it simply to configure my application context. I would like to do all my context setup in one place, where I do it now.

The line between configuration and programming is a fine and indisinct one. Are you suggesting invoking a method "add(Value)" is programming, whilst "setValue(value)" is not? IMHO, an good indicator of "programming" is the presence of conditional & looping constructs.

My motivations for using an app context XML file are to specify the static initial state, relationships and dependencies of my application in a declarative, textual form, that faciltitates automatic dependecy injection.

If I need to call methods of arbritrary signature to express these states/relationships, I wish to be able to.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Feb 28, 2005

Matthew Sgarlata commented

You might want to take a look at the OGNL plug in for Spring. It's not formally released but you might be able to push it to release if you're interested in it.

http://www.ognl.org/resources/ognl-spring.zip

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jan 26, 2006

Rob Harrop commented

After much discussion we have decided the approach to take here is to support a simple convention in the container and then rely on further configuration mechanisms (such as scripting) and expression languages in the container.

The convention we are going to support is:

addXXX(Object o) where meets this contract (!(o instanceof Collection) && !(o.getClass().isArray)).

We have yet to decide on the final XML syntax but it is expected to be similar to this:

<property name="location" mode="autodetect|add|set">
<list>
<value>foo.txt</value>
</list>
</property>

We believe this hits the sweet spot of covering a reasonable number of use cases without resorting to programming in XML. Full on method invocations will be supported by scripting.

Rob

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Nov 9, 2006

Endre Stølsvik commented

This bug can be considered the mother of #5727, #7222 and #7488 (the children are dupes) :

  • #5727: "Some classes don't expose a plublic setter for internal list (as an example, commons-configuration CompositeConfiguration) bue some addXXX(item) method."

  • #7222: asks for "adder injection" based on the requirement of registering event listeners, which often are registered/injected in beans using addListener methods.

  • #7488: "adder" injection in addition to "setter" (invoke addXX() instead of setXX()), and in addition possibilities for arbitrary method invocations (ala Jetty configuration).

And I personally don't see the big deal about "programming in XML": why not leave the possibilities open to support old/legacy "beans" and other kind of libraries?

You do supply a bunch of "hack-tools" to do the exact same thing - in particular the org.springframework.beans.factory.config.MethodInvokingFactoryBean. This would just clean up the syntax - it gets really dirty by using these hack-tools - or one have to resort to actual code and/or tie-ins with the container.

Considering how many forum-posts that result in suggestions around "oh, just make a factory-bean that does it for you", "use a post-processor" and whatnot, it clearly points to a need to more easily and cleanly do such set-up tasks.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Dec 17, 2006

Dave Syer commented

Should it be possible to add more than one element to a collection using the addXXX method? This is more of a problem because the bean factory doesn't like having more than one reference to the same property within a single bean definition. I.e. you can't do this:

<bean id="bar" parent="foo">
	<property name="comment" value="bucket" mode="add"/>
	<property name="comment" value="crap" mode="add"/>
</bean>

without some special case logic in the bean factory.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Dec 17, 2006

Dave Syer commented

N.B. if you only need to be able to add a single object using addX you can do it within the normal Java Beans framework (no need for changes to bean factory) - create a BeanInfo for the object that has the addX method and specify adX as the setter for property "x". Most of the use cases for this enhancement might be satisfied with that simple solution. Where there are large libraries of legacy objects (like Swing) I guess it might be more onerous, but it might not be if the adders are sufficiently high up in the class hierarchy (which they certainly are for listeners etc.).

Example BeanInfo for addComment method on class Bogus:

public class BogusBeanInfo extends SimpleBeanInfo {
public BeanInfo[] getAdditionalBeanInfo() {
BeanInfo info = new SimpleBeanInfo() {
public PropertyDescriptor[] getPropertyDescriptors() {
Method setter = ClassUtils.getMethodIfAvailable(Bogus.class, "addComment", new Class[] { String.class });
PropertyDescriptor comment;
try {
comment = new PropertyDescriptor("comment", null, setter);
} catch (IntrospectionException e) {
throw new BeanDefinitionValidationException("Cannot modify descriptor for comment in Bogus");
}
return new PropertyDescriptor[] { comment };
}
};
return new BeanInfo[] { info };
}
}

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Dec 17, 2006

Endre Stølsvik commented

That should most definitely and obviously be possible if the feature is implemented, less it be a very crippled and strange implementation ("add" definately sounds like a thing you could do more than once), re #7222 but in particular #5727.
For myself, i will do "pane.addTab()"-style stuff, and yes, I'd like to add more than one tab. Same goes for event listeners, and general collection-oriented stuff: The sole reason for the bean having an "add" instead of "set" is this exact point.

PS: Why not make the arbitrary method invocation feature while you're at it? (Check out #7488 for a syntax-idea)

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jan 7, 2007

Juergen Hoeller commented

This has not been implemented yet, and there is no work in progress yet (else this issue would be marked as 'in progress').

We're currently working on the Spring 2.0.2 and subsequently 2.0.3 release. Spring 2.1 (which might address this issue) is scheduled for Q2 2007.

Juergen

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Feb 10, 2007

Jing Xue commented

Over the weekend I actually set out to try and implement a patch for the add(Object o) approach Rob proposed above, but soon realized that add(Object o) would have some significant limitation on usability, because if a real bean (such as a domain object) decides to expose a collection only through addSomething(), chances are that addSomething() is going to take a parameter type more specific than Object, and can't be "pinpointed" through reflection (blindly taking the first addSomething that has one parameter seems a bit arbitrary...).

I have voted for this issue, and if I could vote for a solution, I now think I would vote for the one Andreas proposed, which allows for a formal description of the adder method.

I might add that I have read at various places that Spring forces designs - especially DDD - to open up what would have been completely internal collections. Fixing this issue will go a long way towards addressing that concern (however exaggerated it may be :-) ).


Jing Xue

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jan 7, 2008

Juergen Hoeller commented

The current plan is to support this through the @Autowired annotation, annotating add-style methods that will be invoked for each matching bean in the context. There are no plans to support this with external metadata as well, mainly because there's no universally agreed-on format for externally specifying such add methods (in contrast to standard bean properties and constructor arguments, where the patterns are essentially older than Spring as a product).

Juergen

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Nov 30, 2008

Dirk Scheffler commented

I go with Andreas Senft and would see the described method tag as a very valuable feature. Limiting spring to simple bean properties is not powerfull enough to connect all kind of objects. Calling an arbitrary method is not more or less programming than calling a setter method. The method feature is very clean in design while the other suggestions like add properties are interfering with the property logic where properties are uniqe. Method calls should be able to be repeated for the same method like in the add case needed.

I really miss such a feature and like to encourage Juergen to think about it. Connecting objects that were never written to work with Spring or that are written to work not only with Spring should be supported as well. Since the existing constructor call is doing almost the same like the required method tag, it should not be that difficult to create such a feature.

Juergen, if you consider enabling the feature, I will offer you my help.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Nov 30, 2008

Dirk Scheffler commented

To the naming of the feature. I think method is not the right terminus since this wouldn't be a method declaration. I would like to suggest two suitable names:

  • call
  • invoke

The method method should be called before the init method would be called just like it is in the case of the setters.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Mar 21, 2009

Tomasz Wysocki commented

Another proposed approach (+patch).

The idea is not to introduce any new "call" or "invoke" syntax but reuse existing "property" elements to call arbitrary methods. The binding between property name and the method is defined used new "define" elements.

<bean id="rod" class="test.beans.TestBean">
<define name="friend" write="addFriend"/>
<property name="friend">
<ref local="jenny"/>
</property>
<property name="friend">
<ref local="david"/>
</property>
</bean>

Above defines new property called friend which when set will call "addFriend" method.
If method is not overloaded then name of the method alone will suffice, otherwise you need to specify parameters like this "addFriend(java.lang.Object)".
Properties defined with "define" by default can be set multiple times. This is specified with attribute "multiple" which is "true" by default.

Things like nested properties are also supported.

 <bean id="parentWithChild" class="org.springframework.beans.factory.xml.XmlBeanCustomPropertyDescriptorTests$Parent">
    <define name="childArr.elements" write="addElement"/>
    <property name="childArr">
        <list>
            <bean class="org.springframework.beans.factory.xml.XmlBeanCustomPropertyDescriptorTests$Child"/>
            <bean class="org.springframework.beans.factory.xml.XmlBeanCustomPropertyDescriptorTests$Child"/>
        </list>
    </property>
    <property name="childArr[0].elements" value="SomeElement1"/>
    <property name="childArr[1].elements" value="SomeElement2"/>
    <property name="childArr[1].elements" value="SomeElement3"/>
</bean>

Also properties can be inherited from parent.

Behind the scenes it works by overriding the property descriptors obtained from CachedIntrospectionResults per BeanWrapper instance (not globally per jvm).
So each bean "define" tags are local to a bean.

Patch against r678 of https://src.springframework.org/svn/spring-framework/trunk

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Mar 21, 2009

Dirk Scheffler commented

Dzien dobry Tomasz,

thank you for caring about this issue. I think your approach has to some disadvantages. It introduces a new construct, that again handles special methods that take only one parameter. Also there is the issue that the same property name can now appear more than once which confuses concepts and thus also validation.

I still suggest to support arbitrary method calling beside normal properties. The argument that this would be programming is not correct because it depends on the case. Of course it could be misused. But this is with everything the case. For example there are setters to some classes that are not meaned for configuration. They could also be called. Image a legacy method like this: registerXForY(Y key, Y value). How would you cover that by your current approach?

BTW: I would suggest to introduce an annotation that allows to mark methods including simple setters. This annotation could be @Configurable. IDEs like SpringIDE could suggest those methods or properties in code completion in before others that do no have this annotation.

I still think limiting DI to properties covers a lot but is not sufficient. The property based DI could be sufficient in a programming language that is designed with IOC/DI in mind. It could have special properties or annotations that mark normal properties from the beginning when meaned for DI. But a language like Java contains so many legacy stuff that there is no clear concept of properties in the means of configuration and there is no clear concept of properties at all since they are just a naming convention and not a language pattern.

Please evaluate my thoughts.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Mar 22, 2009

Tomasz Wysocki commented

Dirk, thanks for your interest in my patch.

In fact the patch contains support for calling arbitrary methods. Example below:

<bean id="literalMap" class="org.springframework.beans.factory.xml.HasMap">
<define name="map" write="addEntry(java.lang.Object,java.lang.Object)"/> <!-- if addEntry is not overloaded params are optional -->
<property name="map">
<list><value>foo</value><value>bar</value></list>
</property>
<property name="map">
<list><value>fi</value><value>fum</value></list>
</property>
<property name="map">
<list><value>fa</value><null/></list>
</property>
</bean>

The direct answer to multi-param method support is : any method is in fact a "property setter", only you have array of params instead of a single parameter.

The nice (or ugly) thing about this proposal is that in fact it is an extension to well known BeanWrapperImpl and reuses all the things supported by it (like implicit conversions for instance and property editors).

I've tried to be as little disruptive to current "property" element syntax as possible, and reuse it as much as possible, and also to introduce as few new concepts as possible. But probably it may not feel as "native" as special "invoke" or "call" element. The validation of multiple instances of "property" elements is handled correctly by the patch. But yes it seems clunky to set the same property multiple times if we all recognize that "property" can be set only once. So probably some parts of a patch (like BeanWrapperImpl work) could be accepted and the rest (like xml configuration syntax) reworked to have better look and feel.

I guess the target syntax would be:

<bean id="literalMap" class="org.springframework.beans.factory.xml.HasMap">
<invoke method="addEntry"><value>foo</value><value>bar</value></invoke>
<invoke method="addEntry"><value>fi</value><value>fum</value></invoke>
<invoke method="addEntry"><value>fa</value><null/></invoke>
</bean>
Feel free to check patch for junit tests, xml configurtion examples and the internal changes made to BeanWrapperImpl and other classes.
Thanks for comments!

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Mar 22, 2009

Dirk Scheffler commented

@Tomasz

I am lucky about your answer because:

  1. arbitrary method calling would be possible
  2. you see the issue with muliple instances for the same property and consider an invoke element
  3. normal conversion stuff is working (does this include PropertyEditors?)

I think this would be a nice improvment and I hope the "programming" issue has not so much weight.

What do you think about the @Configurable property for setters and other methods, that could give configurators a better support in what they actually should use with property and invoke elements and what not? A strict designed system could even validate against that and warn the configurator that he addressed properties or methods that are not meaned for configuration. Also imaginable would be an inverse annotation: @NotConfigurable. The two annotations @Configurable and @NotConfigurable would be also a step towards the folks that argument with the "programming" abuse.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Mar 23, 2009

Tomasz Wysocki commented

As for annotation stuff I don't use it much and I've been using spring since its 1.0 times with xml configuration only. Juergen had proposed to support this feature using @Autowired annotations (see comments above). Probably it could have been made that way but from my point of view the xml syntax is still the king and it has to be supported approach.

At least simple util:invoke to simplify the (mis)use of MethodInvocationFactoryBean could be introduced if there is too much resistance to introduce new things in "beans" namespace.

Or maybe we should look at simple invocation facility from different perspective which escapes "programming in xml" argument. If we are doing COP with spring then when calling a method what we are really doing is registering a new plugin (or listener or collaborator in general). Look at #7222 and other similar issues.

Currently spring supports only specifying collaborators at target site like below:

<bean name="composite">
<property name="component" ref="componentA"/>
</bean>
<bean name="componentA"/>

and it supports only one collaborator per property specified at target - "composite" site. What if we "IoC"?

<bean name="composite"/>
<plugin target="composite" role="component">
<bean name="componentA"/>
</plugin>

Not very different but opens a lot of other possibilities like:

  1. you don't have to connect plugins at composite site, rather at component (plugin) site - this would typically require postprocessors.
  2. you can register many "plugins" if target "composite" supports it.
  3. you could specify what methods not only to register but also what methods to use to unregister the plugin!
  4. you could even use interface name instead as role name - so that you don't specify the target name - this is similar to @Autowire by type

Also you can then specify the name of register/unregister methods at "composite" or "plugin" site. I would argue to define such "plug-points" at "composite" site like this:

<bean name="composite">
<plug-point
role="component" // this is an association name - like property name -
[register-method="add{role}"] // optional method name to register plugin, by default it can follow "add{role}" or "register{role}" pattern
[unregister-method="remove{role}"] // optional method name with reverse effect to register
[cardinality="0..n"] // how may collaborators are supported
/>
</bean>

Lots of new ground to cover, but we really start talking plugins! :) But probably too much to make it into 3.0.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Mar 23, 2009

Dirk Scheffler commented

Probably you misunderstood the annotations. They are not a replacement for the XML and they are not comparable to @Autowired. But let us forget the annotations because they seem not to belong here. So I am fully with you to discuss the invocation xml stuff.

I think we should not forget that this feature is not only a search for the best design but mainly a helper for legacy objects that are NOT designed with Spring in mind. So I would still see the best way by having the invocation element as you typed some posts before. A higher level design could be a parallel approach with more efforts to avoid programing in xml.

I find the suggestion to place it in the util namespace (util:invoke) very good. So developers dedicated to a clear cut regarding programming in xml and with a pure spring friendly program design will not find it as a core feature of the beans namespace. My opinion is that you should go that way:

<bean id="someBean" class="SomeClass">
  <util:invoke method="addEntry">
    <value>foo</value>
    <value>bar</value>
  </util:invoke>
  <!-- alternatively in cases where neccessary because of overloading -->
  <util:invoke method="addEntry(java.lang.String,java.lang.String)">
    <value>foo</value>
    <value>bar</value>
  </util:invoke>
</bean>
@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Mar 23, 2009

Dirk Scheffler commented

Regarding the higher level approach. Where I am basically with you is the point that linking should be reversable. But this is should also be possible for normal properties. I think a real good IoC container would manage not only DI (Dependency Injection) but also DS (Dependency Seperation) which would be running after the lifecycle of some bean is beeing finished. I have often tought about this. My dream is a language design for a programming language that has IoC in mind. Java is not really made for that. I am sure that there will come a time where this will be the conclusion of all the IoC experiences: Create a new language that supports separation of concerns (implementation vs. wiring) and this language should have a more direct syntax than XML for wiring. This would be very funny to discuss about such a language. Probably one should start some thread about that idea.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Apr 22, 2009

Jerry Shea commented

my 2 cents: I just want to able to do event wiring in Spring java just like Spring.NET

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jul 7, 2011

Chris Beams commented

Most if not all of the use cases represented here can be addressed with Spring 3.0 and 3.1 using the code-based @Configuration class approach.

Please take a look at this style if you haven't already -- we think that it represents a flexible and natural way to configure any Java object, especially when injection needs don't closely fit the JavaBeans model.

You can get started by looking at the Javadoc for @Configuration, which is rather extensive and complete with cross-references to the other types you'll need to know about. http://static.springsource.org/spring/docs/3.1.0.M2/javadoc-api/org/springframework/context/annotation/Configuration.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
1 participant
You can’t perform that action at this time.