This article explains how to integrating struts with spring. Struts is more established and more stable MVC2 framework at this time so if your application is based on Struts framework you may forget about thinking to move to some other framework. But at the same time you must have heard about the buzz created by Inversion of Control (IOC) design pattern. This design pattern is implemented by Spring framework. Besides there are some more amazing features of Spring like AOP. So if you like to take advantage of these features of Spring you do not have to rebuild the application, but you can integrate your existing Struts application with Spring without much hassle. More about that latter but first we would like to have a look at new features of Spring and how they work.
also read:
If you are beginner for spring framework, please read our article on introduction to spring framework, spring aop, spring mvc. Javabeat covers extensive articles on the spring framework. If you are interested in receiving the updates, please subscribe here.
What’s new in Spring?
Spring implements the IOC design pattern. It is a lightweight container and the objects are managed using an external XMl configuration file. Reference to a dependent object is obtained by exposing a JavaBean property. You just have to enter the properties in the external XML file.
Spring and IOC
IOC is a design pattern that externalizes application logic so that it can be injected into client code rather than written into it. Use of IOC in Spring framework separates the implementation logic from the client.
Spring framework offers more than this. The transaction handling is done beautifully in Spring. It can integrate major persistence frameworks and offer a consistent exception hierarchy. The best part of Spring is the mechanism of aspect-oriented code instead of the usual object-oriented code.
Spring AOP provides us with interceptors which are used to intercept the application logic at any execution point and then apply some methods at the interceptors. They are widely used for logging resulting in a more readable and functional code.
Advantages of integrating Struts with Spring:
The advantages of integrating a Struts application into the Spring framework are:
- Spring framework is based on new design approach and was designed to resolve the existing problems of existing Java applications such as performance.
- Spring framework lets you apply AOP (aspect-oriented programming technique) rather than object-oriented code.
- Spring provides more control on struts actions. That may depend on the method of integration you choose.
If you have decided to integrate your Struts application into Spring, we are ready to begin.
There are three ways of doing this and based on the scenarios in your application you can select the way you want to do it.
- Using Spring’s ActionSupport class to integrate Struts.
- Using ContextLoaderPlugin.
1) Using Spring’s ActionSupport class to integrate Struts.
Integrating Struts with Spring doesn’t sound to be easy but if you follow this method you will find it quite simple and to add to this Spring provides you with org.springframework.web.struts.ActionSupport class. This class provides a method getWebApplicationContext() which you can use to obtain the Spring context. So you just have to extend Spring’s ActionSupport from your strut’s actions.
Using ActionSupport to integrate Struts
package net.javabeat.example1.actions; import java.io.IOException; import javax.servlet.*; import org.apache.struts.action.*; import org.springframework.context.ApplicationContext; import org.springframework.web.struts.ActionSupport; import net.javabeat.example1.beans.Employee; import net.javabeat.example1.business.EmpSearchService; public class SubmitSearch extends ActionSupport { public ActionForward execute( ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { DynaActionForm searchEmpForm = (DynaActionForm) form; String empId = (String) searchEmpForm.get("empId"); ApplicationContext ctx = getWebApplicationContext(); EmpSearchService empSearchService = EmpSearchService) ctx.getBean("EmpSearchSercvice"); Employee employee = empSearchService.find(empId.trim()); if (null == employee) { ActionErrors errors = new ActionErrors(); errors.add(ActionErrors.GLOBAL_ERROR,new ActionError("message.empnotfound")); saveErrors(request, errors); return mapping.findForward("failure") ; } request.setAttribute("employee", employee); return mapping.findForward("success"); } }
Explanation:
The trick here is to extend your Action from the Spring ActionSupport and not from the Struts Action class. The Spring ActionSupport provides you with the method getWebApplicationContext() to obtain an ApplicationContext and from the ApplicationContext you can access the business service layer.
ApplicationContext ctx = getWebApplicationContext(); //getWebApplicationContext provided by the Spring ActionSupport class EmpSearchService empSearchService = (EmpSearchService) ctx.getBean("EmpSearchSercvice"); // From the ApplicationContext get the business service bean
Disadvantages:
This method involves changes in the java files of your application, which is not desirable because if you want to move your application back to Struts framework you will have to redo the changes in the java files. And in this method the Struts actions are not in complete control of Spring.
2) Using ContextLoaderPlugin provided with Spring.
ContextLoaderPlugin is provided with Spring’s 1.0.1 release. This plug-in loads the Spring application context for Strut’s applications ActionServlet.There are two methods of integrating Struts with Spring using this plug-in and these two methods are:
- A) Overriding the Struts RequestProcessor with Spring’s DelegatingRequestProcessor.
- B) Delegate Struts Action management to the Spring framework.
To use the ContextLoaderPlugin loads the Spring context file for the Struts ActionServlet. To use this plug-in you have to add following to your struts config file:
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"/>
The location of the context config file can be set through contextConfigLocation property.
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"> <set-property property="contextConfigLocation" value="/WEB-INF/actionname-servlet.xml” /> </plug-in>
The default location for the Context config file is /WEB-INF/actionname-servlet.xml.
A)Overriding the Struts RequestProcessor with Spring’s DelegatingRequestProcessor.
One way of using ContextLoaderPlugIn is to override the Struts RequestProcessor with Spring’s DelegatingRequestProcessor class. The Spring’s DelegatingRequestProcessor will lookup the Struts Actions defined in
ContextLoaderPlugIn’s WebApplicationContext. You can either use a single ContexLoaderPlugIn for all your Struts modules and then load the context in all your Struts modules or you can also define seperate ContextLoaderPlugIn
for each of your Struts modules and then use the contextConfigLocation parameter to load the appropriate XML file.
Let us have a look at it.
Integration via Spring’s DelegatingRequestProcessor
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <form-beans> <form-bean name="searchEmpForm" type="org.apache.struts.validator.DynaValidatorForm"> <form-property name="empId" type="java.lang.String"/> </form-bean> </form-beans> <global-forwards type="org.apache.struts.action.ActionForward"> <forward name="welcome” path="/index.do"/> <forward name="searchEmployee" path="/searchEmp.do"/> <forward name="submitSearch" path="/submitSearch.do"/> </global-forwards> <action-mappings> <action path="/welcome" forward="/WEB-INF/views/welcome.htm"/> <action path="/searchEmployee" forward="/WEB-INF/views/search.jsp"/> <action path="/submitSearch" type="net.javabeat.example1.books.actions.SubmitSearch" input="/searchEmployee.do" validate="true" name="searchEmpForm"> <forward name="success" path="/WEB-INF/views/empDetail.jsp"/> <forward name="failure" path="/WEB-INF/views/search.jsp"/> </action> </action-mappings> <message-resources parameter="ApplicationResources"/> <controller processorClass="org.springframework.web.struts.DelegatingRequestProcessor"/> <plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/> </plug-in> <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"> <set-property property="csntextConfigLocation" value="/WEB-INF/beans.xml"/> </plug-in> </struts-config>
Explanation:
Here we have overridden the Struts RequestProcessor with Spring’s DelegatingRequestProcessor using the controller tag. Now our Spring’s DelegatingRequestProcessor will lookup the Struts Actions
so we need to register the action in our Spring config file as shown below:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="empSearchService" class="net.javabeat.example1.employee.business.EmpSearchImpl"/> <bean name="/submitSearch" class="net.javabeat.example1.employee.actions.SubmitSearch"> <property name="empSearchService"> <ref bean="empSearchService"/> </property> </bean> </beans>
Explanation:
This part is same as registering the Spring beans but here we register Struts actions as Spring’s beans, the names of the beans must be same as in struts config file. This will allow Spring to populate our beans at the run time.
Package net.javabeat.example1.employee.actions; import java.io.IOException; import javax.servlet.*; import org.apache.struts.action.*; import net.javabeat.example1.employee.beans.Employee; import net.javabeat.example1.employee.business.EmpSearchService; public class SubmitSearch extends Action { private EmpSearchService empSearchService; public EmpSearchService getEmpSearchService () { return empSearchService; } public void setEmpSearchService(EmpSearchService empSearchService) { this.empSearchService = empSearchService; } public ActionForward execute( ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { DynaActionForm searchEmpForm = (DynaActionForm) form; String empId = (String) searchEmpForm.get("empId"); Employee employee = getEmpSearchService().find(empId.trim()); if (null == employee) { ActionErrors errors = new ActionErrors(); errors.add(ActionErrors.GLOBAL_ERROR,new ActionError("message.notfound")); saveErrors(request, errors); return mapping.findForward("failure") ; } request.setAttribute("employee", employee); return mapping.findForward("success"); } }
Explanation:
Now here we are using Struts action but the beans are managed by Spring. In the above code we create a javaBean which is populated by Spring and then use it to run our business logic.
Disadvantages:
This method is definitely better then the previous one but still here the Spring beans are dependent on the RequestProcessor, this reduces the flexibility of the application.
B) Delegate Struts Action management to the Spring framework.
A much better solution is to delegate Struts action management to the Spring framework. You can do this by registering a proxy in the struts-config action mapping. The proxy is responsible for looking up the Struts action in the Spring context. Because the action is under Spring’s control, it populates the action’s JavaBean properties and leaves the door open to applying features such as Spring’s AOP interceptors.
The delegation method of Spring integration
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <form-beans> <form-bean name="searchEmpForm" type="org.apache.struts.validator.DynaValidatorForm"> <form-property name="empId" type="java.lang.String"/> </form-bean> </form-beans> <global-forwards type="org.apache.struts.action.ActionForward"> <forward name="welcome” path="/index.do"/> <forward name="searchEmployee" path="/searchEmp.do"/> <forward name="submitSearch" path="/submitSearch.do"/> </global-forwards> <action-mappings> <action path="/welcome" forward="/WEB-INF/views/welcome.htm"/> <action path="/searchEmployee" forward="/WEB-INF/views/search.jsp"/> <action path="/submitSearch" type="org.springframework.web.struts.DelegatingActionProxy" (1) input="/searchEmployee.do" validate="true" name="searchEmpForm"> <forward name="success" path="/WEB-INF/views/empDetail.jsp"/> <forward name="failure" path="/WEB-INF/views/search.jsp"/> </action> </action-mappings> <message-resources parameter="ApplicationResources"/> <plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/> </plug-in> <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"> <set-property property="contextConfigLocation" value="/WEB-INF/beans.xml"/> </plug-in> </struts-config>
Explaination:
This is simply a struts config file, but here we do not declare the action’s class name but we set the Spring’s DelegatingActionProxy. The DelegatingActionProxy will lookup context for the action name in the spring’s config and map it to its class. The context is declared in the ContextLoaderPlugIn.
Now just register the Struts action as a bean in the Spring config file. The properties for this action’s bean will be automatically populated just like any other Spring bean.
Register a Struts action in the Spring context
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="empSearchService" class="net.javabeat.example1.employee.business.EmpServiceImpl"/> <bean name="/submitSearch" class="net.javabeat.example1.employee.actions.SubmitSearch"> <property name="empSearchService"> <ref bean="empSearchService"/> </property> </bean> </beans>
Disadvantages:
If you have the option to choose a method then this is the best method the only disadvantage here is this method leaves your code less readable because the dependencies are not clear.
Using Spring’s AOP with your Struts Application:
To tackle cross-cutting you can use Spring interceptors
To use Spring’s AOP in your Struts Applications you need to :
- Create the interceptor.
- Registor the interceptor.
- Delclare interceptors.
For example
A sample logging interceptor
package net.javabeat.example1.employee.interceptors; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class LoggingInterceptor implements MethodBeforeAdvice { public void beforeMethod(Method method, Object[] objects, Object o) throws Throwable { System.out.println("logging before intersection"); } }
This interceptor will execute the beforeMethod() before every intersection (We are using MethodBeforeAdvice). Here we are just printing a line but we can use it for anything we like. Next let us register the interceptor.
Registering the interceptor in the Spring config file
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="empSearchService" class="net.javabeat.example1.employee.business.EmpSearchServiceImpl"/> <bean name="/submitSearch" class="net.javabeat.example1.employee.actions.SubmitSearch"> <property name="empSearchService"> <ref bean="empSearchService"/> </property> </bean> <!-- Interceptors --> <bean name="logger" class="net.javabeat.example1.employee.interceptors.LoggingInterceptor"/> <!-- AutoProxies --> <bean name="loggingAutoProxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <value>/submitSearch>/values> </property> <property name="interceptorNames"> <list> <value>logger>/value> </list> </property> </bean> </beans>
Explanation:
First we have to register the interceptor:
<bean name="logger" class="net.javabeat.example1.employee.interceptors.LoggingInterceptor"/>
Now we need to define the intersections, we will use autoproxy:
<bean name="loggingAutoProxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
Now we have to register the Struts action’s that will be intercepted.
<property name="beanNames"> <value>/submitSearch>/values> // <value>/otherBeans>/values> </property> Apply interceptors to the beanNames: <property name="interceptorNames"> <list> <value>logger>/value> </list> </property>
also read:
Conclusion
All the three methods provide good integrating of Struts with Spring and you can select any method that suits your project. The Action-delegation method seems to be better of the three because you have to make fewer changes in the code and you can take advantages of other features of Spring like AOP (discussed latter). This method gives Spring more control over Struts action. You can make the strut’s actions (or beans in Spring) threadsafe through Spring. You can also use Spring’s life cycle methods.
If you have any questions on the spring framework integration with struts, please post it in the comments section. Also search in our website to find lot of other interesting articles related to the spring framework. There are some interesting articles about spring framework, interview questions, spring and hibernate integration,etc.
If you would like to receive the future java articles from our website, please subscribe here.