| IocContainerInternals/AdvancedStuff/ScriptingAndXmlConfigurationFiles |
UserPreferences |
| JicarillaWiki | FrontPage | Overview | Articles | GetStarted | Sourceforge Project Page | RecentChanges |
This wiki is in 'slumber' mode, just like its associated sourceforge project. Edits are disabled, the content is potentially stale and is not maintained. That said, it contains some really useful stuff still. Enjoy!
(part of the IocContainerInternals paper)
Well, that was a lot of talk about container internals. Let's start talking about how they actually interface with the application developer. In the majority of cases, this is not just pure java. Some kind of scripting language, or xml configuration files (or an xml-based scripting language), or some custom heuristic or config file, or a mix of those, is used to make the task of assembling components into a container easier.
We had some pseudocode a while back for constructing an imaginary episode of the simpsons:
function assemble()
{
add( Bart )
add( Homer )
add( Lisa )
add( Maggie )
add( Marge )
}
function wire() { perform-equivalent-of-previous-sample-here() }
assemble()
wire()
getMarge().frown()
After all the talk about all those container interfaces and patterns, let's just take a look at a plethora of different ways of doing the same thing...
Without any kind of helper objects, facade, builder, or something like that, we're doing a lot of 'wiring' by hand. Maybe something like...
ContainerImpl container = new ContainerImpl();
container.initialize();
Resolver resolver = container.getResolver();
container.addCacher(
Bart.class,
new SingletonCacher(
new BeanFactory(
BartImpl.class, resolver
)
)
);
container.addCacher(
Lisa.class,
new PerThreadCacher(
new BeanFactory(
LisaImpl.class, resolver
)
)
);
/* ... add other components here ... */
container.start();
Marge marge = (Marge)container.getInstance( Marge.class );
marge.frown();
Let's consider a builder-like coding pattern that does exactly the same thing...
Container container = Builder.newContainer()
.add( BartImpl.class, Builder.SINGLETON )
.add( LisaImpl.class, Builder.PER_THREAD )
/* ... add other components here ... */
.create();
Marge marge = (Marge)container.getInstance( Marge.class );
marge.frown();
Let's consider a standalone container that reads a groovy script on startup. The file might contain something like this...
add( BartImpl.class, SINGLETON ) add( LisaImpl.class, PER_THREAD ) /* ... add other components here ... */ get( "Marge" ).frown()
That same standalone container, reading an xml file like this one...
<container>
<components>
<component
id="bart"
type="Bart"
implementation="BartImpl"
lifestyle="singleton"/>
<component
id="lisa"
type="Lisa"
implementation="LisaImpl"
lifestyle="per-thread"/>
<!-- ... add other components here ... -->
</components>
<post-startup>
<method-call refid="marge" methodName="frown"/>
</post-startup>
</container>
Now consider a container that has an ant task associated with it. The ant task will parse your source tree and generate an XML file much like the one above for you. You might use javadoc attributes like this...
/** @component type="Bart" id="bart" lifestyle="singleton" */
class BartImpl implements Bart
{
/* ... */
}
/** @component type="Lisa" id="lisa" lifestyle="per-thread" */
class BartImpl implements Bart
{
/* ... */
}
/* ... add other components here ... */
and then you might implement the "post startup" bit via a JMX management interface.
These approaches seem quite different from each other when you just glance at them. The first one is a complex mess of java method calls but is the most "embeddable" in other java applications (this sample is real easy to run in an IDE, for example). The scripting language example is extremely short but requires learning another language. The XML version has all the benefits of XML but is painful to read. The attribute-based example means you don't need extra source files besides those of your components at all, but does distribute the assembly information across multiple (possibly hundreds) of build files.
At second glance, I hope you can see how these approaches are all reasonably equivalent: all of them accomplish exactly the same, namely the correct execution of the pseudocode example at the start of this page. The current direction in which many container efforts are headed is a very clean seperation between the choice of end-user interaction and their "core" IoC implementation.
I just told you all those approaches are very similar. But many people disagree with me there, for a couple of reasons...
When you declare your component assembly, there's clear constraints on how you can interact with the system. The declaration can be verified and checked programmatically. You can't "break out of the box" that the container creates for you. "Hacking" things together in a way that's different from how the container designers intended is made very difficult. Proponents of this kind of declarative assembly say its very secure. They'll also say a declarative solutions is clean, because you have a very strict responsibility split, and people in various roles are not really allowed to mess with each others responsibilities.
People in favor of a scripted approach will be happy to point out that the use of scripting is more flexible (you can often "break out of the box" and do totally different things from what the original designer intended) and more powerful (scripting languages have things like for loops and if statements).
Advantages of doing things in pure java is that it is very portable, light on dependencies, and also very testable. In more practical terms, there's heaps of support for unit testing of java applications, java has a strong syntax with lots of syntax verification (ie, pick any IDE), and of course to run a java program you need nothing but the JVM, which we already need anyway. The pure java approach doesn't usually result in a very clear split of responsibilities, nor is it very flexible (you'll ship compiled code in an application, and that is difficult to change), but it is very powerful.
Clearly, the choice of assembly style can impact many aspects of your application, and is a tough one to make. That's why "religious wars" are fought over it :-D
TODO
TODO
TODO