Marc Kannegiesser
23.04.2010Modular Web-Applications based on Spring
Many of the Web-Applications we develop for our customers are based upon our small Framework on top of Spring / Spring MVC. This framework basically brings often used components ready-to-use (or ready to customize) and – of course – makes things even simpler than Spring already does.
Modular design of applications brings a lot of advantages but – as always – also some disadvantages. A modular structure can help to increase cohesion and let developers focus on the function their concrete module has. Another big thing is reusability. So the core framework already brings functionality that is used in all projects that depend on the framework so far: user management for example. On the other hand modular design also brings complexity. I think its business of a framework to hide this complexity from the user (developer in this case). Nevertheless its good when the developer knows (and understands) what goes on under the hood and (even more important) can easily extend the framework where he needs to.
As I mentioned we use Spring / Spring MVC as a base for many projects. Spring provides a lot of points where you can extend the framework by implementing interfaces and defining or injecting these implementations to the classes that do the real work (like interceptors in AnnotationHandlerMapping). In most cases this is enough. If you start to develop a modular application it is not. At least it was not for our case.
Our Web-Applications based on the mentioned Framework always use at least two “modules”: The core-module (that brings
user management and some nice features to make coders happy) and the application itself. Each of these modules brings
its own bean-configurations which are loaded all together using a wildcard resource like the following that reads all
beans.xml
files within any subfolder ofMETA-INF
in the applications classpath: classpath*:META-INF/**/beans.xml
This is a really simple way how the modules can interact since one module can provide a Service another depends on.
Now, the big problem is, that each module often brings its own components that have to be registered to Springs
“services”. Let me explain the problem to you by talking
aboutinternationalization again.
Each module brings its own resource bundle containing the internationalization for its web-interface components. Spring
provides a simple way to register aMessageSource
by defining one bean with idmessageSource
in your context. And that
is exactly the problem. It isonebean. So you need a way where each module can register its ownMessageSource
, even
if Spring only supports one. So our framework has to handle this, because it also introduces the modular structure.
The “out of the box” way that would work is that the application, that assembles all these modules together defines the
MessageSource
with all basenames (of the properties-files) the application uses. But this would be part of the
mentioned complexity that should be kept away from the daily business and brings some other problems in (what, if one
module wants to store its internationalization within the database?…).
So what did we do? We use a Simple plugin-mechanism a colleague developed and Synyx publishes OpenSource:Hera
We use a bean that gets registered asMessageSource
to Spring that takes care of dispatching the message-resolving
requests to the real MessageSources
implementations spread all over the modules.
<bean id="messageSource">
<property name="sources">
<plugin:list class="org.synyx.minos.message.ModuleMessageSource"/>
</property>
<property name="useCodeAsDefaultMessage" value="true" />
</bean>
So this registers ourDispatchingMessageSource
that gets injected into all beans within the context, implementing
ModuleMessageSource
by Hera. This pretty much does the trick. The reason that we useModuleMessageSource
instead of
Springs built-inMessageSource
-interface is on the one hand so that we can do some performance-tweaks and on the
other hand so that we dont get any “unwanted” implementations, which get to the context somehow.
With some simple dispatching logic withinDispatchingMessageSource
we found a powerful way to conquer the insufficiency
of Spring, in conjunction with our modular system.
List candidates = sources.getPluginsFor(getPrefixFromCode(code));
for (MessageSourcePlugin source : candidates) {
MessageFormat format = resolveMessageWithSource(source, code, locale);
if (null != format) {
return format;
}
}
By the way, we use this mechanism a lot when it comes to easily extending functionality of the framework-core including
HandlerInterceptor
,PropertyEditorRegistrar
and our Modules itself.