Struts is the de facto web framework for Java applications, mainly because it was one of the first to be released (June 2001). Invented by Craig McClanahan, Struts is an open source project hosted by the Apache Software Foundation. At the time, it greatly simplified the JSP/Servlet programming paradigm and won over many developers who were using proprietary frameworks. It simplified the programming model, it was open source (and thus free as in beer), and it had a large community, which allowed the project to grow and become popular among Java web developers.
Note | |
---|---|
The following section discusses Struts 1 a.k.a. "Struts Classic". Struts 2 is effectively a different product - a successor of WebWork 2.2 (as discussed in Section 17.5, “WebWork 2.x”), carrying the Struts brand now. Check out the Struts 2 Spring Plugin for the built-in Spring integration shipped with Struts 2. In general, Struts 2 is closer to WebWork 2.2 than to Struts 1 in terms of its Spring integration implications. |
To integrate your Struts 1.x application with Spring, you have two options:
Configure Spring to manage your Actions as beans, using the
ContextLoaderPlugin
, and set their dependencies
in a Spring context file.
Subclass Spring's ActionSupport
classes
and grab your Spring-managed beans explicitly using a
getWebApplicationContext() method.
The ContextLoaderPlugin
is a Struts 1.1+ plug-in that loads a Spring context file for the Struts
ActionServlet
. This context refers to the root
WebApplicationContext
(loaded by the
ContextLoaderListener
) as its parent. The default
name of the context file is the name of the mapped servlet, plus
-servlet.xml. If
ActionServlet
is defined in web.xml as
<servlet-name>action</servlet-name>
, the
default is /WEB-INF/action-servlet.xml.
To configure this plug-in, add the following XML to the plug-ins section near the bottom of your struts-config.xml file:
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"/>
The location of the context configuration files can be customized
using the 'contextConfigLocation
' property.
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"> <set-property property="contextConfigLocation" value="/WEB-INF/action-servlet.xml,/WEB-INF/applicationContext.xml"/> </plug-in>
It is possible to use this plugin to load all your context files,
which can be useful when using testing tools like StrutsTestCase.
StrutsTestCase's MockStrutsTestCase
won't
initialize Listeners on startup so putting all your context files in the
plugin is a workaround. (A
bug has been filed for this issue, but has been closed as 'Wont
Fix').
After configuring this plug-in in
struts-config.xml, you can configure your
Action
to be managed by Spring. Spring (1.1.3+)
provides two ways to do this:
Override Struts' default
RequestProcessor
with Spring's
DelegatingRequestProcessor
.
Use the DelegatingActionProxy
class in
the type
attribute of your
<action-mapping>
.
Both of these methods allow you to manage your Actions and their dependencies in the action-servlet.xml file. The bridge between the Action in struts-config.xml and action-servlet.xml is built with the action-mapping's "path" and the bean's "name". If you have the following in your struts-config.xml file:
<action path="/users" .../>
You must define that Action's bean with the "/users" name in action-servlet.xml:
<bean name="/users" .../>
To configure the
DelegatingRequestProcessor
in your
struts-config.xml file, override the
"processorClass" property in the <controller> element. These
lines follow the <action-mapping> element.
<controller> <set-property property="processorClass" value="org.springframework.web.struts.DelegatingRequestProcessor"/> </controller>
After adding this setting, your Action will automatically be looked up in Spring's context file, no matter what the type. In fact, you don't even need to specify a type. Both of the following snippets will work:
<action path="/user" type="com.whatever.struts.UserAction"/> <action path="/user"/>
If you're using Struts' modules feature,
your bean names must contain the module prefix. For example, an action
defined as <action path="/user"/>
with module
prefix "admin" requires a bean name with <bean
name="/admin/user"/>
.
Note | |
---|---|
If you are using Tiles in your Struts application, you must
configure your <controller> with the |
If you have a custom RequestProcessor
and
can't use the DelegatingRequestProcessor
or
DelegatingTilesRequestProcessor
approaches, you
can use the
DelegatingActionProxy
as the type in
your action-mapping.
<action path="/user" type="org.springframework.web.struts.DelegatingActionProxy" name="userForm" scope="request" validate="false" parameter="method"> <forward name="list" path="/userList.jsp"/> <forward name="edit" path="/userForm.jsp"/> </action>
The bean definition in action-servlet.xml
remains the same, whether you use a custom
RequestProcessor
or the
DelegatingActionProxy
.
If you define your Action
in a context
file, the full feature set of Spring's bean container will be
available for it: dependency injection as well as the option to
instantiate a new Action
instance for each
request. To activate the latter, add
scope="prototype" to your Action's bean
definition.
<bean name="/user" scope="prototype" autowire="byName" class="org.example.web.UserAction"/>
As previously mentioned, you can retrieve the
WebApplicationContext
from the
ServletContext using the
WebApplicationContextUtils
class. An easier way
is to extend Spring's Action
classes for Struts.
For example, instead of subclassing Struts'
Action
class, you can subclass Spring's
ActionSupport
class.
The ActionSupport
class provides additional
convenience methods, like
getWebApplicationContext(). Below is an example of
how you might use this in an Action:
public class UserAction extends DispatchActionSupport { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { if (log.isDebugEnabled()) { log.debug("entering 'delete' method..."); } WebApplicationContext ctx = getWebApplicationContext(); UserManager mgr = (UserManager) ctx.getBean("userManager"); // talk to manager for business logic return mapping.findForward("success"); } }
Spring includes subclasses for all of the standard Struts Actions - the Spring versions merely have Support appended to the name:
The recommended strategy is to use the approach that best suits
your project. Subclassing makes your code more readable, and you know
exactly how your dependencies are resolved. However, using the
ContextLoaderPlugin
allow you to easily add new
dependencies in your context XML file. Either way, Spring provides some
nice options for integrating the two frameworks.