Spring Boot integration
This guide targets Spring Boot 3.2+ with Java 17 or later (Kotlin also supported). The avsb-spring-boot-starter dependency provides Spring Boot auto-configuration that registers the SDK as a singleton bean, adds a HandlerInterceptor for per-request context scoping, and exposes an AvsbClient bean ready for constructor injection. By the end you will be reading flags in controllers and Spring components with no boilerplate initialisation code.
Add the dependency
Maven:
1<dependency>2 <groupId>com.avsbhq</groupId>3 <artifactId>avsb-spring-boot-starter</artifactId>4 <version>1.0.0</version>5</dependency>Gradle (Kotlin DSL):
1implementation("com.avsbhq:avsb-spring-boot-starter:1.0.0")Obtain your SDK key
Open your A vs B project, go to Settings → Environments, and copy the Server SDK key. Set it via an environment variable or in your application.yaml:
1avsb:2 sdk-key: ${AVSB_SDK_KEY}3
4 # Optional: polling interval in milliseconds (default 60000)5 polling-interval: 60000Configure context scoping (optional)
Implement the AvsbContextFactory interface and declare it as a Spring bean. The interceptor calls it once per request to build the EvalContext from the current HttpServletRequest.
1import com.avsbhq.avsb.AvsbContextFactory;2import com.avsbhq.avsb.core.EvalContext;3import jakarta.servlet.http.HttpServletRequest;4import org.springframework.stereotype.Component;5import java.util.Map;6
7@Component8public class RequestContextFactory implements AvsbContextFactory {9
10 @Override11 public EvalContext fromRequest(HttpServletRequest request) {12 String userId = request.getHeader("X-User-Id");13 String plan = request.getHeader("X-User-Plan");14 return EvalContext.user(15 userId != null ? userId : "anon",16 Map.of("plan", plan != null ? plan : "free")17 );18 }19}Inject AvsbClient into a controller
1import com.avsbhq.avsb.AvsbClient;2import com.avsbhq.avsb.core.Flag;3import org.springframework.web.bind.annotation.GetMapping;4import org.springframework.web.bind.annotation.RestController;5import java.util.Map;6
7@RestController8public class CheckoutController {9
10 private final AvsbClient avsb;11
12 public CheckoutController(AvsbClient avsb) {13 this.avsb = avsb;14 }15
16 @GetMapping("/checkout")17 public Map<String, Object> show() {18 Flag<Boolean> checkout = avsb.getBoolFlag("checkout-v2", false);19 Flag<String> theme = avsb.getStringFlag("ui-theme", "default");20
21 return Map.of(22 "showNewCheckout", checkout.getValue(),23 "theme", theme.getValue()24 );25 }26}AvsbClient bean injected by the auto-configuration is request-scoped when the interceptor is active. Injecting it as a constructor parameter in a @RestController (which is singleton-scoped by default) uses a CGLIB proxy — the same pattern Spring uses for HttpServletRequest injection.Read a flag
1// Boolean — generic type inferred2Flag<Boolean> flag = avsb.getBoolFlag("checkout-v2", false);3if (flag.getValue()) { /* new experience */ }4
5// String6Flag<String> theme = avsb.getStringFlag("ui-theme", "default");7
8// Number9Flag<Double> timeout = avsb.getDoubleFlag("api-timeout-secs", 30.0);10
11// JSON (Jackson-deserialised)12Flag<PricingConfig> config = avsb.getJsonFlag("pricing-config",13 PricingConfig.class, PricingConfig.getDefault());14
15// Metadata16System.out.printf("Source: %s, Variation: %s%n",17 flag.getSource(), flag.getVariationKey());Track an event
1avsb.track("purchase", TrackPayload.builder()2 .value(149.99)3 .property("currency", "usd")4 .property("sku", "PRO-ANNUAL")5 .build());Identify a user
Inject AvsbServer when you need to create a scoped client for a different user mid-request (for example after a service-to-service token exchange):
1import com.avsbhq.avsb.AvsbServer;2
3// In constructor: private final AvsbServer server;4
5EvalContext ctx = EvalContext.user(user.getId(),6 Map.of("plan", user.getPlan(), "orgId", user.getOrgId()));7AvsbClient scoped = server.forUser(ctx);8boolean showNew = scoped.getBoolFlag("checkout-v2", false).getValue();Graceful shutdown
The auto-configuration registers the AvsbServer bean as a DisposableBean. Spring Boot's graceful shutdown sequence (enabled by default in Spring Boot 3.x) calls destroy() after in-flight requests complete — the SDK flushes pending events and stops the polling thread automatically. No extra configuration is required.
Testing
1import com.avsbhq.avsb.testing.AvsbMockAutoConfiguration;2import com.avsbhq.avsb.testing.MockAvsbClient;3import org.junit.jupiter.api.Test;4import org.springframework.beans.factory.annotation.Autowired;5import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;6import org.springframework.context.annotation.Import;7import org.springframework.test.web.servlet.MockMvc;8import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;9import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;10
11@WebMvcTest(CheckoutController.class)12@Import(AvsbMockAutoConfiguration.class)13class CheckoutControllerTest {14
15 @Autowired MockMvc mockMvc;16 @Autowired MockAvsbClient mockAvsb;17
18 @Test19 void showsNewCheckout_whenFlagOn() throws Exception {20 mockAvsb.flagOn("checkout-v2");21
22 mockMvc.perform(get("/checkout"))23 .andExpect(jsonPath("$.showNewCheckout").value(true));24 }25
26 @Test27 void showsLegacy_whenFlagOff() throws Exception {28 mockAvsb.flagOff("checkout-v2");29
30 mockMvc.perform(get("/checkout"))31 .andExpect(jsonPath("$.showNewCheckout").value(false));32 }33}What's next
- Java SDK reference — full
AvsbServerandAvsbClientAPI including Micronaut and Quarkus adapters. - ASP.NET Core integration — the same auto-configuration pattern for .NET.
- Sticky bucketing — guarantee consistent variation assignment across requests using Redis or DynamoDB.