如何使用Spring框架实现Java Web应用的子域名适配功能?
Spring Boot 子域名适配落地实现方案
前置校验(优先排除非代码问题)
- 确认泛域名A解析已生效:本地ping任意子域名,返回IP为你的VPS地址即配置正确
- 确认反向代理(Nginx/Caddy等)已透传
Host请求头,大多数公开方案失效都是因为代理层丢失Host头,Nginx参考配置如下:
location / { proxy_pass http://127.0.0.1:8080; # 替换为Spring应用实际监听端口 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
如果Spring应用直接对外暴露端口、没有用反向代理,可跳过本条校验
核心实现步骤
1. 自定义子域名映射注解
import java.lang.annotation.*; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SubdomainMapping { // 匹配的子域名,填*则适配所有子域名 String value() default "*"; }
2. 实现子域名匹配逻辑
import org.springframework.web.servlet.mvc.condition.RequestCondition; import javax.servlet.http.HttpServletRequest; public class SubdomainRequestCondition implements RequestCondition<SubdomainRequestCondition> { private final String expectedSubdomain; public SubdomainRequestCondition(String expectedSubdomain) { this.expectedSubdomain = expectedSubdomain; } @Override public SubdomainRequestCondition combine(SubdomainRequestCondition other) { // 方法注解优先级高于类注解,直接使用方法维度的配置 return new SubdomainRequestCondition(other.expectedSubdomain); } @Override public SubdomainRequestCondition getMatchingCondition(HttpServletRequest request) { String host = request.getHeader("Host"); if (host == null) return null; // 去除端口后缀 host = host.split(":")[0]; // 替换为你自己的主域名,注意保留前缀的. String mainDomainSuffix = ".yourdomain.com"; if (!host.endsWith(mainDomainSuffix)) { // 主域名访问时不触发子域名匹配逻辑 return null; } // 提取子域名片段 String subdomain = host.substring(0, host.length() - mainDomainSuffix.length()); // 匹配规则:通配符优先级低于精确匹配 if ("*".equals(expectedSubdomain) || expectedSubdomain.equals(subdomain)) { return this; } return null; } @Override public int compareTo(SubdomainRequestCondition other, HttpServletRequest request) { return "*".equals(this.expectedSubdomain) ? 1 : -1; } }
务必将上述代码中mainDomainSuffix的yourdomain.com替换为你实际的自有主域名
3. 注册自定义匹配规则到Spring MVC
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.web.servlet.mvc.condition.RequestCondition; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import java.lang.reflect.Method; public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping { @Override protected RequestCondition<?> getCustomMethodCondition(Method method) { SubdomainMapping annotation = AnnotationUtils.findAnnotation(method, SubdomainMapping.class); return annotation == null ? null : new SubdomainRequestCondition(annotation.value()); } }
4. 替换Spring默认的请求映射处理器
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; @Configuration public class WebConfig extends WebMvcConfigurationSupport { @Override public RequestMappingHandlerMapping requestMappingHandlerMapping() { return new CustomRequestMappingHandlerMapping(); } }
5. 接口使用示例
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class SubdomainTestController { // 匹配admin.yourdomain.com的请求 @GetMapping("/hello") @SubdomainMapping("admin") public String adminAccess() { return "admin子域名访问成功"; } // 匹配所有其他子域名的请求 @GetMapping("/hello") @SubdomainMapping("*") public String commonSubdomainAccess() { return "通用子域名访问成功"; } // 匹配主域名yourdomain.com的请求 @GetMapping("/hello") public String mainDomainAccess() { return "主域名访问成功"; } }
常见问题排查
- 本地测试可修改系统hosts文件,添加
admin.yourdomain.com 127.0.0.1记录,无需部署到VPS即可验证逻辑 - 如果需要适配多级子域名(如a.b.yourdomain.com),调整
SubdomainRequestCondition中的子域名提取逻辑即可 - 若使用Spring Cloud Gateway等网关组件,需在网关层提前透传Host头到下游Spring应用
内容的提问来源于stack exchange,提问作者thebossdev
相关产品推荐
相关产品推荐

