Spring MVC: Multi-Controller actions using @Controller

In previous article, we have discussed about @Controller annotation in-depth. But the example covered just one simple method with

class-level mapping (i.e.; @RequestMapping("/controller")) and
method-level mapping (i.e.; @RequestMapping(value="/firstmvc.htm",
                               method=RequestMethod.GET))

which eventually forms
http://<server>:<port>/<context-root>/controller/firstmvc.htm
URL path for this particular method for service invocation

Now, what if you got two-more methods with different styles clubbed under the same component class (i.e.; class-level) ?

Or other way, you want to club similar http requests to be handled under one component class ?

Solution: Use multi-action controller using @RequestMapping for each public method defined in the same class (which again annotated with @RequestMapping). Apparently, @RequestMapping annotation comes with good number of attributes to define for any particular method

We will extend the same example covered in the previous article. Thus, more explanation for the annotated component class, which will have 4 public methods covering 3 GET request & 1 POST request with input arguments

For details about @RequestMappting see here

Technology Used

  • Java 1.7
  • Eclipse Kepler IDE
  • Spring-4.0.0-RELEASE
  • Apache Maven 3.0.4
  • Apache-Tomcat-7.0.41

Mavenize or download required jars

Add Spring-4.0.0 dependencies to the pom.xml

<!-- Spring dependencies -->
		<!-- for compile only, your container should have this -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>

		<!-- JSTL tag library -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

		<!-- Spring Core and Context -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- Spring MVC -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>

Folks who aren’t familiar with Maven concepts or don’t require maven for their project, can download the below jars individually from the spring site and include them in the classpath.

Directory Structure

Before moving on, let us understand the directory/package structure once you create project in Eclipse IDE
Maven has to follow certain directory structure

  • src/test/java –> test related files, mostly JUnit test cases
  • src/main/java –> create source java files under this source folder
  • src/main/resources –> all configuration files placed here
  • Maven Dependencies or Referenced Libraries –> includes jars in the classpath
  • WEB-INF under webapp –> stores web.xml & other configuration files related to web application
  • create a package “com.spring.series.mvc.controller” under src/main/java folder for our app or requirement

1_SpringMVC_MultiController_Directory_structure

Web application

For any web application, entry point is web.xml which describes how the incoming http requests are served / processed. Further, it describes about the global-context and local-context param (i.e.; <context-param> & <init-param>) for loading the files particular to project requirements & contains respective listener.

With this introduction, we will understand how we configured web.xml for our application with Multi-Action @Controller using Annotation support.

web.xml (the entry point à under WEB-INF)

Note: Normally, URI for any http request is looks like http://<server>:<port>/<context-root>/<from-here-application-specific-path>

This web.xml file describes,

  • all http requests with wild card pattern “/” will be intercepted by the configured servlet called “DispatcherServlet” (org.springframework.web.servlet.DispatcherServlet)
  • </servlet-name> name mentioned in the web.xml is mvc-dispatcher, which on loading/exploding the war into the application server(Tomcat application server for our example) looks for the servlet filename called “mvc-dispatcher-servlet.xml”
  • <context-param> with its attributes describes the location of the file from where it has to be loaded
  • “mvc-dispatcher-servlet.xml” is the file which describes how exactly specific http requests are handled or which controllers gets invoked for certain http requests. We will see “mvc-dispatcher-servlet.xml” file for our Spring MVC web application
  • <welcome-file-list> files under this tag will displayed to the user when there is no specific path continuing with basic URL path. In this example, filename is login.jsp typically it can any file valid for the context

web.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" 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">

	<!-- project display for web application -->
	<display-name>SpringMVC-Annotation</display-name>

	<!-- welcome file list -->
	<welcome-file-list>
		<!-- <welcome-file>index.jsp</welcome-file> -->
		<welcome-file>login.jsp</welcome-file>
	</welcome-file-list>

	<!-- Spring MVC DispatcherServlet: dispatches HTTP requests to registered controllers -->
	<servlet>
		<servlet-name>mvc-dispatcher</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>mvc-dispatcher</servlet-name>
		<url-pattern>/</url-pattern><!-- *.html -->
	</servlet-mapping>

	<!-- location of the root application context xml file -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
	</context-param>

	<!-- context loader listener -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
</web-app>

