Variable Tutorial
This tutorial demonstrates passing values downstream between functions using variables within a Spring Boot application.
Unlike direct method parameters, variables do not need to be passed explicitly by the caller. A variable set in one function is available for injection into any downstream function, allowing pipelines to be composed from independent classes that know nothing of each other.
Maven dependency
<dependency>
<groupId>net.officefloor.springboot</groupId>
<artifactId>officefloor-rest-spring-boot-4-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Application class
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Out<T> / Var<T>
The following method demonstrates setting values for variables.
public class OutLogic {
public static void setValues(Out<Person> person, @Description Out<String> description) {
person.set(new Person("Daniel", "Sagenschneider"));
description.set("Need to watch his code!");
}
}
Variables are created by reflectively interrogating the method parameters. Because many variables may share the same type, qualifiers distinguish them — much like naming a variable.
The @Qualifier meta-annotation is the recommended approach. The qualifier used in this tutorial is:
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Description {
}
Prefer Out<T> to set a variable. This allows the compiler to reason about out/in combinations and catch uninitialised variables.
When a function must both read and write the same variable, use Var<T>. Use this sparingly as it reduces the ability to statically reason about flow composition.
public class VarLogic {
public static void setValues(Var<Person> person, @Description Var<String> description) {
person.set(new Person("Daniel", "Sagenschneider"));
description.set("Need to watch his code!");
}
}
@Val / In<T>
The following method demonstrates reading variable values downstream.
public class ValLogic {
public static void useValues(@Val Person person, @Description @Val String description,
ObjectResponse<ServerResponse> response) {
response.send(new ServerResponse(person, description));
}
}
@Val annotates a parameter to receive the current value of a variable. It distinguishes the variable from other injected objects such as ObjectResponse.
The preferred combination is Out<T> upstream with @Val downstream — readable and avoids importing extra framework types at the read site.
For symmetry there is also In<T>:
public class InLogic {
public static void useValues(In<Person> person, @Description In<String> description,
ObjectResponse<ServerResponse> response) {
response.send(new ServerResponse(person.get(), description.get()));
}
}
Composing flows — YAML endpoint files
The YAML files wire together the upstream setter and downstream reader as a two-step pipeline. Each step is independent — neither class references the other.
OutSet:
class: net.officefloor.tutorial.variablehttpserver.OutLogic
method: setValues
next: InUse
InUse:
class: net.officefloor.tutorial.variablehttpserver.InLogic
method: useValues
VarSet:
class: net.officefloor.tutorial.variablehttpserver.VarLogic
method: setValues
next: ValUse
ValUse:
class: net.officefloor.tutorial.variablehttpserver.ValLogic
method: useValues
OutToValSet:
class: net.officefloor.tutorial.variablehttpserver.OutLogic
method: setValues
next: OutToValUse
OutToValUse:
class: net.officefloor.tutorial.variablehttpserver.ValLogic
method: useValues
Testing
Tests use MockMvc to exercise the full composed pipeline through Spring MVC:
@SpringBootTest
@AutoConfigureMockMvc
public class VariableHttpServerTest {
@Autowired
private MockMvc mvc;
@Autowired
private ObjectMapper mapper;
private static final ServerResponse EXPECTED =
new ServerResponse(new Person("Daniel", "Sagenschneider"), "Need to watch his code!");
@Test
public void outIn() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/outIn")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json(mapper.writeValueAsString(EXPECTED)));
}
@Test
public void varVal() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/varVal")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json(mapper.writeValueAsString(EXPECTED)));
}
@Test
public void outVal() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/outVal")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json(mapper.writeValueAsString(EXPECTED)));
}
}
Next
The Kotlin tutorial demonstrates implementing REST procedure logic in Kotlin alongside Java procedures.

