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.

Ubiquitous Tech
Ubiquitous Tech
Ubiquitous Tech
How to Build a GrowingIO User‑Tracking Spring Boot Starter from Scratch

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.GrowingioAutoConfiguration

Step 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

user-trackingSpring Bootjava-sdkauto-configurationconfiguration-propertiesstarterGrowingIO
Ubiquitous Tech
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.