|
From: James C. <jim...@do...> - 2004-02-16 22:30:50
|
I have previously developed web applications using WebWork (1 & 2), and I
have been drawn to the Spring Framework because of its complete IOC support
and more so, its excellent data abstraction layer. Particularly of interest
to me on my current project is its Hibernate support and Transaction
capabilities.
At the moment however, I am focused on some of the web tier architecture. It
may not be for everyone, but I find myself longing for WebWork's simple
configuration of the web tier. I realize that I could integrate Spring and
WebWork and be done with it, but I don't use WebWork's taglib, nor do I want
to introduce another dependency if I don't have to. Right now, I am trying
to get Spring's web MVC framework sorted out. I apologize in advance if I
misstate any facts or statements regarding the Spring Framework's
capabilities or implementation. I am a Spring-chicken at the moment. :-)
I think I understand the various mapping files that must be configured, and
the two, three, or twelve optional variants of each. As I was going through
each of the classes that provide a view resolver (InternalViewResolver,
ResourceBundleViewResolver and XmlViewResolver), I began to wonder if I was
the only person who would like a simplified configuration. I think the
primary differentiation when comparing Spring or WebWork on the Web-tier
alone, is WebWork's simpler and more cohesive configuration file. I don't
think either framework's WEB MVC configuration provides any more general
functionality than the other, but I would like an optional resolver class
that allows a single configuration file to specify the Controllers, URL
Mapping, View Resolvers and the parameters that each may take. I don't think
this would be difficult either because of Spring's pluggability.
In Spring, in order to use a single controller I have to configure the:
viewResolver / instance of ResourceBundleViewResolver, XmlViewResolver,
or InternalViewResolver.
Used by the dispatcher servlet to resolve the ModelAndView's view
name (and locale) to a View class.
This object is retrieved "by name" by the dispatcher servlet, hence
there is only one per servlet context.
handlerMapping / instance of BeanNameUrlHandlerMapping or
SimpleUrlHandlerMapping.
Used by the dispatcher to
There can be many url handlers in a single servlet context since the
dispatcher servlet searches for bean's in its context that implement
the HandlerMapping interface. They can also be sorted in order to
give one handler priority over another when there is a URL that
matches multiple HandlerMappings.
controller / instance of the Controller interface. There are a bunch of
these, and this probably represents one of the more
confusing aspects of Spring.
Each controller provides different functionality from the next, but
it seems like the design suffers a bit here. For example,
AbstractController is extended by MultiActionController,
BaseCommandController and ParameterizableViewController. Since a
controller must pick a single subclass to extend, a developer can
only take advantage of the benefits of one of these controllers. I
preferred WebWork's use of interfaces (and IOC) to drive the capabi-
lities of the Controller. This a moot point however, since I am only
focused on configuration at the moment.
Each of these three configuration areas (four if you count
ExceptionResolvers, five if you count HandlerAdapters, etc.) are powerful in
their flexibility, but somewhat cumbersome for someone coming from WebWork
(or Maverick). For many of us a simpler configuration would be a welcome
augmentation to Spring, and because of Spring's configuration flexibility, I
don't imagine it would take away from any features.
As an example for configuration, consider the following sample from the
petclinic example application that ships with Spring.
-----------------------------------------------------------------------
petclinic-servlet.xml
=====================
<bean id="viewResolver"
class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename"><value>views</value></property>
</bean>
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/welcome.htm">clinicController</prop>
</props>
</property>
</bean>
<bean id="clinicController"
class="org.springframework.samples.petclinic.web.ClinicController">
<property name="methodNameResolver">
<ref local="clinicControllerResolver"/>
</property>
<property name="clinic">
<ref bean="clinic"/>
</property>
</bean>
<bean id="clinicControllerResolver"
class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameR
esolver">
<property name="mappings">
<props>
<prop key="/welcome.htm">welcomeHandler</prop>
</props>
</property>
</bean>
views.properties
================
welcomeView.class=org.springframework.web.servlet.view.JstlView
welcomeView.url=/WEB-INF/jsp/welcome.jsp
-----------------------------------------------------------------------
This is a lot of configuration information when compared to a similar
WebWork configuration. It also is spread across two files, although I
suppose that there is a Spring technique to declare viewResolver data in the
petclinic-servlet.xml file, perhaps by using an XmlViewResolver and
embedding the XML data as an IOC property? Another unclear step is the
knowledge the developer must possess that his
ClinicController.welcomeHandler() method returns the view mapping named
'welcomeView'. It is this name that is looked up in the views.properties
file. This is somewhat unintuitive and other examples have tried to combat
this ambiguity by including the view mappings directly (sort of) in the
Controller configuration.
<bean id="findOwnersForm"
class="org.springframework.samples.petclinic.web.FindOwnersForm">
<property name="formView"><value>findOwnersForm</value></property>
<property
name="selectView"><value>selectOwnerView</value></property>
<property name="successView"><value>ownerRedirect</value></property>
</bean>
This example demonstrates something us WebWork developers have used very
effectively for some time. The view is always returned as a simple string in
WebWork and this maps to the actual view via configuration information. The
same thing is occurring in the Spring example, but in a slightly backhanded
way. The SimpleFormController defines formView and successView as String
properties. If the controller completes successfully, it returns
getSuccessView() as its view and this in turn returns 'ownerRefirect' as the
mapped view. The developer also introduced a new view result called
'selectView'. In order to support this new result view, the developer has to
implement the selectView property in their subclass. Very cumbersome when
compared to WebWork's "always map" approach to view results.
Thanks for sticking with this unintentionally long post. I guess I set all
of this up to suggest an alternative approach to configuring Spring's web
MVC that is more compact and easier for new developers. It is based heavily
on WebWork's configuration, so it should prove even easier for WebWork
developers to migrate. I do realize that this configuration will ruffle the
feathers of some architects that laud Spring's separation of components, but
I appreciate the fact that a configuration change does not necessarily
negate an architecture. In fact it is a testament to Spring's solid
architecture that such a configuration may be made possible without changing
any of Spring's infrastructure.
proposed configuration - controller.xml
==============================================
<controller alias="/welcome.htm"
class="org.springframework.samples.petclinic.web.ClinicController"
method="welcomeHandler">
<property name="clinic"><ref bean="clinic"/></property>
<results>
<result name="selectView"
class="org.springframework.web.servlet.view.JstlView"
url="/WEB-INF/jsp/welcome.jsp" />
<result name="successView"
class="org.springframework.web.servlet.view.RedirectView"
url="owner.htm" />
</results>
</controller>
These seven lines in a single file represent all of the configuration
information found in the prior 23 lines spread across a couple files. There
are probably other Spring capabilities that I am not representing here in
this small example, but this is at least 95 percent of the types of web
controllers that I use. I also am not showing the new configuration bean
that would have to be configured in the xxx-servlet.xml file.
I believe this configuration data should exist in the xxx-servlet.xml file
or loadable from a separate file. No matter where it exists, it should be
loadable as an applicationContext object with full Spring IOC capability,
including parameter substitution.
If I was going to approach writing this new configuration support bean I
would create a configuration bean that can read in the controller.xml file
in a Spring IOC manner. I guess I would base this work on XmlViewResolver,
although I am unsure if this includes all of the IOC benefits automatically?
<bean id="viewResolver"
class="org.springframework.web.servlet.mvc.ControllerConfig">
<property name="location">
<value>/WEB-INF/controller.xml</value>
</property>
</bean>
I would have to give the bean an id of "viewResolver" because that is the
only property that the dispatcher loads by name. The dispatcher discovers
the other configuration parameters by searching for interface
implementations amongst all the configured beans.
So org.springframework.web.servlet.mvc.ControllerConfig would read the
controller.xml file and implement the following interfaces:
- org.springframework.web.servlet.ViewResolver
- org.springframework.web.servlet.HandlerExceptionResolver
- org.springframework.web.servlet.HandlerMapping
I'm not sure what the best approach for loading the XML configuration file
is to get Spring's IOC support, including external bean references from the
applicationContext and servletContexts. Also it should be reloadable during
development through the use of a flag. Any ideas on where to start with
that?
Once the configuration is loaded, it seems like the interface
implementations would be straightforward.
Thanks for reading this far. Hopefully it wasn't a waste of your time!
- jim cook
|