You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot 3.0.2:能否用HttpExchangeRepository持久化请求到关系型数据库?

可以实现HttpExchange的关系型数据库持久化

完全可以通过自定义HttpExchangeRepository的实现类,替代默认的InMemoryHttpExchangeRepository,将HttpExchange对象持久化到MySQL、PostgreSQL等关系型数据库,下面分别给出Spring Data JPA和Spring JDBC两种实现方案,适配Spring Boot 3.0.2版本。

方案一:Spring Data JPA实现

1. 定义实体类与数据库映射

创建对应数据库表的实体类,利用JSON类型存储请求/响应头(适配MySQL 8+、PostgreSQL的JSON支持):

import jakarta.persistence.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.actuate.web.exchanges.HttpExchange;
import org.springframework.boot.actuate.web.exchanges.HttpRequest;
import org.springframework.boot.actuate.web.exchanges.HttpResponse;
import java.time.Instant;
import java.util.Map;

@Entity
@Table(name = "http_traces")
public class HttpTraceEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Instant timestamp;

    private Long duration;

    private String requestMethod;

    private String requestUri;

    private String requestRemoteAddress;

    @Column(columnDefinition = "JSON")
    private String requestHeaders;

    private Integer responseStatusCode;

    @Column(columnDefinition = "JSON")
    private String responseHeaders;

    // 构造方法、Getter、Setter省略

    // 从HttpExchange转换为实体
    public static HttpTraceEntity fromHttpExchange(HttpExchange exchange) {
        HttpTraceEntity entity = new HttpTraceEntity();
        ObjectMapper objectMapper = new ObjectMapper();

        entity.setTimestamp(exchange.getTimestamp());
        entity.setDuration(exchange.getDuration().toMillis());

        HttpRequest request = exchange.getRequest();
        entity.setRequestMethod(request.getMethod());
        entity.setRequestUri(request.getUri().toString());
        entity.setRequestRemoteAddress(request.getRemoteAddress());
        try {
            entity.setRequestHeaders(objectMapper.writeValueAsString(request.getHeaders()));
        } catch (JsonProcessingException e) {
            entity.setRequestHeaders("{}");
        }

        HttpResponse response = exchange.getResponse();
        entity.setResponseStatusCode(response.getStatusCode());
        try {
            entity.setResponseHeaders(objectMapper.writeValueAsString(response.getHeaders()));
        } catch (JsonProcessingException e) {
            entity.setResponseHeaders("{}");
        }

        return entity;
    }

    // 从实体转换为HttpExchange
    public HttpExchange toHttpExchange() {
        ObjectMapper objectMapper = new ObjectMapper();
        HttpRequest request = HttpRequest.builder()
                .method(requestMethod)
                .uri(java.net.URI.create(requestUri))
                .remoteAddress(requestRemoteAddress)
                .headers(objectMapper.convertValue(requestHeaders, Map.class))
                .build();

        HttpResponse response = HttpResponse.builder()
                .statusCode(responseStatusCode)
                .headers(objectMapper.convertValue(responseHeaders, Map.class))
                .build();

        return new HttpExchange(timestamp, java.time.Duration.ofMillis(duration), request, response);
    }
}

2. 创建JPA Repository接口

import org.springframework.data.jpa.repository.JpaRepository;

public interface JpaHttpTraceRepository extends JpaRepository<HttpTraceEntity, Long> {
}

3. 自定义HttpExchangeRepository实现

import org.springframework.boot.actuate.web.exchanges.HttpExchange;
import org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository;
import org.springframework.data.domain.Sort;
import java.util.List;
import java.util.stream.Collectors;

public class JpaHttpExchangeRepositoryImpl implements HttpExchangeRepository {

    private final JpaHttpTraceRepository jpaRepository;

    public JpaHttpExchangeRepositoryImpl(JpaHttpTraceRepository jpaRepository) {
        this.jpaRepository = jpaRepository;
    }

    @Override
    public void add(HttpExchange exchange) {
        HttpTraceEntity entity = HttpTraceEntity.fromHttpExchange(exchange);
        jpaRepository.save(entity);
    }

    @Override
    public List<HttpExchange> findAll() {
        return jpaRepository.findAll(Sort.by(Sort.Direction.DESC, "timestamp"))
                .stream()
                .map(HttpTraceEntity::toHttpExchange)
                .collect(Collectors.toList());
    }
}

4. 配置Bean替换默认实现

import org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ActuatorConfiguration {

    @Bean
    public HttpExchangeRepository httpExchangeRepository(JpaHttpTraceRepository jpaHttpTraceRepository) {
        return new JpaHttpExchangeRepositoryImpl(jpaHttpTraceRepository);
    }
}

