Apache Seata with Spring Cloud Alibaba: Auto-Configuration vs Manual Setup
I spent an entire afternoon debugging why my distributed transactions weren’t propagating across microservices. The error was cryptic:
io.seata.rm.datasource.exec.LockConflictException: Global lock conflict at io.seata.rm.datasource.exec.LockConflictHandler.process(LockConflictHandler.java:44)Turns out, I was mixing Spring Boot and Spring Cloud Alibaba Seata configurations incorrectly. The XID (transaction ID) wasn’t propagating between services because I assumed the Spring Cloud starter would handle everything automatically.
The Problem: XID Not Propagating
I had two microservices communicating via HTTP. Service A started a distributed transaction and called Service B. But Service B couldn’t join the same global transaction because the XID wasn’t being passed along.
@Servicepublic class OrderService {
@Autowired private RestTemplate restTemplate;
@GlobalTransactional public void createOrder(Order order) { // Create order locally orderRepository.save(order);
// Call inventory service restTemplate.postForObject( "http://inventory-service/deduct", order, Void.class ); // XID should propagate here! }}The inventory service was running in a separate transaction context. This caused data inconsistency—orders were created but inventory wasn’t deducted properly.
Spring Boot vs Spring Cloud Alibaba Seata
I initially set up Seata using plain Spring Boot dependencies:
<dependencies> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency></dependencies>This required manual XID propagation for every HTTP client. I had to configure interceptors for RestTemplate, RestClient, and WebClient:
@Configurationpublic class SeataConfig {
@Bean public RestTemplate restTemplate() { RestTemplate template = new RestTemplate(); template.getInterceptors().add(new SeataXidClientInterceptor()); return template; }
@Bean public RestClient restClient() { return RestClient.builder() .requestInterceptor(new SeataXidClientInterceptor()) .build(); }
@Bean public WebClient webClient() { return WebClient.builder() .filter((request, next) -> { String xid = RootContext.getXID(); if (xid != null) { request = ClientRequest.from(request) .header(RootContext.KEY_XID, xid) .build(); } return next.exchange(request); }) .build(); }}Plus, I needed a servlet filter to extract XID from incoming requests:
@Componentpublic class XidPropagationFilter implements Filter {
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; String xid = httpRequest.getHeader(RootContext.KEY_XID);
if (xid != null) { RootContext.bind(xid); }
try { chain.doFilter(request, response); } finally { RootContext.unbind(); } }}This was a lot of boilerplate. I wondered: Is there a simpler way?
Switching to Spring Cloud Alibaba Seata
Then I discovered Spring Cloud Alibaba Seata starter. It promised automatic XID propagation:
<dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2025.0.0.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies></dependencyManagement>
<dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency></dependencies>Note: Version compatibility matters! Spring Cloud Alibaba 2025.0.0.0 is for Spring Boot 3.x, while 2025.1.0.0 targets Spring Boot 4.x.
What’s Actually Automatic?
I switched to Spring Cloud Alibaba Seata and removed my manual configurations. Let me share what I found:
1. Incoming HTTP Requests - AUTOMATIC
The starter automatically registers a filter to extract XID from incoming HTTP requests. No more manual XidPropagationFilter.
// This filter is auto-configured by Spring Cloud Alibaba Seata// @Component// public class XidPropagationFilter implements Filter { ... }2. RestTemplate - AUTOMATIC
RestTemplate beans are automatically enhanced with XID propagation:
@Beanpublic RestTemplate restTemplate() { return new RestTemplate(); // XID propagation handled automatically!}3. RestClient - NOT AUTOMATIC
Here’s where I got surprised. RestClient (introduced in Spring 6.1) is not auto-configured:
@Beanpublic RestClient restClient() { return RestClient.builder() .requestInterceptor(new SeataXidClientInterceptor()) // Still required! .build();}I found this out the hard way when my RestClient calls weren’t joining the global transaction.
4. WebClient - NOT AUTOMATIC
WebClient also requires manual configuration:
@Beanpublic WebClient webClient() { return WebClient.builder() .filter((request, next) -> { String xid = RootContext.getXID(); if (xid != null) { request = ClientRequest.from(request) .header(RootContext.KEY_XID, xid) .build(); } return next.exchange(request); }) .build();}Comparison Table
Here’s a quick reference:
┌─────────────────────────┬────────────────┬─────────────────────────┐│ Component │ Spring Boot │ Spring Cloud Alibaba │├─────────────────────────┼────────────────┼─────────────────────────┤│ Incoming HTTP Requests │ Manual Filter │ Automatic ││ RestTemplate │ Manual │ Automatic ││ RestClient │ Manual │ Manual ││ WebClient │ Manual │ Manual ││ seata.conf │ Required │ Required ││ application.properties │ Required │ Required ││ undo_log Table │ Required │ Required │└─────────────────────────┴────────────────┴─────────────────────────┘What’s Still Manual?
Even with Spring Cloud Alibaba, these configurations are still required:
1. seata.conf Configuration
transport { type = "TCP" server = "NIO" heartbeat = true}
service { vgroup_mapping.my_tx_group = "default" default.grouplist = "127.0.0.1:8091" enableDegrade = false disableGlobalTransaction = false}
client { rm.asyncCommitBufferLimit = 10000 rm.reportRetryCount = 5 rm.reportSuccessEnable = false rm.sagaBranchRegisterEnable = false rm.sqlParserType = druid tm.commitRetryCount = 5 tm.rollbackRetryCount = 5 tm.defaultGlobalTransactionTimeout = 60000}2. application.properties
seata.enabled=trueseata.application-id=my-serviceseata.tx-service-group=my_tx_groupseata.service.vgroup-mapping.my_tx_group=defaultseata.service.grouplist.default=127.0.0.1:8091seata.config.type=fileseata.registry.type=file3. undo_log Table
Each database still needs the undo_log table:
CREATE TABLE IF NOT EXISTS `undo_log` ( `branch_id` BIGINT NOT NULL COMMENT 'branch transaction id', `xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id', `context` VARCHAR(128) NOT NULL COMMENT 'undo_log context', `rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info', `log_status` INT NOT NULL COMMENT '0:normal status,1:defense status', `log_created` DATETIME(6) NOT NULL COMMENT 'create datetime', `log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime', UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT = 'AT transaction mode undo log';Version Compatibility Gotcha
I hit this error when I used incompatible versions:
java.lang.NoSuchMethodError: 'void org.springframework.boot.SpringApplication.setRegisterShutdownHook(boolean)' at com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration.onApplicationEvent(NacosAutoServiceRegistration.java:78)The fix was to align versions correctly:
┌─────────────────────────────────┬───────────────────┐│ Spring Cloud Alibaba Version │ Spring Boot │├─────────────────────────────────┼───────────────────┤│ 2025.1.0.0 │ 4.x ││ 2025.0.0.0 │ 3.x ││ 2023.0.1.0 │ 3.x (older) ││ 2022.0.0.0 │ 2.7.x │└─────────────────────────────────┴───────────────────┘Which Should You Choose?
Choose Spring Cloud Alibaba Seata When:
- You’re already in the Spring Cloud ecosystem
- You’re using RestTemplate primarily
- You want less boilerplate for incoming requests
- You need Feign integration (also auto-configured)
Choose Spring Boot Seata When:
- You want simpler dependency management
- You’re using RestClient or WebClient exclusively
- You need fine-grained control over XID propagation
- You’re not using other Spring Cloud components
My Final Setup
I ended up with a hybrid approach since I use both RestTemplate and RestClient:
<dependencies> <!-- Spring Cloud Alibaba Seata for auto-config --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency></dependencies>@Configurationpublic class HttpClientConfig {
// RestTemplate - auto-configured by Spring Cloud Alibaba @Bean public RestTemplate restTemplate() { return new RestTemplate(); }
// RestClient - still needs manual interceptor @Bean public RestClient restClient(RestTemplate restTemplate) { return RestClient.builder() .requestFactory(restTemplate.getRequestFactory()) .requestInterceptor(new SeataXidClientInterceptor()) .build(); }}This gives me the best of both worlds: automatic XID propagation for incoming requests and RestTemplate, while still supporting RestClient with minimal boilerplate.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments