基于 MyBatis + Spring Boot 的完整 IP 日志记录链路,包括:
✅ 项目结构
└── com.example.iplog
├── config/ ← Web 拦截器与线程池配置
├── controller/ ← 可选:用于测试
├── interceptor/ ← IP 日志拦截器
├── mapper/ ← MyBatis Mapper 接口
├── model/ ← 实体类
├── service/impl/ ← Service 实现
└── util/ ← IP 提取工具
1️⃣ 数据库建表 SQL
CREATE TABLE sys_ip_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
username VARCHAR(64) NOT NULL COMMENT '用户名',
ip VARCHAR(45) NOT NULL COMMENT '客户端IP',
request_url VARCHAR(255) NOT NULL COMMENT '请求地址',
method VARCHAR(10) DEFAULT NULL COMMENT '请求方式',
user_agent VARCHAR(255) DEFAULT NULL COMMENT '用户代理',
trace_id VARCHAR(64) DEFAULT NULL COMMENT '链路追踪ID',
status_code INT DEFAULT NULL COMMENT '响应状态码',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '记录时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='IP访问日志';
2️⃣ 实体类 SysIpLog
@Data
public class SysIpLog {
private Long id;
private String username;
private String ip;
private String requestUrl;
private String method;
private String userAgent;
private String traceId;
private Integer statusCode;
private LocalDateTime createTime;
}
3️⃣ Mapper 接口与 XML
接口:SysIpLogMapper.java
@Mapper
public interface SysIpLogMapper {
void insert(SysIpLog log);
}
XML 映射:SysIpLogMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.iplog.mapper.SysIpLogMapper">
<insert id="insert" parameterType="com.example.iplog.model.SysIpLog">
INSERT INTO sys_ip_log (
username, ip, request_url, method,
user_agent, trace_id, status_code, create_time
) VALUES (
#{username}, #{ip}, #{requestUrl}, #{method},
#{userAgent}, #{traceId}, #{statusCode}, now()
)
</insert>
</mapper>
4️⃣ IP 提取工具类 IpUtil
public class IpUtil {
public static String getClientIp(HttpServletRequest request) {
String[] headers = {
"X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP",
"HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"
};
for (String header : headers) {
String ip = request.getHeader(header);
if (StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip.split(",")[0].trim();
}
}
return request.getRemoteAddr();
}
}
5️⃣ Service 接口 & 实现
接口:IpLogService
public interface IpLogService {
void saveAsync(SysIpLog log);
}
实现:IpLogServiceImpl
@Service
public class IpLogServiceImpl implements IpLogService {
@Autowired
private SysIpLogMapper sysIpLogMapper;
@Async("logExecutor")
@Override
public void saveAsync(SysIpLog log) {
sysIpLogMapper.insert(log);
}
}
6️⃣ 拦截器:IpLogInterceptor
@Component
public class IpLogInterceptor implements HandlerInterceptor {
@Autowired
private IpLogService ipLogService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String ip = IpUtil.getClientIp(request);
String url = request.getRequestURI();
String method = request.getMethod();
String userAgent = request.getHeader("User-Agent");
String traceId = request.getHeader("X-Trace-Id");
String username = request.getUserPrincipal() != null
? request.getUserPrincipal().getName() : "anonymous";
SysIpLog log = new SysIpLog();
log.setIp(ip);
log.setRequestUrl(url);
log.setMethod(method);
log.setUserAgent(userAgent);
log.setTraceId(traceId);
log.setUsername(username);
ipLogService.saveAsync(log);
return true;
}
}
7️⃣ 拦截器注册配置:WebMvcConfig
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private IpLogInterceptor ipLogInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(ipLogInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/static/**", "/actuator/**");
}
}
8️⃣ 异步线程池配置:AsyncConfig
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean("logExecutor")
public Executor logExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(1000);
executor.setThreadNamePrefix("IpLogAsync-");
executor.initialize();
return executor;
}
}
✅ 测试验证
你可以用一个简单的接口 /hello 访问后,看数据库是否写入成功。
🔁 拓展建议
• 支持 statusCode 记录,可用 Filter 包装响应;
• 支持多租户、TraceId 传递;
• 接入 MQ 或写入 Elasticsearch 进一步增强可观测性。