|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 4
Another important point to understand about DI is how your objects are written. Consider a service bean that accesses a DAO object for data persistence:
public class MyService {
private MyDao dao;
public void setMyDao( MyDao dao ) {
this.dao = dao;
}
public Widget businessMethod() {
return dao.doBusinessThing();
}
}
In traditional applications you would probably create a DAO factory object that, through a configuration file, creates the
appropriate instance of the DAO on your behalf. In this example, Spring creates the correct MyDao instance (as you define it in the configuration file), creates the MyService object, and invokes the setMyDao() method to inject the DAO into the service. You write your application assuming that the DAO object is valid and is the correct
implementation. Your service bean should not be concerned with creating the DAO object, but rather with invoking the correct
method on the DAO object to accomplish the business objective.
The core business value in adopting Spring is the separation between code and configuration, which leads to a more manageable application.
The aptly named Spring MVC is a full MVC implementation that follows the patterns and paradigms that Spring is known for,
including DI. Spring provides a front controller servlet named DispatcherServlet. To build an application, you construct the following components:
ModelAndView object
Spring provides various controllers for you to use as base classes for creating your own controllers, depending on your needs. Among them are ones that:
The rest of this article will be a hands-on introduction to Spring MVC, by way of a programming exercise. Together, we'll build a CMS that presents a list of articles, allows users to read articles, and allows users to post new articles. It's simple, but it demonstrates how to use a basic controller that accepts no parameters, a command controller that accepts the identifier of an article to display, and a form controller that processes an article submission.
Listing 1 shows the source code for the homepage controller. This simple controller accepts no request parameters and returns
a ModelAndView object that contains a list of news articles.
package com.geekcap.geeknews.web;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import com.geekcap.geeknews.core.GeekNewsService;
import com.geekcap.geeknews.core.NewsArticle;
/**
* The HomePageController is responsible for building the model of data to display
* on the home page, which at this point contains a list of article overviews.
*
* @author shaines
*
*/
public class HomePageController extends AbstractController {
/**
* Provides access to GeekNews business methods
*/
private GeekNewsService service;
/**
* Responsible for translating a web request into a ModelAndView object for presentation
*/
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest req, HttpServletResponse res) throws Exception {
// Use the service to load the articles
List<NewsArticle> articles = service.getArticleOverviews();
// Send the articles to the "home" view
return new ModelAndView( "home", "articles", articles );
}
/**
* Injected by Spring
* @param service
*/
public void setGeekNewsService( GeekNewsService service ) {
this.service = service;
}
}
The HomePageController extends Spring's AbstractController, which provides simple servlet-like functionality. It implements a single method -- handleRequestInternal() -- which accepts HttpServletRequest and HttpServletResponse objects, just as a servlet's service() method would. You could read parameters from the HttpServletRequest, but later I'll show you an easier alternative. The purpose of the AbstractController is to invoke business functionality and to generate a ModelAndView object from it.
In MVC vernacular, the model and view are separate entities, so you might wonder why a ModelAndView object seemingly pairs the two. In Spring MVC, the ModelAndView object is a container that hosts the model and provides insight to Spring's view resolver about how to locate the view. In
the HomePageController, the ModelAndView is created with three parameters:
"home": The message that is sent to the view resolver to tell it to show the page identified by home (which I'll explain below).
"articles": An identifier for the model. When the view receives the model, it will be able to access it in the request through the "articles" key.
articles: The model itself.
The business logic, in this case, is delegated to the GeekNewsService, which Spring injects after it creates the HomePageController (shown below).
With the HomePageController built and a service bean that can be used to access back-end data, let's review the configuration of a Spring MVC application
to observe how a request arrives at the Web container and then makes its way to the controller.
The primary entry point for a Spring application is the DispatcherServlet, so this first step is to create a DispatcherServlet in the web.xml file:
<servlet>
<servlet-name>geeknews</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
Next, that DispatcherServlet needs to be mapped to a URI pattern. You can choose any pattern that you want, but most Spring applications map requests
to pages ending in .htm. Add the following to your web.xml file:
<servlet-mapping>
<servlet-name>geeknews</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
In this case, all requests that end in .htm will be sent to the geeknews DispatcherServlet. By default, Spring looks for your Spring beans in a file whose name starts with the servlet name followed by -servlet.xml. So we need to create a geeknews-servlet.xml file that contains the HomePageController, our URL mapping strategy (to map URLs to controllers), and our view resolver, and place it in the WEB-INF directory. Listing 2 shows the contents of the geeknews-servlet.xml file.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/home.htm">homePageController</prop>
</props>
</property>
</bean>
<bean id="homePageController" class="com.geekcap.geeknews.web.HomePageController">
<property name="geekNewsService" ref="geekNewsService" />
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
The geeknews-servlet.xml file defines the HomePageController with the name homePageController. It injects the geekNewsService bean (as a reference, which means this is not a String value, but rather a reference to another bean) into its geekNewsService attribute. The geekNewsService bean is created in the geeknews-service.xml file (discussed below). In addition to defining the homePageController, the geeknews-servlet.xml file defines the urlMapping bean and the viewResolver bean.
The urlMapping bean is responsible for translating a URL pattern to a controller. There are several URL mappers:
BeanNameUrlHandlerMapping: Maps a URL to a bean based on the name of the controller's bean, as defined in the bean's XML definition.
SimpleUrlHandlerMapping: Maps a URL to a bean based on a list of properties. (In Listing 2, the SimpleUrlHandlerMapping class is used to map /home.htm to the bean with the ID of homePageController.)
ControllerClassNameHandlerMapping: Maps a URL to a bean based on the bean's class name. For example, HomePageController would be mapped to /homePage*, such as /home.htm.
ControllerBeanNameHandlerMapping: Similar to the BeanNameUrlHandlerMapping mapper, but does not expect bean names to follow the URL convention. Also supports Controller annotations.
CommonsPathMapHandlerMapping: Maps URLs to a controller based on Jakarta Commons Attributes metadata.
DefaultAnnotationHandlerMapping: Maps URLs to a controller for methods that implement the RequestMapping annotation.
The view resolver is responsible for translating the view name in the ModelAndView into a component, such as a JSP, that renders the view. The several available view resolvers are mostly based on the type
of view they are forwarding to. View resolvers exist for FreeMarker, Jasper Reports, Velocity, XML, XSLT, and of course JSPs
via URL paths. Listing 2 defines the InternalResourceViewResolver, which prefixes the view name with / and suffixes it with .jsp. Thus when the HomePageController returns the view name home, it is resolved to /home.jsp.
Putting it all together:
DispatcherServlet handles all requests that end in .htm, so it handles /home.htm.
SimpleUrlHandlerMapping maps /home.htm to the HomePageController.
HomePageController loads a list of articles from the GeekNewsService and returns those articles in a ModelAndView object whose destination is home.
InternalResourceViewResolver prefixes home with / and suffixes it with .jsp, which means that it is forwarded to /home.jsp for presentation.
Listing 3 shows the complete web.xml file.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd"
version="2.4">
<display-name>SpringMVC</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/geeknews-services.xml
/WEB-INF/geeknews-dao.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>geeknews</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>geeknews</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
<taglib>
<taglib-uri>http://java.sun.com/jstl/core</taglib-uri>
<taglib-location>/WEB-INF/tld/c.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://java.sun.com/jstl/fmt</taglib-uri>
<taglib-location>/WEB-INF/tld/fmt.tld</taglib-location>
</taglib>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
The DispatcherServlet automatically loads the geeknews-servlet.xml file, but when you build Spring applications it's a good practice to divide bean XML files into their logical components.
In Listing 3, the contextConfigLocation context parameter defines two additional Spring XML files that should be loaded: geeknews-services.xml and geeknews-dao.xml. The ContextLoaderListener class is responsible for loading resources and reads the contextConfigLocation parameter to determine which configuration files to load. Finally, the web.xml file imports two components of the Java Standard Tag Library (JSTL) that will be used in the JSP file.