Dispatcher Servlet

Below mvc-dispatcher-servlet.xml file defines,

  • <context:component-scan base-package=”” /> this tag scans all classes & sub-classes under the value of base-package attribute and register them with the container
  • Basically these classes are annotated with @Controller on top of the class with value attribute for URL path
  • Other bean element, basically defines the logic for view resolver i.e.; viewName returned by the Controller which will be sandwiched between the prefix (WEB-INF/jsp) & suffix (.jsp)
  • Now, there should be a file under the directory as defined i.e.; (WEB-INF/jsp/<viewName>.jsp)
  • Otherwise, not found error thrown

mvc-dispatcher-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<!-- scan packages to register controllers which are annotated -->
	<context:component-scan base-package="com.spring.series.mvc.controller" />

	<!-- view resolver for rendering the final output -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix">
			<value>/WEB-INF/jsp/</value>
		</property>
		<property name="suffix">
			<value>.jsp</value>
		</property>
	</bean>
</beans>

Let’s see coding in action

 

Controller class

MyFirstMVController.java

package com.spring.series.mvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/controller")
public class MyFirstMVController {

	// http://localhost:8081/SpringMVC-Annotation/controller/

	/**
	 * this is default request handler, when there is no specific method-level mapping after class-level mapping
	 *
	 * request type = GET
	 *
	 * http://localhost:8081/SpringMVC-Annotation/controller/
	 *
	 */
	@RequestMapping(value="/", method=RequestMethod.GET)
	public String defaultMultiControllerAction() {
		return "default";
	}

	// http://localhost:8081/SpringMVC-Annotation/controller/firstmvc.htm

	/**
	 * this is the same controller, as seen in the first example
	 *
	 *  request type = GET
	 *
	 * http://localhost:8081/SpringMVC-Annotation/controller/firstmvc.htm
	 *
	 */
	@RequestMapping(value="/firstmvc.htm", method=RequestMethod.GET)
	public ModelAndView firstMVController() {

		ModelAndView modelAndView = new ModelAndView("showMessage");
		modelAndView.addObject("message", "Welcome To Spring MVC Annotation based web application");
		return modelAndView;
	}

	// http://localhost:8081/SpringMVC-Annotation/controller/hello?guestName=Shaz

	/**
	 * a simple example with get request appending input arguments in the URL after question(?) mark
	 *
	 * request type = GET
	 *
	 * http://localhost:8081/SpringMVC-Annotation/controller/hello.htm?guestName=Shaz
	 *
	 */
	@RequestMapping(value="/hello.htm", method=RequestMethod.GET)
	public String sayHelloToGuestUser(@RequestParam(required=false, defaultValue="User") String guestName, Model model) {

		model.addAttribute("user", "Hello " + guestName);
		return "hello";
	}

	// http://localhost:8081/SpringMVC-Annotation/controller/login

	/**
	 * a POST request with input arguments as login credentials to the web page
	 *
	 * request type = POST
	 *
	 * http://localhost:8081/SpringMVC-Annotation/controller/login
	 * userName = admin
	 * password = admin123
	 *
	 */
	@RequestMapping(value="/login.htm", method=RequestMethod.POST)
	public String loginToWeb(@RequestParam(required=true) String userName, @RequestParam(required=true) String password) {

		if((null != userName && "admin".equalsIgnoreCase(userName)) && (null != password && "admin123".equalsIgnoreCase(password))){
			return "success";
		}
		return "error";
	}
}

We will understand each method with its annotation and attributes in detail.

Note:

  • Basic URL path–> http://<server>:<port>/<context-root>/
  • class-level mapping –> @RequestMapping(“/controller”)

Method Name: public String defaultMultiControllerAction()
Input Arguments: None
Request Mapping: @RequestMapping(value=”/”, method=RequestMethod.GET)
HTTP Method: GET
URL: http://localhost:8081/SpringMVC-Annotation/controller/
Description: If we enter just the basic URL path as mentioned above, then this method gets invoked to service the http GET request

Method Name: public ModelAndView firstMVController()
Input Arguments: None
Request Mapping: @RequestMapping(value=”/firstmvc.htm”, method=RequestMethod.GET)
HTTP Method: GET
URL: http://localhost:8081/SpringMVC-Annotation/controller/firstmvc.htm
Description: If we append the “/firstmvc.htm” after the basic URL path, then this method will be invoked

