Hello World
Now that we have a Spring Boot application that can run, let’s add some simple functionality. We want to expose an HTTP/REST endpoint at /api/hola that will return “Hola Spring Boot from X” where X is the IP address where the service is running. To do this, navigate to src/main/java/com/example. This location should have been created for you if you followed the preceding steps; remember, the groupId we passed to the spring init program did not apply groupId to the Java package hierarchy, and we’ve left it as it is which should be “com.example”. Then create a new Java class called HolaR estController, as shown in Example 2-1. We’ll add a method named hola() that returns a string along with the IP address of where the service is running. You’ll see in Chapter 5, in our load balancing and service discovery sections, how the host IPs can be used to demonstrate proper failover, loadbalancing, etc.
Example 2-1. src/main/java/com/example/HolaRestController.java
public class HolaRestController {
public String hola() throws UnknownHostException {
String hostname = null;
try {
hostname = InetAddress.getLocalHost() .getHostAddress();
} catch (UnknownHostException e) { hostname = "unknown";
}
return "Hola Spring Boot de " + hostname; }
}
Add the HTTP Endpoints
At this point, this piece of code is just a POJO (plain old Java object) and you could (and should) write a unit test that verifies its behavior. To expose this as a REST endpoint, we’re going to make use of the following annotations in Example 2-2:
@RestController
Tell Spring this is an HTTP controller capable of exposing HTTP endpoints (GET, PUT, POST, etc.).
@RequestMapping
Map specific parts of the HTTP URI path to classes, methods, and parameters in the Java code.
Note, import statements are omitted.
Example 2-2. src/main/java/com/example/HolaRestController.java
@RestController
@RequestMapping("/api") public class HolaRestController {
@RequestMapping(method = RequestMethod.GET, value = "/hola",
produces = "text/plain")
public String hola() throws UnknownHostException {
String hostname = null;
try {
hostname = InetAddress.getLocalHost() .getHostAddress();
} catch (UnknownHostException e) { hostname = "unknown";
}
return "Hola Spring Boot de " + hostname; }
}
In this code, all we’ve done is add the aforementioned annotations. For example, @RequestMapping("/api") at the Class level says “map any method-level HTTP endpoints under this root URI path.” When we add @RequestMapping(method = RequestMethod.GET, value = "/hola", produces = "text/plain"), we are telling Spring to expose an HTTP GET endpoint at /hola (which will really be /api/ hola) and map requests with media type of Accept: text/plain to this method. Spring Boot defaults to using an embedded Tomcat servlet container, but this can be switched to other options like Undertow or Jetty.
If we build our application and run spring-boot:run again, we should be able to reach our HTTP endpoint:
$ mvn clean package spring-boot:run
Now if we point our browser to http://localhost:8080/api/hola, we should see a response similar to:
What if we want to add some environment-aware configuration to our application? For example, instead of saying “Hola,” maybe we want to say “Guten Tag” if we deploy our app in production for German users? We need a way to inject properties to our app. Externalize Configuration
Spring Boot makes it easy to use external property sources like properties files, command-line arguments, the OS environment, or Java System properties. We can even bind entire “classes” of properties to objects in our Spring context. For example, if I want to bind all helloapp.* properties to my HolaRestController, I can add
@ConfigurationProperties(prefix="helloapp"), and Spring Boot will automatically try to bind helloapp.foo and helloapp.bar
to Java Bean properties in the HolaRestController class. Let’s define a new property in src/main/resources/application.properties called helloapp.saying. The application.properties file was automatically created for us when we created our project. Note we could change the file name to application.yml and Spring would still recognize it as a YAML file as the source of properties.
Let’s add a new property to our src/main/resources/application.properties file:
helloapp.saying=Guten Tag aus
In the HolaRestController in Example 2-3, let’s add the @Configu rationProperties annotation and our new saying field. Note we also need setters and getters.
Example 2-3. src/main/java/com/example/HolaRestController.java
@RestController
@RequestMapping("/api")
@ConfigurationProperties(prefix="helloapp") public class HolaRestController { private String saying;
@RequestMapping(method = RequestMethod.GET, value = "/hola",
produces = "text/plain")
public String hola() throws UnknownHostException {
String hostname = null;
try {
hostname = InetAddress.getLocalHost() .getHostAddress();
} catch (UnknownHostException e) { hostname = "unknown";
}
return saying + " " + hostname; }
public String getSaying() { return saying; }
public void setSaying(String saying) {
this.saying = saying;
}
}
Let’s stop our application from running before (if we haven’t) and restart it:
$ mvn clean package spring-boot:run
Now if we navigate to http://localhost:8080/api/hola, we should see the German version of the saying:
We can now externalize properties that would change depending on the environment in which we are running. Things like service URIs, database URIs and passwords, and message queue configurations would all be great candidates for external configuration. Don’t overdo it though; not everything needs to change depending on the environment in which it runs! Ideally an application would be configured exactly the same in all environments including timeouts, thread pools, retry thresholds, etc.
Expose Application Metrics and Information
If we want to put this microservice into production, how will we monitor it? How can we get any insight about how things are running? Often our microservices are black boxes unless we explicitly think through how we want to expose metrics to the outside world. Spring Boot comes with a prepackaged starter called actuator that makes doing this a breeze.
Let’s see what it takes to enable the actuator. Open up the pom.xml file for your hola-springboot microservice and add the following
Maven dependency within the <dependencies>...</dependen cies> section:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Now restart your microservice by stopping it and running:
$ mvn clean package spring-boot:run
Just by adding the actuator dependency, our application now has a lot of information exposed that would be very handy for debugging or general microservice insight. Try hitting the following URLs and examine what gets returned:
- http://localhost:8080/beans
- http://localhost:8080/env
- http://localhost:8080/health
- http://localhost:8080/metrics
- http://localhost:8080/trace
- http://localhost:8080/mappings
Here’s an example of what the http://localhost:8080/envendpoint looks like:
Exposing runtime insight like this relieves the developer to just focus on writing code for the microservice that delivers business value. Delegating to frameworks to do heavy lifting and boilerplate is definitely a good idea.
How to Run This Outside of Maven?
Up to this point we’ve been thinking through development and building our hello-world microservice from the perspective of a developer’s laptop using Maven. But what if you want to distribute
your microservice to others or run it in a live environment (development, QA, production)?
Luckily, with Spring Boot it only takes a few steps to get us ready for shipment and production. Spring Boot prefers atomic, executable JARs with all dependencies packed into a flat classpath. This means the JAR that we create as part of a call to mvn clean package is executable and contains all we need to run our microservice in a Java environment! To test this out, go to the root of our holaspringboot microservice project and run the following commands:
$ mvn clean package
$ java -jar target/hola-springboot-1.0.jar
If your project was named demo instead of hola-springboot, then substitute the properly named JAR file (demo-1.0.jar).
That’s it!
We’ll notice this sort of idiom when we explore Dropwizard and WildFly Swarm.