I find myself working more and more with Spring these days, and what I find raises questions. This week, my thoughts are turned toward beans overwriting, that is registering more than one bean with the samee name.
In the case of a simple project, there’s no need for this; but when building a a plugin architecture around a core, it may be a solution. Here are some facts I uncovered and verified regarding beans overwriting.
- Single bean
idattribute in the Spring bean file is of type
ID, meaning you can have only a single bean with a specific
IDin a specific Spring beans definition file.
- Overwriting bean dependent on context fragments loading order
- As opposed to classpath loading where the first class takes priority over those others further on the classpath, it's the last bean of the same name that is finally used. That's why I called it overwriting. Reversing the fragment loading order proves that.
- Fragment assembling methods define an order
- Fragments can be assembled from
<import>statements in the Spring beans definition file or through an external component (e.g. the Spring context listener in a web app or test classes). All define a deterministic order.
- As a side note, though I formerly used import statements in my projects (in part to take advantage of IDE support), experience taught me it can bite you in the back when reusing modules: I'm in favor of assembling through external components now.
- Spring lets you define names in addition to ids (which is a cheap way of putting illegals characters fors ID). Those names also overwrites ids.
- Spring lets you define aliases of existing beans: those aliases also overwrites ids.
- Scope overwriting
- This one is really mean: by overwriting a bean, you also overwrite scope. So, if the original bean had a specified scope and you do not specify the same, tough luck: you just probably changed the application behavior.
Not only are perhaps not known by your development team, but the last one is the killer reason not to overwrite beans. It’s too easy to forget scoping the overwritten bean.
In order to address plugins architecture, and given you do not want to walk the OSGi path, I would suggest what I consider a KISS (yet elegant) solution.
Let us use simple Java properties in conjunction with
ProperyPlaceholderConfigurer. The main Spring Beans definition file should define placeholders for beans that can be overwritten and read two defined properties file: one wrapped inside the core JAR and the other on a predefined path (eventually set by a JVM property).
Both property files have the same structure: fully-qualified interface names as keys and fully-qualified implementations names as values. This way, you define default implementations in the internal property file and let uses overwrite them in the external file (if necessary).
As an added advantage, it shields users from Spring so they are not tied to the framework.
Sources for this article can be found in Maven/Eclipse format here.