diff --git a/README.md b/README.md
index 5b3bb30..c757044 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,8 @@ Next, you can view traces that went through the backend via http://localhost:941
* This is a locally run zipkin service which keeps traces in memory
## Starting the Services
+
+### Servlet Container Option
In a separate tab or window, start each of [brave.webmvc.Frontend](/webmvc4/src/main/java/brave/webmvc/Frontend.java)
and [brave.webmvc.Backend](/webmvc4/src/main/java/brave/webmvc/Backend.java):
```bash
@@ -39,6 +41,15 @@ $ mvn jetty:run -Pfrontend
$ mvn jetty:run -Pbackend
```
+### Spring Boot Option
+In a separate tab or window, start each of [brave.webmvc.Frontend](/webmvc4-boot/src/main/java/brave/webmvc/Frontend.java)
+and [brave.webmvc.Backend](/webmvc4-boot/src/main/java/brave/webmvc/Backend.java):
+```bash
+$ cd webmvc4-boot
+$ mvn compile exec:java -Dexec.mainClass=brave.webmvc.Backend
+$ mvn compile exec:java -Dexec.mainClass=brave.webmvc.Frontend
+```
+
Next, run [Zipkin](http://zipkin.io/), which stores and queries traces
reported by the above services.
diff --git a/webmvc4-boot/README.md b/webmvc4-boot/README.md
new file mode 100644
index 0000000..bf14563
--- /dev/null
+++ b/webmvc4-boot/README.md
@@ -0,0 +1,18 @@
+## WebMVC 4 Boot Example
+
+Instead of servlet, this uses Spring Boot 1.5 to create a self-contained
+application that runs Spring WebMVC 4 controllers.
+
+* brave.webmvc.Frontend and Backend : Rest controllers with no tracing configuration
+* brave.webmvc.TracingConfiguration : This adds tracing by configuring the tracer, server and client tracing interceptors.
+
+`TracingConfiguration` is automatically loaded due to `META-INF/spring.factories`
+This allows the `Frontend` and `Backend` controllers to have no tracing
+code. Inside the tracing configuration, you'll notice the rest template
+is setup via a `RestTemplateCustomizer`, which ensures application-level
+interceptors are not affected. Also, you'll notice layered tracing for
+server requests. First, `TracingFilter` creates a span, then later,
+`SpanCustomizingAsyncHandlerInterceptor` adds MVC tags to it.
+
+*Note* This only lightly configures tracing. When doing anything serious,
+consider [Spring Cloud Sleuth](https://github.com/spring-cloud/spring-cloud-sleuth) instead.
diff --git a/webmvc4-boot/pom.xml b/webmvc4-boot/pom.xml
new file mode 100644
index 0000000..d66633d
--- /dev/null
+++ b/webmvc4-boot/pom.xml
@@ -0,0 +1,69 @@
+
+ 4.0.0
+
+ io.zipkin.brave
+ brave-webmvc4-boot-example
+ 1.0-SNAPSHOT
+ jar
+
+ brave-webmvc4-boot-example
+ Example using Brave to trace RPCs from Spring Web MVC
+
+
+ UTF-8
+ 1.7
+ 1.7
+
+ 1.5.14.RELEASE
+ 5.1.3
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+ io.zipkin.brave
+ brave-bom
+ ${brave.version}
+ pom
+ import
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ io.zipkin.brave
+ brave
+
+
+ io.zipkin.reporter2
+ zipkin-sender-okhttp3
+
+
+ io.zipkin.brave
+ brave-context-slf4j
+
+
+ io.zipkin.brave
+ brave-instrumentation-spring-web
+
+
+ io.zipkin.brave
+ brave-instrumentation-spring-webmvc
+
+
+
diff --git a/webmvc4-boot/src/main/java/brave/webmvc/Backend.java b/webmvc4-boot/src/main/java/brave/webmvc/Backend.java
new file mode 100644
index 0000000..9dff48c
--- /dev/null
+++ b/webmvc4-boot/src/main/java/brave/webmvc/Backend.java
@@ -0,0 +1,28 @@
+package brave.webmvc;
+
+import java.util.Date;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@EnableAutoConfiguration
+@RestController
+public class Backend {
+
+ @RequestMapping("/api")
+ public String printDate(@RequestHeader(name = "user-name", required = false) String username) {
+ if (username != null) {
+ return new Date().toString() + " " + username;
+ }
+ return new Date().toString();
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(Backend.class,
+ "--spring.application.name=backend",
+ "--server.port=9000"
+ );
+ }
+}
diff --git a/webmvc4-boot/src/main/java/brave/webmvc/Frontend.java b/webmvc4-boot/src/main/java/brave/webmvc/Frontend.java
new file mode 100644
index 0000000..34aa935
--- /dev/null
+++ b/webmvc4-boot/src/main/java/brave/webmvc/Frontend.java
@@ -0,0 +1,33 @@
+package brave.webmvc;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+
+@EnableAutoConfiguration
+@RestController
+@CrossOrigin // So that javascript can be hosted elsewhere
+public class Frontend {
+
+ @Autowired RestTemplate restTemplate;
+
+ @RequestMapping("/") public String callBackend() {
+ return restTemplate.getForObject("http://localhost:9000/api", String.class);
+ }
+
+ @Bean RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(Frontend.class,
+ "--spring.application.name=frontend",
+ "--server.port=8081"
+ );
+ }
+}
diff --git a/webmvc4-boot/src/main/java/brave/webmvc/TracingConfiguration.java b/webmvc4-boot/src/main/java/brave/webmvc/TracingConfiguration.java
new file mode 100644
index 0000000..e3352c6
--- /dev/null
+++ b/webmvc4-boot/src/main/java/brave/webmvc/TracingConfiguration.java
@@ -0,0 +1,86 @@
+package brave.webmvc;
+
+import brave.Tracing;
+import brave.context.slf4j.MDCCurrentTraceContext;
+import brave.http.HttpTracing;
+import brave.propagation.B3Propagation;
+import brave.propagation.ExtraFieldPropagation;
+import brave.servlet.TracingFilter;
+import brave.spring.web.TracingClientHttpRequestInterceptor;
+import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;
+import java.util.ArrayList;
+import java.util.List;
+import javax.servlet.Filter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.client.RestTemplateCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+import zipkin2.Span;
+import zipkin2.reporter.AsyncReporter;
+import zipkin2.reporter.Sender;
+import zipkin2.reporter.okhttp3.OkHttpSender;
+
+/**
+ * This adds tracing configuration to any web mvc controllers or rest template clients.
+ */
+@Configuration
+// Importing a class is effectively the same as declaring bean methods
+@Import(SpanCustomizingAsyncHandlerInterceptor.class)
+public class TracingConfiguration extends WebMvcConfigurerAdapter {
+
+ /** Configuration for how to send spans to Zipkin */
+ @Bean Sender sender() {
+ return OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans");
+ }
+
+ /** Configuration for how to buffer spans into messages for Zipkin */
+ @Bean AsyncReporter spanReporter() {
+ return AsyncReporter.create(sender());
+ }
+
+ /** Controls aspects of tracing such as the name that shows up in the UI */
+ @Bean Tracing tracing(@Value("${spring.application.name}") String serviceName) {
+ return Tracing.newBuilder()
+ .localServiceName(serviceName)
+ .propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "user-name"))
+ .currentTraceContext(MDCCurrentTraceContext.create()) // puts trace IDs into logs
+ .spanReporter(spanReporter()).build();
+ }
+
+ /** decides how to name and tag spans. By default they are named the same as the http method. */
+ @Bean HttpTracing httpTracing(Tracing tracing) {
+ return HttpTracing.create(tracing);
+ }
+
+ /** Creates client spans for http requests */
+ @Bean @Order(Ordered.HIGHEST_PRECEDENCE)
+ RestTemplateCustomizer tracingRestTemplateCustomizer(final HttpTracing httpTracing) {
+ return new RestTemplateCustomizer() {
+ @Override public void customize(RestTemplate restTemplate) {
+ List interceptors =
+ new ArrayList<>(restTemplate.getInterceptors());
+ interceptors.add(0, TracingClientHttpRequestInterceptor.create(httpTracing));
+ }
+ };
+ }
+
+ /** Creates server spans for http requests */
+ @Bean Filter tracingFilter(HttpTracing httpTracing) {
+ return TracingFilter.create(httpTracing);
+ }
+
+ @Autowired SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;
+
+ /** Decorates server spans with application-defined web tags */
+ @Override public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(webMvcTracingCustomizer);
+ }
+}
diff --git a/webmvc4-boot/src/main/resources/META-INF/spring.factories b/webmvc4-boot/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..248137f
--- /dev/null
+++ b/webmvc4-boot/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+brave.webmvc.TracingConfiguration
diff --git a/webmvc4-boot/src/main/resources/application.properties b/webmvc4-boot/src/main/resources/application.properties
new file mode 100644
index 0000000..12fab19
--- /dev/null
+++ b/webmvc4-boot/src/main/resources/application.properties
@@ -0,0 +1,5 @@
+# spring.application.name and server.port are set in the main methods,
+# so not done here
+logging.level.org.springframework.web=DEBUG
+# Adds trace and span IDs to logs (when a trace is in progress)
+logging.pattern.level=%d{ABSOLUTE} [%X{traceId}/%X{spanId}] %-5p [%t] %C{2} - %m%n