ArticlesProjectsCredentialsAbout
strutsjavamvc

Struts: MVC for Java Web Apps Before Spring MVC

·4 min read

Struts: MVC for Java Web Apps Before Spring MVC

Before Spring MVC, before JSF, before any modern Java web framework, there was Struts. Released by Craig McClanahan in 2000 and donated to the Apache Foundation, Struts brought the Model-View-Controller pattern to Java web development at a time when most Java web code was procedural logic embedded in JSPs.

The Problem Struts Solved

Without a framework, a typical Java web application in 2000 looked like this — business logic, database access, and HTML generation all mixed in a JSP:

<%
String ip = request.getParameter("ip");
Connection conn = DriverManager.getConnection(url, user, pass);
PreparedStatement ps = conn.prepareStatement("SELECT * FROM devices WHERE ip=?");
ps.setString(1, ip);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
%>
<tr><td><%= rs.getString("ip") %></td><td><%= rs.getString("status") %></td></tr>
<%
}
conn.close();
%>

Testing was impossible. Changing the layout required understanding the SQL. Changing the SQL required understanding the HTML.

Struts separated these concerns: the controller handled HTTP requests, the model held the data, and the view (JSP) rendered HTML without any business logic.

The Struts Architecture

ActionForm — a JavaBean holding form data:

public class DeviceSearchForm extends ActionForm {
    private String ip;

    public String getIp()           { return ip; }
    public void   setIp(String ip)  { this.ip = ip; }

    public ActionErrors validate(ActionMapping m, HttpServletRequest req) {
        ActionErrors errors = new ActionErrors();
        if (ip == null || ip.trim().isEmpty()) {
            errors.add("ip", new ActionError("error.ip.required"));
        }
        return errors;
    }
}

Action — handles the request, calls business logic, selects the next view:

public class DeviceSearchAction extends Action {
    public ActionForward execute(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {

        DeviceSearchForm searchForm = (DeviceSearchForm) form;
        DeviceService    service   = ServiceLocator.getDeviceService();

        Device device = service.findByIp(searchForm.getIp());
        if (device == null) {
            return mapping.findForward("notFound");
        }
        request.setAttribute("device", device);
        return mapping.findForward("success");
    }
}

struts-config.xml — wired everything together:

<struts-config>
  <form-beans>
    <form-bean name="deviceSearchForm"
               type="com.motorola.nms.web.DeviceSearchForm"/>
  </form-beans>

  <action-mappings>
    <action path="/deviceSearch"
            type="com.motorola.nms.web.DeviceSearchAction"
            name="deviceSearchForm"
            validate="true"
            input="/deviceSearch.jsp">
      <forward name="success"  path="/deviceDetail.jsp"/>
      <forward name="notFound" path="/deviceNotFound.jsp"/>
    </action>
  </action-mappings>
</struts-config>

JSP — pure display, no logic:

<c:if test="${not empty device}">
  <p>IP: <c:out value="${device.ip}"/></p>
  <p>Status: <c:out value="${device.status}"/></p>
</c:if>

What Struts Got Right

The separation of concerns was a genuine improvement. Controllers were testable (they were just classes with a method). JSPs only read from request attributes, not from databases. Validation was centralised in the ActionForm.

The XML configuration was a single place to see all the URLs your application handled and where they went. Compared to hunting through JSP files, that was progress.

What Struts Got Wrong

One controller class per action. A typical application had 50 URLs and 50 Action classes, each with one method. The proliferation of tiny classes created an organisational overhead that grew with the application.

ActionForms were awkward. You needed a separate class to hold form data, even for simple forms. The validation model was limited — ActionForm validation ran before the Action, but the Action could not put errors back into the form easily.

XML configuration grew unwieldy. A medium-sized application had a struts-config.xml with hundreds of lines. Navigating it required understanding the whole file.

Thread safety surprises. Actions were singletons — one instance shared across all requests. Instance variables in an Action class were a threading bug waiting to happen. This caught every team at least once.

Spring MVC

Spring MVC (2003) addressed all of these. A single controller class could handle multiple URLs with annotated methods. No ActionForm — method parameters were bound directly from request parameters. No XML required — annotation-based configuration. Actions (controllers) could be prototypes, eliminating the thread safety issue.

Struts was the right tool at the time it arrived. Spring MVC was the right tool when it arrived. The pattern — controller handles request, model holds data, view renders HTML — survived the transition. The ceremony did not.