How to Build a GrowingIO User‑Tracking Spring Boot Starter from Scratch
This article walks through the complete process of creating a Spring Boot Starter that wraps the GrowingIO Java SDK, covering the motivation, project setup, property abstraction, auto‑configuration, custom logging, service implementation, and a simple test to verify the starter works in a Spring Boot application.
Spring Boot has been around for years, yet many developers have never built their own Spring Boot Starter. This tutorial demonstrates how to encapsulate the GrowingIO user‑tracking Java SDK into a reusable Spring Boot Starter component.
Why a Starter?
The official GrowingIO SDK does not provide a Spring Boot Starter, making integration cumbersome, especially when using configuration centers in micro‑service architectures. Additionally, SDK version changes can tightly couple business code, and the raw API is verbose. A starter solves these issues by providing a simple, configurable, and test‑friendly API.
Starter Encapsulation Pattern
The basic pattern consists of three essential steps:
Define a XXXProperties class annotated with @ConfigurationProperties to map custom and SDK properties.
Create an auto‑configuration class ( XXXAutoConfiguration) that registers beans and loads properties into the SDK.
Add a spring.factories entry so Spring can discover the auto‑configuration.
Advanced extensions may include an @EnableXXX annotation, additional ordering or environment annotations, and an AOP interceptor for non‑intrusive event logging.
Step 1: Project Setup
Create a Maven project named growingio-spring-boot-starter and add the following dependencies:
<dependencies>
<dependency>
<groupId>io.growing.sdk.java</groupId>
<artifactId>growingio-java-sdk</artifactId>
<version>1.0.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.13</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.4.13</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
</dependencies>Step 2: Property Abstraction
Create GrowingioProperties to hold configuration values. Example:
@ConfigurationProperties(GrowingioProperties.GROWINGIO_PREFIX)
@Data
public class GrowingioProperties {
public static final String GROWINGIO_PREFIX = "growingio";
private String apiHost;
private String projectId;
private Integer sendMsgInterval = 100;
private Integer sendMsgThread = 3;
private Integer msgStoreQueueSize = 500;
private Boolean compress = true;
private String loggerLevel = "debug";
private String loggerImplemention = "cn.aijavapro.growingio.log.GrowingioLogger";
private String runMode = "test";
private Integer connectionTimeout = 2000;
private Integer readTimeout = 2000;
private Boolean enable = false;
}Define GrowingioConstant with the SDK keys (e.g., API_HOST_KEY = "api.host", PROJECT_ID_KEY = "project.id", etc.).
Step 3: Custom Logger
public class GrowingioLogger implements GioLoggerInterface {
private static final Logger logger = LoggerFactory.getLogger(GrowingioLogger.class);
@Override
public void debug(String s) { logger.info("测试阶段日志输出:{}", s); }
@Override
public void error(String s) { logger.error("测试阶段日志输出:{}", s); }
}Step 4: Request Wrapper
public class EventMessageRequest implements Serializable {
private static final long serialVersionUID = 892840357641768298L;
private String eventKey;
private String userId;
private Map<String, Object> eventVariableMap;
public EventMessageRequest(String eventKey, String userId, Map<String, Object> eventVariableMap) {
this.eventKey = eventKey;
this.userId = userId;
this.eventVariableMap = eventVariableMap;
}
}Step 5: Service Implementation
public class GrowingioServiceImpl implements GrowingioService {
private static final Logger logger = LoggerFactory.getLogger(GrowingioServiceImpl.class);
@Override
public void sendEventMessage(EventMessageRequest request) {
try {
if (StringUtils.isEmpty(request.getEventKey()) && StringUtils.isEmpty(request.getUserId())) {
return;
}
GIOEventMessage.Builder builder = new GIOEventMessage.Builder()
.eventTime(System.currentTimeMillis())
.eventKey(request.getEventKey())
.loginUserId(request.getUserId());
for (Map.Entry<String, Object> entry : request.getEventVariableMap().entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (value != null) {
if (value instanceof String) {
builder.addEventVariable(key, (String) value);
} else if (value instanceof Long) {
builder.addEventVariable(key, ((Long) value).toString());
} else if (value instanceof Integer) {
builder.addEventVariable(key, ((Integer) value));
} else if (value instanceof Double) {
builder.addEventVariable(key, ((Double) value));
} else if (value instanceof BigDecimal) {
builder.addEventVariable(key, ((BigDecimal) value).doubleValue());
} else {
logger.warn("not support event variable value type:{}", key);
}
}
}
GIOEventMessage eventMessage = builder.build();
GrowingAPI.send(eventMessage);
} catch (Exception e) {
logger.error("send growingio event message error:{}", e.getMessage(), e);
}
}
}Step 6: Auto‑Configuration
@Configuration
@EnableConfigurationProperties(GrowingioProperties.class)
public class GrowingioAutoConfiguration {
private static final Logger logger = LoggerFactory.getLogger(GrowingioAutoConfiguration.class);
@Autowired
private GrowingioProperties growingioProperties;
@Bean
public GrowingioService growingioService() { return new GrowingioServiceImpl(); }
private void checkProperties() {
if (StringUtils.isEmpty(growingioProperties.getApiHost())) {
throw new RuntimeException("growing properties api.host must be defined");
}
if (StringUtils.isEmpty(growingioProperties.getProjectId())) {
throw new RuntimeException("growing properties project.id must be defined");
}
}
@PostConstruct
private void init() {
checkProperties();
Properties props = new Properties();
props.setProperty(GrowingioConstant.API_HOST_KEY, growingioProperties.getApiHost());
props.setProperty(GrowingioConstant.PROJECT_ID_KEY, growingioProperties.getProjectId());
props.setProperty(GrowingioConstant.SEND_MSG_INTERVAL_KEY, growingioProperties.getSendMsgInterval().toString());
props.setProperty(GrowingioConstant.SEND_MSG_THREAD_KEY, growingioProperties.getSendMsgThread().toString());
props.setProperty(GrowingioConstant.MSG_STORE_QUEUE_SIZE_KEY, growingioProperties.getMsgStoreQueueSize().toString());
props.setProperty(GrowingioConstant.COMPRESS_KEY, growingioProperties.getCompress().toString());
props.setProperty(GrowingioConstant.LOGGER_LEVEL_KEY, growingioProperties.getLoggerLevel());
props.setProperty(GrowingioConstant.LOGGER_IMPL_KEY, growingioProperties.getLoggerImplemention());
props.setProperty(GrowingioConstant.RUN_MODE_KEY, growingioProperties.getRunMode());
props.setProperty(GrowingioConstant.CONNECTION_TIMEOUT_KEY, growingioProperties.getConnectionTimeout().toString());
props.setProperty(GrowingioConstant.READ_TIMEOUT_KEY, growingioProperties.getReadTimeout().toString());
ConfigUtils.init(props);
logger.info("init load growingio starter api properties success,url:{},runmode:{},enable:{}",
growingioProperties.getApiHost(), growingioProperties.getRunMode(), growingioProperties.getEnable());
}
}Add the following line to src/main/resources/META-INF/spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.aijavapro.growingio.config.GrowingioAutoConfigurationStep 7: Test the Starter
In a consumer Spring Boot project, add the starter dependency:
<dependency>
<groupId>cn.aijavapro</groupId>
<artifactId>growingio-spring-boot-stater</artifactId>
<version>1.0‑SNAPSHOT</version>
</dependency>Create application.yml with required properties (e.g., runMode: test) and a simple controller that calls growingioService.sendEventMessage(...) when an order is saved. Start the application and invoke curl http://localhost:8080/order/save. If the console shows the debug log from GrowingioLogger, the starter is working.
Conclusion
The article walks through building a minimal Spring Boot Starter for GrowingIO, covering property abstraction, auto‑configuration, custom logging, service logic, and verification. The same pattern can be reused for other analytics SDKs such as SensorsData or ChatGPT Java SDKs.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Ubiquitous Tech
A ubiquitous public account for pirate enthusiasts, regularly sharing curated experiences, tech learning, and growth insights. Currently publishing articles on AI RAG customer service, AI MCP technology, and open-source design. Personal free Knowledge Planet: Awakening New World Programmer.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
