Spring @MatrixVariable in a map

In this tutorial I will show you how to obtain matrix variable (using @MatrixVariable annotation) in a map from the URL segment. The URI has the possibility of including name-value pairs within path segments. INnSpring MVC these are referred to as matrix variables.

If a URL is expected to contain matrix variables, the request mapping pattern must represent them with a URI template. This ensures the request can be matched correctly regardless of whether matrix variables are present or not and in what order they are provided.

All matrix variables may be obtained in a Map:

// GET /owners/42;q=11;r=12/pets/21;q=22;s=23
@RequestMapping(value = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)
public void findPet(
        @MatrixVariable Map<String, String> matrixVars,
        @MatrixVariable(pathVar="petId"") Map<String, String> petMatrixVars) {
    // matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
    // petMatrixVars: ["q" : 11, "s" : 23]
}

I am going to use Thymeleaf template for view file to display data on UI (User Interface).

Prerequisites

Java at least 8, Gradle 6.5.1, Maven 3.6.3, Spring Boot 2.3.3, Thymeleaf Template

Project Setup

You can create either gradle or maven based project in your favorite IDE or tool. The name of the project is spring-matrix-variable-in-map.

If you are creating gradle based project then use below build.gradle script:

buildscript {
	ext {
		springBootVersion = '2.3.3.RELEASE'
	}
	
    repositories {
    	mavenCentral()
    }
    
    dependencies {
    	classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

plugins {
    id 'java-library'
    id 'org.springframework.boot' version "${springBootVersion}"
}

sourceCompatibility = 12
targetCompatibility = 12

repositories {
    mavenCentral()
}

dependencies {
	implementation "org.springframework.boot:spring-boot-starter-web:${springBootVersion}"
	implementation("org.springframework.boot:spring-boot-starter-thymeleaf:${springBootVersion}")
}

If you are creating maven based project then use below pom.xml file:

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.roytuts</groupId>
	<artifactId>spring-matrix-variable-default-value</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.3.RELEASE</version>
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
	</dependencies>

    <build>
        <plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
				<configuration>
					<source>at least 8</source>
					<target>at least 8</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Web Configuration

@MatrixVariable annotation indicates that a method parameter should be bound to a name-value pair within a path segment. @MatrixVariable annotation is disabled by default in Spring MVC. In order to enable it you need to tweak the configuration and set the removeSemicolonContent property of RequestMappingHandlerMapping to false. By default it is set to true.

If you do not set it to false then you will see the following exception in your application:

Missing matrix variable 'q' for method parameter of type String

In order to support matrix variable in your Spring MVC application, you need to do the following in your Spring Boot application:

package com.roytuts.spring.matrix.variable.in.map.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;

@Configuration
public class WebConfig implements WebMvcConfigurer {

	@Override
	public void configurePathMatch(PathMatchConfigurer configurer) {
		UrlPathHelper urlPathHelper = new UrlPathHelper();
		urlPathHelper.setRemoveSemicolonContent(false);
		configurer.setUrlPathHelper(urlPathHelper);
	}
}

Web Controller

You need to create a Spring web controller to render your view on UI.

In the following example, I have path variable, matrix variable and the model where I am passing the value of the matrix variable to be available on UI.

package com.roytuts.spring.matrix.variable.in.map.controller;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.PathVariable;

@Controller
public class HomeController {

	@GetMapping("/owner/{ownerId}/report/{reportId}")
	public String homePage(@PathVariable String ownerId, @MatrixVariable Map<String, List<String>> matrixVars,
			@MatrixVariable(pathVar = "reportId") Map<String, List<String>> reportMatrixVars, Model model) {
		System.out.println("ownerId : " + ownerId);

		List<List<String>> matrixVarlist = mapToList(matrixVars);
		for (List<String> list : matrixVarlist) {
			for (String string : list) {
				System.out.println(string);
			}
		}

		List<List<String>> reportMatrixVarlist = mapToList(matrixVars);

		for (List<String> list : reportMatrixVarlist) {
			for (String string : list) {
				System.out.println(string);
			}
		}

		model.addAttribute("matrixVars", matrixVars);
		model.addAttribute("reportMatrixVars", reportMatrixVars);

		return "index";
	}

	private List<List<String>> mapToList(Map<String, List<String>> matrixVarMap) {
		List<List<String>> outlist = new ArrayList<List<String>>();
		Collection<Entry<String, List<String>>> matrixVarSet = matrixVarMap.entrySet();

		for (Entry<String, List<String>> entry : matrixVarSet) {
			List<String> rowList = new ArrayList<String>();

			String name = entry.getKey();
			rowList.add(name);

			List<String> matrixVar = entry.getValue();
			rowList.addAll(matrixVar);

			outlist.add(rowList);
		}
		return outlist;
	}

}

View File – UI

The following view file displays the value of matrix variable’s value when rendered in the browser. This template file – index.html – is kept under src/main/resources/templates folder.

I am iterating each matrix variable on Thymeleaf template view file and displaying the value in a table. I am also validating whether the matrix variable list is empty or not.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Boot Matrix Variable in a map</title>
</head>
<body>
	<div th:if="${not #lists.isEmpty(matrixVars)}">
	    <table>
	        <tr th:each="var : ${matrixVars}">
	            <td th:text="${var.value}"></td>
	        </tr>
	    </table>
	</div>
	
	<div th:if="${not #lists.isEmpty(reportMatrixVars)}">
	    <table>
	        <tr th:each="var : ${reportMatrixVars}">
	            <td th:text="${var.value}"></td>
	        </tr>
	    </table>
	</div>
</body>
</html>

Main Class

A class having main method with @SpringBootApplication is enough to deploy the application into embedded Tomcat server.

package com.roytuts.spring.matrix.variable.in.map;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringMatrixVariableInMapApp {

	public static void main(String[] args) {
		SpringApplication.run(SpringMatrixVariableInMapApp.class, args);
	}

}

Testing the Application

Now hit the URL http://localhost:8080/owner/42;q=11;r=12/report/21;q=22;s=23 in the browser, you will see the below output in the browser:

spring matrix variable in map

Console Output

ownerId : 42
q
11
22
r
12
s
23
q
11
22
r
12
s
23

Source Code

Download

Thanks for reading.

Leave a Reply

Your email address will not be published. Required fields are marked *