Skip to content

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:

Application Log
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.

ServiceAController.java
@Service
public 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:

pom.xml (Spring Boot Approach)
<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:

Manual XID Propagation (Spring Boot)
@Configuration
public 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:

XidPropagationFilter.java
@Component
public 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:

pom.xml (Spring Cloud Alibaba Approach)
<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.

No Longer Needed!
// 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:

RestTemplateConfig.java
@Bean
public 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:

RestClientConfig.java
@Bean
public 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:

WebClientConfig.java
@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();
}

Comparison Table

Here’s a quick reference:

Auto-Configuration Comparison
┌─────────────────────────┬────────────────┬─────────────────────────┐
│ 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

resources/seata.conf
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

application.properties
seata.enabled=true
seata.application-id=my-service
seata.tx-service-group=my_tx_group
seata.service.vgroup-mapping.my_tx_group=default
seata.service.grouplist.default=127.0.0.1:8091
seata.config.type=file
seata.registry.type=file

3. undo_log Table

Each database still needs the undo_log table:

undo_log.sql
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:

Startup Error
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:

Version Compatibility Matrix
┌─────────────────────────────────┬───────────────────┐
│ 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:

pom.xml
<dependencies>
<!-- Spring Cloud Alibaba Seata for auto-config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
</dependencies>
HttpClientConfig.java
@Configuration
public 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