Method Name: public String sayHelloToGuestUser(@RequestParam(required=false, defaultValue=”User”) String guestName, Model model)
Input Arguments: String parameter & Model object
Request Mapping: @RequestMapping(value=”/hello.htm”, method=RequestMethod.GET)
HTTP Method: GET
URL: http://localhost:8081/SpringMVC-Annotation/controller/hello.htm?guestName=Shaz
Description:

  • Similar to above request but with input argument “guestName”, which will be passed in the Model to the view part for displaying a welcome message to guest user.
  •  The other argument “Model” is used to set the message which will be later rendered at the view technology to display the welcome message to guest user
  • @RequestParam –> used to map the incoming input arguments with the HTTP GET request
  • Attribute required=false –> denotes that it is not a mandatory parameter, if not provided defaultValue will be used which is “User” in our case

Method Name: public String loginToWeb(@RequestParam(required=true) String userName, @RequestParam(required=true) String password)
Input Arguments: username/password à (admin/admin123)
Request Mapping: @RequestMapping(value=”/login.htm”, method=RequestMethod.POST)
HTTP Method: POST
URL: http://localhost:8081/SpringMVC-Annotation/controller/login.htm
Description:

  • This is similar to login page and since credentials aren’t to be shared with the outside world therefore it is kept as HTTP POST request.
  • Upon entering the basic URL path into the browser, which will display login page asking “username” & “password” with a “Login” button to click for authentication.
  • On clicking “Login” button, which will invoke this method for authentication to redirect to success page for correct credentials, otherwise redirecting to error page with message “Bad Credentials”
  • @RequestParam –> used to map the incoming input arguments with the HTTP POST request, which will be sent in the body separately not in the URL itself as in the previous case.
  • Attribute required=true –> denotes that it is a mandatory parameter

That’s all with the core part of the Spring MVC Framework; we will look into building a view to output the result to the end user after rendering it

View Resolver: The JSP (Java Server Page)

We have got four different methods with @Controller & @RequestMapping annotations with different mappings at method-level. Let’s us explore one-by-one to understand how exactly this needs to be consumed

@Controller Method 1: Default page
Enter the URL http://localhost:8081/SpringMVC-Annotation/controller/ into your favorite browser
Rendered output
2_SpringMVC_Default_Page

@Controller Method 2: Method-level mapping (“/firstmvc.htm”)
Enter the URL http://localhost:8081/SpringMVC-Annotation/controller/firstmvc.htm into your favorite browser
Rendered output
3_SpringMVC_method_level_mapping

@Controller Method 3: Method-level mapping (“/hello.htm”) with input arguments in the HTTP GET URL
Enter the URL: http://localhost:8081/SpringMVC-Annotation/controller/hello.htm?guestName=Shaz into your favorite browser
Rendered output:
4_SpringMVC_method_level_mapping_with_input_arguments

@Controller Method 4: For this case, we need to hop from the login page i.e.; after the entering the username/password combination, this method will get invoked
Steps to follow:

  • Enter the basic context path URL “http://localhost:8081/SpringMVC-Annotation“
  • Displays login page with input field for username/password
  • Enter the correct credentials and click “Login” button
  • On clicking “Login”  button, will invoke this method and authenticates with the provided credentials
  • There are two possible outcomes
  • If the credentials are authenticate and are correct, then success page will be displayed
  • Otherwise, error page with “Bad Credentials” message

Enter the URL http://localhost:8081/SpringMVC-Annotation/ into the browser
5_SpringMVC_POST_Login_Credentials
After entering correct credentials (admin/admin123)
6_SpringMVC_POST_Login_Succes_Page
Enter wrong credentials and see what browser displays (admin/test123)
7_SpringMVC_POST_Login_Error_Page

Conclusion: We have seen Multi action controller with latest spring annotations support. This way it is quite easy to build similar HTTP GET/POST methods under the same @Controller component class.

Download project

Spring-MVC-Multi-Controller-actions-using-@Controller (4kB)

 

Read Also:

 

Happy Coding !!
Happy Learning !!

Spring MVC: Creating RESTful web service using annotation
Spring MVC: Annotation based “Hello World” web application