MVC Walkthrough
using-lishys-notes.notes

Guide


The guide pane on the right contains information organized into sections.

Use the and buttons to move through the sections, or click an accordion tab (shown below) to go directly to a particular topic.



Code Reference


Each paragraph in the guide is usually associated with a particular piece of code. An orange arrow indicates that the code displayed in the middle pane is relevant to the guide text.



If the arrow is gray, simply click anywhere on the paragraph and the relevant code will be displayed.



Code Highlighter


The highlight feature identifies areas of the code which relate to a word or term in the guide. Hover the mouse over any underlined word and the relevant code will be highlighted.



You can disable the highlighting feature at any time by clicking on the button.

Don't forget to make the appropriate code page visible by clicking on any paragraph with a gray arrow.
RandomService.java
package com.lishman.random.service;

import java.util.List;

import com.lishman.random.web.RandomData;


public interface RandomService {
    
    public int randomNumber();
    
    public int randomNumberInRange(int startOfRange, int endOfRange);

    public RandomData randomData();
    
    public List<Integer> randomNumberList();
}

RandomServiceImpl.java
package com.lishman.random.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.springframework.stereotype.Service;

import com.lishman.random.web.RandomData;


@Service
public class RandomServiceImpl implements RandomService {

    public int randomNumber() {
        return new Random().nextInt();        
    }
    
    public int randomNumberInRange(int startOfRange, int endOfRange) {
        return new Random().nextInt(endOfRange - startOfRange) + startOfRange;        
    }

    public RandomData randomData() {  
        Random generator = new Random();      
        RandomData data = new RandomData();
        data.setBoolean(generator.nextBoolean());
        data.setDouble(generator.nextDouble());
        data.setInteger(generator.nextInt());   
        return data;        
    }
    
    public List<Integer> randomNumberList() {
        Random generator = new Random();        
        List<Integer> numbers = new ArrayList<Integer>(10);
        for (int i=0; i<10; i++) {
            numbers.add(generator.nextInt());
        }        
        return numbers;
    } 
    
}

RandomController.java
package com.lishman.random.web;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import com.lishman.random.service.RandomService;

@Controller
public class RandomController {
    
    @Autowired
    private RandomService random;

    @RequestMapping("/random.html")
    @ModelAttribute("randomNumber") public int randomNumber() {
        return random.randomNumber();        
    }
    
    @RequestMapping(value="/random.html", params={"start", "end"})
    public ModelAndView randomNumberInRange(@RequestParam("start") int startOfRange,
                                            @RequestParam("end") int endOfRange) {
        int result = random.randomNumberInRange(startOfRange, endOfRange);     
        return new ModelAndView("random", "randomNumber", result);
    }

    @RequestMapping("/random-data.html")
    @ModelAttribute public RandomData randomData() {    
        return random.randomData();        
    }
    
    @RequestMapping("/random-list.html")
    @ModelAttribute("randomNumbers") public List<Integer> randomNumberList() {  
        return random.randomNumberList();
    } 
}
RandomData.java
package com.lishman.random.web;

public class RandomData {
    
    public boolean randomBoolean;
    public int randomInteger;
    public double randomDouble;

    public boolean isBoolean() {
        return randomBoolean;
    }

    public void setBoolean(boolean randomBoolean) {
        this.randomBoolean = randomBoolean;
    }

    public int getInteger() {
        return randomInteger;
    }

    public void setInteger(int randomInteger) {
        this.randomInteger = randomInteger;
    }

    public double getDouble() {
        return randomDouble;
    }

    public void setDouble(double randomDouble) {
        this.randomDouble = randomDouble;
    }
    

}

random-data.jsp
<html>
  <body>
  
    <h1>Random Data</h1>
    
    <p>A random boolean is ${randomData.boolean}</p>
    <p>A random double is ${randomData.double}</p>
    <p>A random integer is ${randomData.integer}</p>
    
    <p><a href="../index.html">Back</a></p>
    
  </body>
</html> 

random-list.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  

<html>
  <body>
  
    <h1>Random Numbers</h1>
    <p>Here are 10 random numbers:</p>
    
    <c:forEach items="${randomNumbers}" var="randomNumber">
      <div>${randomNumber}</div>
    </c:forEach>
      
    <p><a href="../index.html">Back</a></p>

  </body>
</html> 

random.jsp
<html>
  <body>
  
    <h1>Random Number</h1>
    <p>A random number is ${randomNumber}</p>
    
    <p><a href="../index.html">Back</a></p>
    
  </body>
</html> 

random-servlet.xml
<?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">

  <context:component-scan base-package="com.lishman.random.web"/>
  <context:component-scan base-package="com.lishman.random.service"/>
  
  <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver"
        p:prefix="/WEB-INF/jsp/"
        p:suffix=".jsp"/>
        
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         id="WebApp_ID"
         version="2.5">
  
  <display-name>Random</display-name>
  
  <servlet>
    <servlet-name>random</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>random</servlet-name>
    <url-pattern>/app/*</url-pattern>
  </servlet-mapping>
  
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  
</web-app>
index.html
<html>
  <body>
  
    <h1>Randomizer</h1>
   
    <p>
      <a href="app/random.html">Random number</a><br/>
      
      <a href="app/random.html?start=1000&end=1040">Random number in range</a><br/> 
           
      <a href="app/random-data.html">Random data</a><br/>
      
      <a href="app/random-list.html">List of random numbers</a>
    </p>
 
  </body>
</html> 
server.log
INFO: Mapped URL path [/random.html] onto handler 'randomController'
INFO: Mapped URL path [/random-data.html] onto handler 'randomController'
INFO: Mapped URL path [/random-list.html] onto handler 'randomController'

Initialization

During start up, the web server loads and initializes DispatcherServlet as it would any other servlet.
DispatcherServlet reads a configuration file called [servlet-name]-servlet.xml and scans the base-packagecom.lishman.randoms for components, which it registers in the web application context.

The DefaultAnnotationHandlerMapping bean (which is registered by default) then creates a map of @RequestMapping paths/random to controllers, so when a request is received, it can be routed to the appropriate handler.
We can see details of these mappings in the server log file

These beans are also registered by default:
  • AnnotationMethodHandlerAdapter
  • DefaultRequestToViewNameTranslator
We will see why next.

Request

The web server receives a request such as

http://host/root/app/random.htmlrandom.html"
and uses the url-pattern in servlet-mapping to route the request to DispatcherServlet.

DispatcherServlet then uses the request mappings produced by DefaultAnnotationHandlerMapping to direct the request to the appropriate controller.
Next, the AnnotationMethodHandlerAdapter bean (which is also registered by default) uses HTTP paths/random, HTTP methods and request parametersparams to narrow the request to a specific method on the controller.
For example, the randomNumber()randomNumber:eq(1) and randomNumberInRange()randomNumberInRange:eq(0) methods are both mapped to the /random.html path, and this will have been registered by DefaultAnnotationHandlerMapping at start up.

However, it is only when a request is received that AnnotationMethodHandlerAdapter can determine which of the two methods is to be executed based on the presence or absence of the startstart:eq(0) and endend:eq(0) request parameters.

View

All handlers must resolve to a logical view name, either explicitly (by returning a String, View or ModelAndView) or implicitly (based on conventions).
If no view name is returned by a method, the default URL to view name translator bean, called DefaultRequestToViewNameTranslator, converts the URL of the incoming request into a logical view name.
We can see this with the randomNumber()randomNumber:eq(1), randomData()randomData:eq(0) and randomNumberList()randomNumberList:eq(0) methods which do not specify the name of a view and therefore rely on DefaultRequestToViewNameTranslator to derive the view name from the URL.

InternalResourceViewResolver then transforms this logical view name into the name of a real JSP page in the /WEB-INF/jsp directory.
This allows a URL such as

http://host/root/app/random.htmlrandom.html"
to display the /WEB-INF/jsp/random.jsp page, without the view name even being mentioned in the controller.
Of course this default can always be overridden by specifying the name of a viewrandom:eq(10) in our handler method, as is the case with randomNumberInRange.
Home  |  Getting Started  |  Controllers  |  Views  |  Forms  |  REST  |  Testing  |  Configuration  |  MVC Walkthrough  |   lishblog  |  Email Lishy