Song of The Day: Spring 2001 - Artist: Lila

Recently, I rewrote a significant part of a Tomcat/JSP/JDBC application using the Spring Framework 2.x and Hibernate 3.x. Although Hibernate has its niceties and nuances, the Spring Framework integration alone reduced the code complexity, made the code more readable and maintainable, and reinforced good coding practices.
Those in the Java space likely already know the advantages of an MVC framework, possibly having used Struts or Spring previously, but today I thought I’d write a few thoughts about it.
So What is Spring
Spring is a component framework introduced by Rod Johnson back in 2002. The latest version has a plethora of features, but probably one of the best known, and best publicized for some time now, is the IOC (inversion of control) implementation, which may be more correctly be termed with certain concepts as dependency injection (DI). Another facet, considered positive by most I think, is that Spring emphasizes POJO-based development, which may keep architectures simpler compared to EJB-based frameworks. Moreover, the brief example below takes advantage of Spring’s Web MVC Framework as well, which can dramatically reduce effort on forms.
Why is IOC/DI Good
Basically, it takes the dirty work out of instantiating and referencing singleton objects; well, components in general. Although non-singleton prototype objects are supported, I cover the default scope, singleton, with Spring in this entry. Objects can be defined within an XML file, instantiated during start-up, and called upon during problem execution via an application context. No complicated inheritance models, which reduces code for constructors and getter/setter methods and adds flexibility to constructors and field initialization. There is also the whole Hollywood Principle that can be reviewed here, which describes how IOC/DI performs the work for the programmer by calling necessary code as needed with event-driven-type methods.
Primitive MVC-ish Model
There are many good books and references available, of course, but let’s use a simple example. Let’s say I have a crypto class/object that I want to decrypt and validate some value during a Web request. Typically, one would perform something like the following in a pure JSP model:
- Create a servlet to handle and dispatch the request to the appropriate request handler.
- Instantiate the crypto object with the HttpServletRequest and request.getInitParameter() web.xml, configuration values.
- Decrypt the value within the request, say from a cookie header.
- Determine authorization upon examination and dispatch the appropriate view (e.g., success.jsp or failure.jsp).
Refactored MVC with Spring
Why would the above be easier in Spring? First, there is true separation between the crypto object (aka domain-type object) and other MVC request/response components. Let’s say I have defined the following from my crypto and controller classes (simplified a bit for brevity):
/**
* Crypto class. Used to create a nonce and set/get the key used for
* obfuscation.
*/
package mydomain.Crypto;
public class Crypto {
private String key;
public Crypto() {
...
}
public void setKey (String key) { //base64 encoded or something
this.key = key;
}
public String getKey () {
return key;
}
public boolean isValidNonce() {
...
}
public String generateNonce() {
...
}
}
…Now the Spring Framework Controller class:
/**
* Spring simple form controller class.
*/
public class LoginController implements SimpleFormController {
/**
* Set up for form...
*/
public ModelAndView showForm(HttpServletRequest request,
HttpServletResponse response, BindException errors)
throws Exception {
FormCommand cmd = (FormCommand) errors.getTarget();
/*
* prepare backing object if need be
*/
ApplicationContext ac = this.getApplicationContext();
Crypto crypto = (Crytpo) ac.getBean("crypto");
Map model = errors.getModel();
/*
* JSP/JSTL/EL will reference ${nonce} in HTML
*/
model.put("nonce", crypto.generateNonce());
return new ModelAndView("login", model);
}
/**
* Form submission...
*/
@SuppressWarnings("unchecked")
public ModelAndView onSubmit(HttpServletRequest request,
HttpServletResponse response,
Object command, BindException errors)
throws ServletException {
FormCommand cmd = (FormCommand) command;
/* grab Spring application context */
ApplicationContext ac = this.getApplicationContext();
Crypto crypto = (Crytpo) ac.getBean("crypto");
ModelAndView mv = new ModelAndView();
String nonce = request.getParameter("nonce");
String hiddenNonce = request.getParameter("hidden_nonce");
/*
* Could have been validated in the command object, but is here for
* less code in the blog entry.
*/
if (nonce.compareTo(hiddenNonce) == 0 && crypto.isValidNonce(nonce)) {
mv.setViewName("form_success");
} else {
mv.setViewName("form_failure");
}
return mv;
}
/**
* Set up form backing object for form validation, etc.
*/
protected Object formBackingObject(HttpServletRequest request)
throws ServletException {
FormCommand cmd = new FormCommand();
return cmd;
}
}
For completeness we’ll define command and validator classes:
/**
* Used by Spring during HTML/JSP form validation.
*/
public class FormCommand {
public FormCommand() {
...
}
/* constructor and getter/setter methods bound to Spring
* and HTML/JSP form
*/
public void setNonce(String nonce) {
...
}
public String getNonce() {
...
}
}
/**
* Validator class for form
*/
public class Validator implements Validator {
public boolean supports(Class clazz) {
return clazz.equals(FormCommand.class);
}
public void validate(Object obj, Errors errors) {
FormCommand cmd = (FormCommand) obj;
String nonce = cmd.getNonce();
/* example invalid nonce */
if (nonce == null || nonce.compareTo("") == 0) {
/* error grabbed from messages.properties,
* set up via Spring Framework
*/
errors.reject("error.reg.invalidnonce");
}
}
}
The XML configuration (e.g., in myapp-servlet.xml or applicationContext.xml) would look something like the following to tie together all the classes and objects:
<bean id="crypto" class="mydomain.Crypto">
<property name="key"><value>AABBCC==</value></property>
</bean>
<!-- my login form controller and validator -->
<bean id="validator" class="mydomain.Validator"/>
<bean id="loginFormController" class="mydomain.LoginFormController">
<property name="sessionForm"><value>true</value></property>
<property name="commandName"><value>cmd</value></property>
<property name="commandClass"><value>mydomain.FormCommand</value>
</property>
<property name="validator"><ref bean="validator"/></property>
<property name="formView"><value>login</value></property>
<property name="successView">
<value>from_success.htm</value>
</property>
<property name="messages" ref="messageSource"/>
</bean>
Now, the only part missing is the code for the JSP, which could look something like this:
<% /* login.jsp: tags could be in an include.jsp file */ %>
<%@ page session="true"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="s" uri="/struts-tags"%>
<%@ taglib prefix="spring" uri="/spring" %>
<html>
<head><title>Login Form</title></head>
<body>
<form name=login method="post" class="myStyle">
<p>
<spring:hasBindErrors name="cmd">
<div class="invalidForm">
Invalid entry. Please try again.
</div>
</spring:hasBindErrors>
<input name="hidden_nonce" type=hidden value="${nonce}">
<spring:bind path="cmd.nonce">
<!-- here for explanation and example -->
<input name="nonce" value="<c:out value="${status.value}"/>" type="text">
</spring:bind>
<!-- much left out in JSP and existing field data theoretical -->
</body>
</html>
Although the above may look complicated initially, it actually is quite straight-forward. We defined a Spring-based form class, validator and command object classes for collection and validation of HTML form input fields, the business/domain objects (e.g., class Crypto operating on data), the EL-based JSP, and we configured the appropriate files to tie it all together.
There are various other tidbits left out (e.g., mapping of say login.form to login.jsp in the Spring config files); however, when a user visits the login URL (i.e., something like http://localhost:8080/login.form), the backing object is created (i.e., the FormCommand class) and then the showForm method is invoked. If there are errors during the submission of the login.jsp (i.e., before onSubmit is invoked), then the validator object works in conjunction with the Spring Framework to display the error via the tags within <spring:hasBindErrors>. In other words the form, on error, is displayed back to the user without further processing.
I didn’t show an extensive example of validation failing, but you can check here for other examples. If everything passes the validation phrase and object, then the onSubmit method processes the request and invokes the appropriate view (i.e., .jsp here). This flow shows the how the IOC/DI accomplishes an event-driven model with the HTML form.
Furthermore, there is no passing of the HttpServletRequest object to domain objects or other unnecessary code interweaving (aka spaghetti code) between views, models, and JSPs and, as good, no convoluted scriptlets. I have seen how scriplets, and scripted code in general, quickly make JSPs and other dynamic web pages unreadable and unmaintainable, so this is a good model!
Furthermore, the initialization of our Crypto object is flexible allowing us to pass in keys without littering the web.xml with initialization parameters.
There is a lot unexplained for now, but I unfortunately have limited time :). It’s a start! In future entries perhaps I can expand and clarify. Furthermore, JNDI/JDBC inclusion would be a nice add-on for data persistence.
Tags: Eric O’Laughlen, Spring Framework, Java