方案二:Spring JDBC实现

1. 创建数据库表

执行SQL脚本创建表(以MySQL为例):

CREATE TABLE http_traces (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    timestamp DATETIME(6) NOT NULL,
    duration BIGINT NOT NULL,
    request_method VARCHAR(10) NOT NULL,
    request_uri VARCHAR(2048) NOT NULL,
    request_remote_address VARCHAR(100),
    request_headers JSON NOT NULL,
    response_status_code INT,
    response_headers JSON NOT NULL
);

2. 自定义HttpExchangeRepository实现

import org.springframework.boot.actuate.web.exchanges.HttpExchange;
import org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository;
import org.springframework.boot.actuate.web.exchanges.HttpRequest;
import org.springframework.boot.actuate.web.exchanges.HttpResponse;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Instant;
import java.util.List;
import java.util.Map;

public class JdbcHttpExchangeRepositoryImpl implements HttpExchangeRepository {

    private final JdbcTemplate jdbcTemplate;
    private final ObjectMapper objectMapper;

    private static final String INSERT_SQL = """
            INSERT INTO http_traces (timestamp, duration, request_method, request_uri, request_remote_address, request_headers, response_status_code, response_headers)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?)
            """;

    private static final String SELECT_ALL_SQL = """
            SELECT * FROM http_traces ORDER BY timestamp DESC
            """;

    public JdbcHttpExchangeRepositoryImpl(JdbcTemplate jdbcTemplate, ObjectMapper objectMapper) {
        this.jdbcTemplate = jdbcTemplate;
        this.objectMapper = objectMapper;
    }

    @Override
    public void add(HttpExchange exchange) {
        HttpRequest request = exchange.getRequest();
        HttpResponse response = exchange.getResponse();

        String requestHeadersJson;
        String responseHeadersJson;
        try {
            requestHeadersJson = objectMapper.writeValueAsString(request.getHeaders());
            responseHeadersJson = objectMapper.writeValueAsString(response.getHeaders());
        } catch (JsonProcessingException e) {
            requestHeadersJson = "{}";
            responseHeadersJson = "{}";
        }

        jdbcTemplate.update(INSERT_SQL,
                exchange.getTimestamp(),
                exchange.getDuration().toMillis(),
                request.getMethod(),
                request.getUri().toString(),
                request.getRemoteAddress(),
                requestHeadersJson,
                response.getStatusCode(),
                responseHeadersJson
        );
    }

    @Override
    public List<HttpExchange> findAll() {
        return jdbcTemplate.query(SELECT_ALL_SQL, new HttpTraceRowMapper());
    }

    private class HttpTraceRowMapper implements RowMapper<HttpExchange> {
        @Override
        public HttpExchange mapRow(ResultSet rs, int rowNum) throws SQLException {
            Instant timestamp = rs.getTimestamp("timestamp").toInstant();
            long duration = rs.getLong("duration");

            HttpRequest request = HttpRequest.builder()
                    .method(rs.getString("request_method"))
                    .uri(java.net.URI.create(rs.getString("request_uri")))
                    .remoteAddress(rs.getString("request_remote_address"))
                    .headers(objectMapper.convertValue(rs.getString("request_headers"), Map.class))
                    .build();

            HttpResponse response = HttpResponse.builder()
                    .statusCode(rs.getInt("response_status_code"))
                    .headers(objectMapper.convertValue(rs.getString("response_headers"), Map.class))
                    .build();

            return new HttpExchange(timestamp, java.time.Duration.ofMillis(duration), request, response);
        }
    }
}

3. 配置Bean替换默认实现

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

@Configuration
public class ActuatorConfiguration {

    @Bean
    public HttpExchangeRepository httpExchangeRepository(JdbcTemplate jdbcTemplate, ObjectMapper objectMapper) {
        return new JdbcHttpExchangeRepositoryImpl(jdbcTemplate, objectMapper);
    }
}

注意事项

  • 确保数据库支持JSON类型(MySQL 8.0+、PostgreSQL 9.2+均支持),如果使用低版本数据库,可将headers字段改为TEXT类型,序列化/反序列化逻辑保持不变。
  • 若需要限制存储的追踪记录数量,可在add方法中添加清理旧数据的逻辑(比如保留最近N条记录)。
  • 生产环境建议为追踪表添加索引(比如timestamp字段),提升查询性能。

内容的提问来源于stack exchange,提问作者Ermolai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 20:20:55