
干了三四年Java后端开发被问得最多的一句话就是Spring Boot怎么连前端。很多刚入行的朋友拿着Spring Boot教程看了一堆Bean、自动装配、起步依赖说得头头是道真到要跟前端联调的时候一脸懵——接口写了前端请求打过来不是404就是跨域报错能急出一身汗。先说一个可能让不少人意外的事实Spring Boot压根儿就不管怎么连前端这件事它管的是怎么把后端能力暴露成前端能调用的接口。所谓前后端连接本质上就是一次HTTP请求从浏览器或者App发出去打到Spring Boot启动的内嵌Tomcat上经过层层处理最终由Controller里的某个方法接住然后把结果以JSON的形式原路返回。中间涉及接口设计、数据格式约定、跨域策略、参数传递任何一个环节出了岔子前端那边看到的就是一片红。这篇文章我打算把Spring Boot前后端连接这件事从头到尾拆一遍从最基础的HTTP交互讲起到接口怎么设计、数据怎么对齐、跨域怎么处理、后端怎么主动调外部接口再到Spring Boot自动装配的原理最后附一份我这些年联调踩坑的排查清单。适合正在做毕设的学生、刚入职的后端新人以及那些把Spring Boot跑起来了但始终没搞明白前端到底怎么找到后端的朋友。内容偏实战每段代码都能直接抄。1. 内容整体设计与思路拆解1.1 先纠正一个认知偏差Spring Boot语言其实是个框架标题里写的是使用springboot语言这里得较个真Spring Boot不是语言它是Java生态里的一个开发框架。Java才是语言Spring Boot是Java世界里最主流的后端快速开发脚手架。你写出来的代码依然是Java代码只是借助Spring Boot帮你把大量重复的配置、依赖管理、组件装配给自动化了。打个比方Java好比是炒菜的锅和灶台Spring Boot相当于一份预制菜。菜还是你自己炒但配菜、调料、火候指南都给你准备好了你只管按着说明下锅。放到项目里Spring Boot通过起步依赖Starter把常用的库比如Web、数据库、消息队列整合进来通过自动装配AutoConfiguration根据classpath下的依赖自动帮你创建好一堆默认的Bean通过约定大于配置让你不用写一堆XML配置就能把项目跑起来。所以后面我们聊的所有前后端连接问题本质都是在讲Spring Boot这个脚手架启动后如何对外提供HTTP服务以及前端如何与这个HTTP服务进行数据交换。1.2 前后端分离架构下的连接到底连接的是什么现在的主流项目基本都走前后端分离架构前端可能是一个Vue应用、React应用、微信小程序或者干脆是App里的WebView后端就是我们要写的Spring Boot应用。两者之间隔着一条网络而连接就是这条网络上的HTTP请求与响应。很多新人误解前后端连接是需要什么特殊技术或神秘配置其实拆开来看就三个核心问题请求怎么发出去前端通过axios、fetch、XMLHttpRequest这些工具发HTTP请求带上URL、请求方式GET/POST/PUT/DELETE、请求头、请求体。请求怎么接得住Spring Boot的DispatcherServlet接收请求根据URL和方法找到对应的Controller方法然后把请求参数解析成Java对象传给方法体。结果怎么回得去Controller方法执行完返回一个Java对象Spring Boot通过HttpMessageConverter把对象序列化成JSON字符串写进HTTP响应体前端拿到后解析渲染。这三个问题一通前后端就连上了。但实际项目里接口风格乱、字段对不上、跨域被拦、日期格式解析失败、Long精度丢失这些问题会让整个连接过程处处是坑。我们接下来逐个拆解。2. 核心细节解析与实操要点2.1 认识Spring MVC的请求处理链路Spring Boot的前后端连接能力底层由Spring MVC模块提供。一次请求的完整链路大致是这样浏览器或App发起HTTP请求打到Spring Boot内嵌的Tomcat端口默认8080。Tomcat把请求交给Servlet容器中的DispatcherServlet前端控制器。DispatcherServlet通过HandlerMapping找到匹配的Controller方法处理器。通过HandlerAdapter调用该方法执行前会完成参数绑定把请求参数转成方法入参。Controller方法执行完毕返回一个对象通常配合ResponseBody或RestController。交给HttpMessageConverter将对象序列化为JSON写进响应返回给前端。你不需要把这个过程的每一环都背下来但必须理解Spring Boot做前后端连接的所有魔法都围绕这个链路展开。后端联调时遇到参数没接住返回格式不对这类问题排查思路就是沿着这条链路逐段检查——URL是否匹配、请求方式是否一致、参数能不能转换、返回值序列化有没有报错。2.2 接口设计规范前后端要先签合同写接口代码之前一定要先定接口规范。前后端连接能不能顺利60%的问题出在接口设计阶段而不是编码阶段。我强烈建议你在团队里或毕设文档里先约定这四件事URL命名规则用名词复数表示资源比如/api/users、/api/orders不要用动词除了login/logout这类动作型接口不要出现大小写混用。请求方式语义GET查、POST增、PUT/PATCH改、DELETE删。别图省事全用POST前端和后端都会乱。统一返回体所有接口返回同样的JSON结构比如{code: 0, message: success, data: {...}}前端写公共拦截器不用为每个接口单独处理。错误码约定0代表成功非0代表失败错误码要有语义比如1001参数错误、1002未授权、1003资源不存在前端根据code决定提示还是跳转。接口契约一旦定好前后端就可以并行开发前端用Mock数据后端等接口逻辑写完直接联调。如果你是自己做全栈项目这套规范同样适用能省掉后面大量的自我拉扯。2.3 Controller层的核心注解与用法Controller是前端请求直接落地的位置也是最需要写明白的地方。先把最常用的一组注解说清楚我直接用代码演示RestController RequestMapping(/api/users) public class UserController { Autowired private UserService userService; // GET /api/users/1 路径参数 GetMapping(/{id}) public ResultUser getUserById(PathVariable(id) Long id) { return Result.success(userService.getById(id)); } // GET /api/users?page1size10 查询参数 GetMapping public ResultPageResultUser listUsers(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size) { return Result.success(userService.page(page, size)); } // POST /api/users 前端传JSON Body PostMapping public ResultLong createUser(RequestBody User user) { userService.save(user); return Result.success(user.getId()); } // PUT /api/users/1 更新 PutMapping(/{id}) public ResultBoolean updateUser(PathVariable(id) Long id, RequestBody User user) { user.setId(id); return Result.success(userService.updateById(user)); } // DELETE /api/users/1 删除 DeleteMapping(/{id}) public ResultBoolean deleteUser(PathVariable(id) Long id) { return Result.success(userService.removeById(id)); } }这段代码覆盖了前后端连接中最常见的五类操作。几个关键点RestController相当于是Controller加ResponseBody的组合所有方法的返回值直接序列化成JSON写进响应体而不是走视图解析。做前后端分离项目就认准它别再写Controller然后到处加ResponseBody。PathVariable取的是URL路径里的变量比如/api/users/1里的1。RequestParam取的是URL问号后面的参数比如?page1size10里的page和size还可以设置defaultValue避免前端漏传导致NPE。RequestBody取的是请求体里的JSON字符串Spring Boot会帮你反序列化成Java对象。注意POST和PUT这类带Body的方法才用得上GET一般不用。我见过不少毕设项目前端用axios传JSON后端却用RequestParam去接结果前端传一个{name: 张三}后端收到的是null或直接400。这就是典型的注解用错位置。2.4 参数接收的四种场景与踩坑清单前后端参数对接归纳起来就四种常见场景我做成了一张表方便你对照自查场景前端发请求的方式后端接收方式常见坑查询单个资源GET /api/users/1PathVariable Long id前端拼URL漏了参数类型不匹配比如传了字符串abc列表查询分页/过滤GET /api/users?page1size10name张RequestParam配基本类型或POJO前端参数名与后端字段名不一致page传成字符串Spring无法转Integer新增/修改资源POST/PUT /api/usersBody传JSONRequestBody User userJSON字段名与Java字段名对不上日期格式不对多余字段导致反序列化失败提交表单非JSONPOST /api/loginContent-Type为form-data或x-www-form-urlencodedRequestParam或直接用一个POJO接前端没有正确设置Content-Type字段与POJO属性不匹配这里我必须重点说一个非常隐蔽的问题RequestBody只能有一个。有朋友写过public Result create(RequestBody User user, RequestBody Order order)直接启动失败。正确的做法是包一层比如RequestBody CreateOrderRequest request里面同时放User user和Order order两个字段。这个细节在面试里也算是个小高频题记住能加分。3. 实操过程与核心环节实现3.1 快速搭建一个可联调的Spring Boot项目这部分我给一个从零到能跑通前后端连接的最小可复现方案。用IDEA新建项目时选Spring InitializrJava版本建议8或11如果你是Spring Boot 2.x如果选Spring Boot 3.x就配Java 17以上。关键依赖只需要一个spring-boot-starter-web这个starter会拉进Spring MVC、内嵌Tomcat、JacksonJSON序列化工具等一整套Web开发所需的库前后端连接的基本能力全在里面。项目建好后目录结构长这样src/main/java/com/example/demo ├── DemoApplication.java ├── controller │ └── HelloController.java ├── entity │ └── User.java └── result └── Result.javaDemoApplication.java是启动类上面有个SpringBootApplication注解里面包括三个核心注解后面专门讲原理。写一个最简单的接口RestController public class HelloController { GetMapping(/hello) public ResultString hello(RequestParam(value name, defaultValue World) String name) { return Result.success(Hello, name); } }启动项目后浏览器直接访问http://localhost:8080/hello?nameSpringBoot就能看到返回的JSON。到这里一个最基础的前后端连接已经通了——前端发请求后端接请求再返还数据。3.2 统一返回体的设计与实现不夸张地说统一返回体是前后端连接里性价比最高的一个设计。前端拿到接口返回不用判断这次是数组还是对象还是字符串只要先看code再看data就完事。直接给一个可以直接用的类public class ResultT { private Integer code; // 状态码0成功非0失败 private String message; // 提示信息 private T data; // 具体数据 public static T ResultT success(T data) { ResultT result new Result(); result.code 0; result.message success; result.data data; return result; } public static T ResultT error(Integer code, String message) { ResultT result new Result(); result.code code; result.message message; return result; } // getter/setter 省略 }有朋友问为什么不直接用Map塞数据能用但有几个问题没有统一的字段命名约束每个人写的key都不一样类型不安全前端取数据全靠猜将来要加字段比如链路追踪ID得改所有接口。用泛型类把结构固定下来前后端沟通成本会低很多。还有个小技巧如果字段不需要返回给前端比如密码、内部标志位在实体类属性上加JsonIgnore或统一在序列化层处理。千万别说懒就不管很多前端看到了不该看的数据的事故就是这么来的。3.3 JSON数据交换日期格式、字段映射与精度问题前后端连接用到JSON的地方最多也最容易出幺蛾子。讲三个高频问题。日期格式。Java侧的LocalDateTime默认序列化出来是一串数组或者带T的ISO字符串前端日期组件经常解析不了或者显示得很难看。最简单的方案在application.yml里全局配置spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8如果前端只要日期不要时间可以在实体字段上加注解细化JsonFormat(pattern yyyy-MM-dd, timezone GMT8) private LocalDate birthday;字段映射。前端喜欢用userIdJava代码里字段是userId这没问题。但前端如果传的是user_id、id或者uid反序列化就会得到null。可以用JsonProperty把Java字段名和JSON字段名做映射JsonProperty(value user_id) private Long userId;这里提醒一句命名规范能统一就统一JsonProperty是兜底手段不是偷懒借口。真正要把字段对齐还是靠接口文档定义好每个字段的名字和类型前后端都按文档写。Long精度丢失。这个坑不遇到一次真的不知道有多疼。前端JavaScript的Number类型能安全表示的最大整数是2^53 - 1而Java的Long最大能到2^63 - 1。当id字段超过JavaScript的安全整数范围雪花ID很容易超前端拿到的数字会失真比如后端的18237189273192713前端解析完可能变成18237189273192710。解决方法给Long类型的字段统一加注解序列化为字符串JsonSerialize(using ToStringSerializer.class) private Long id;或者在配置里全局处理把Long都转String。这个细节在联调时特别容易忽略莫名其妙前端拿到的id和数据库对不上最后查一圈发现是精度问题。3.4 后端如何主动调用第三方接口前后端连接不只是前端调后端后端系统之间也会互相调。Spring Boot提供了RestTemplate这个HTTP客户端工具虽然在Spring Boot 3.x里官方更推荐WebClient但RestTemplate依然是中小项目里最简单直观的方案。一个封装好的样例Component public class HttpUtil { private final RestTemplate restTemplate new RestTemplate(); public String get(String url) { return restTemplate.getForObject(url, String.class); } public T T get(String url, ClassT responseType) { return restTemplate.getForObject(url, responseType); } public T T post(String url, Object requestBody, ClassT responseType) { HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntityObject entity new HttpEntity(requestBody, headers); return restTemplate.postForObject(url, entity, responseType); } }用的时候直接注入HttpUtil。需要带token的话就在headers里加上Authorization头。需要注意RestTemplate默认使用的SimpleClientHttpRequestFactory在Spring Boot 3.x里可能遇到连接池/超时配置不生效的问题建议显式替换为HttpComponentsClientHttpRequestFactory并设置连接超时和读取超时Bean public RestTemplate restTemplate() { SimpleClientHttpRequestFactory factory new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(3000); factory.setReadTimeout(5000); return new RestTemplate(factory); }这个超时设置特别重要不设的话一旦第三方接口卡住你的线程会一直阻塞高并发场景下很快就拖垮应用。3.5 文件上传下载场景的前后端协作前后端连接还有一个高频场景是文件上传下载。文件上传不能直接走JSON序列化要用multipart/form-data格式。Spring Boot里接收很简单PostMapping(/upload) public ResultString uploadFile(RequestParam(file) MultipartFile file) { String originalFilename file.getOriginalFilename(); // 存储逻辑本地磁盘、OSS、MinIO等 return Result.success(fileUrl); }前端用表单或axios的FormData方式提交注意字段名必须叫file和后端RequestParam(file)对上。如果上传时遇到MaxUploadSizeExceededException多半是配置没放开在application.yml里调整spring: servlet: multipart: max-file-size: 50MB max-request-size: 100MB下载文件的接口返回类型要写成ResponseEntitybyte[]或者ResponseEntityResource同时设置Content-Disposition响应头告诉浏览器这是一个附件否则图片之类的会直接在浏览器里打开而不是下载GetMapping(/download/{fileName}) public ResponseEntitybyte[] download(PathVariable String fileName) { byte[] data fileService.getFileBytes(fileName); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, attachment; filename\ fileName \) .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(data); }4. 常见问题与排查技巧实录4.1 跨域问题为什么前端请求总是被拦前后端分离联调跨域是绕不开的第一座山。简单说浏览器的同源策略规定协议、域名、端口任何一项不同就属于跨域默认会拦截响应。典型场景前端跑在http://localhost:5173后端跑在http://localhost:8080端口不同浏览器就会拦截。Spring Boot解决跨域有三种常用姿势姿势一单个接口加注解适合接口少或临时联调CrossOrigin(origins http://localhost:5173) GetMapping(/hello) public ResultString hello() { return Result.success(ok); }姿势二全局配置类适合大多数项目Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }姿势三CorsFilter过滤器适合需要精细控制或者Spring Security等场景。三种方式的本质一样都是往响应头里加Access-Control-Allow-*相关的头。allowedOriginPatterns(*)配合allowCredentials(true)时不能写*要写成具体域名或用allowedOriginPatterns这个坑尤其容易踩写错了一个前端直接跪。还有一个需要说明的点当前端发复杂请求带自定义Header、Content-Type为application/json时浏览器会先发一个OPTIONS预检请求。如果在配置里没有放行OPTIONS前端就会看到CORS error但后端日志里明明有请求进来。处理办法很简单把OPTIONS加进allowedMethods即可。4.2 联调高频报错速查表下面这张表是我这些年联调实战中的高频踩坑总结几乎每个项目都会遇到其中两三条现象可能原因排查方向404 Not FoundURL路径不对Controller没加RestController没启动成功核对接口路径看启动日志确认Controller类被扫描到405 Method Not Allowed请求方式与Mapping不一致比如前端POST但后端只写了GetMapping检查前端的method和后端的Mapping注解400 Bad Request参数类型无法转换RequestBody的JSON结构不匹配请求头Content-Type不是application/json看后端日志里具体的类型转换异常核对JSON字段名和类型415 Unsupported Media Type前端没有设置Content-Type为application/json前端axios设置headers: {Content-Type: application/json}500 Internal Server Error后端代码运行时异常返回值序列化失败看后端堆栈日志检查实体类是否有getter/setter前端拿到所有字段都是null返回的对象没走getterJSON字段名与前端不一致检查实体类是否添加了JsonIgnore或没有getter方法核对字段命名请求通了但参数收不到注解用错JSON用了RequestParam字段名不一致换成RequestBody核对参数名跨域报错且后端日志没有请求CORS配置缺失或OPTIONS未放行添加全局CorsConfig确认allowedMethods里包含OPTIONS大整数id失真JavaScript安全整数范围超出2^53-1Long字段统一序列化为字符串端口被占用8080被其他进程占了换一个随机端口或用server.port0注意随机端口联调不方便4.3 联调利器统一请求日志过滤器排错最快的方式是让后端把每一次请求的URL、请求方式、参数、耗时全部打出来。Spring Boot自带logback加一个OncePerRequestFilter就能实现。Component public class RequestLogFilter extends OncePerRequestFilter { private static final Logger log LoggerFactory.getLogger(RequestLogFilter.class); Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { long startTime System.currentTimeMillis(); String uri request.getRequestURI(); String method request.getMethod(); String queryString request.getQueryString(); log.info(收到请求: {} {} {}, method, uri, queryString); filterChain.doFilter(request, response); long cost System.currentTimeMillis() - startTime; log.info(请求处理完成: {} {} 耗时{}ms, method, uri, cost); } }这个过滤器在联调时的价值巨大。前端说我请求了但没反应你翻一下日志有没有收到请求、耗时多少、异常堆栈是什么一目了然。我甚至会在日志里把请求体内容包装成一个可重复读取的RequestWrapper再打印这样前端传了什么参数全都能看到排查问题基本不用来回问。4.4 Spring Boot版本太高导致的兼容问题热词里有springboot版本太高确实值得单独提一嘴。Spring Boot 3.x相比2.x有几个重要变化javax.*包改名成jakarta.*大量老代码里的import javax.servlet.*要改成import jakarta.servlet.*。最低要求Java 17如果你还在用Java 8老老实实停在Spring Boot 2.7.x。一些第三方starter比如某些旧版MyBatis、Shiro框架可能还没适配3.x引入后启动报错。如果你是做毕设或者学习用建议直接选Spring Boot 2.7.x搭配Java 8资料最多、踩坑最少、答疑最容易。等把整套流程跑通了再升级3.x也不迟。如果是新项目且团队技术栈跟得上那直接3.x也没问题就是遇到问题要靠搜英文issue。5. Spring Boot自动装配原理为什么能连上5.1 三个关键注解的组合关系聊到Spring Boot免不了要被问自动装配原理面试高频题也是理解为什么我只写了一个Controller请求就能被接住的关键。SpringBootApplication是一个组合注解它把下面三个注解打包在一起SpringBootConfiguration本质是Configuration表示当前类是配置类。EnableAutoConfiguration开启自动装配自动装配的核心入口。ComponentScan扫描当前包及其子包下的Component、Service、Controller、Repository等Bean。我常用一个类比来理解ComponentScan好比是让公司搜罗全公司所有带工牌的员工BeanEnableAutoConfiguration好比是让行政根据仓库里已有的办公用品classpath依赖自动把工位、电脑、网线全部装好SpringBootConfiguration是这个公司的行政部规章规定这些装配行为的管理规范。所以为什么你的Controller能被找到并处理请求因为SpringBootApplication所在类的包路径包含Controller所在的包ComponentScan扫到了它并注册成Bean。如果你把Controller放在启动类所在包的兄弟目录Spring根本扫描不到请求就404。这个问题我见过太多人踩了。5.2 自动装配spring.factories与条件装配EnableAutoConfiguration的奥妙在于它会去读META-INF/spring.factoriesSpring Boot 2.7及以前或META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.importsSpring Boot 3.x里的配置里面列了一长串候选的自动配置类。每一条候选配置类都带了一堆Conditional条件注解最常见的几个ConditionalOnClassclasspath下存在某个类才生效。比如你引入了spring-boot-starter-webclasspath下有了DispatcherServletWebMvcAutoConfiguration才会生效。ConditionalOnMissingBean容器里没有某个Bean时才创建默认的。比如你手动定义了一个数据源自动配置里那个默认的HikariDataSource就不会再创建。ConditionalOnProperty配置了指定属性才生效。这套机制的价值在于按需装配。你引入什么依赖Spring Boot就自动配置对应能力你写了自定义Bean自动配置就退位让贤。前后端连接的一系列组件——内嵌Tomcat、DispatcherServlet、Jackson转换器、跨域处理等等全都是通过这些自动配置在项目启动时被装配出来的。理解这个原理对你排查为什么我配置了没生效这类问题很有帮助。如果你的配置和Spring Boot自动配置冲突通常你的配置优先级更高因为有ConditionalOnMissingBean但如果你想让某个自动配置类不生效也可以在启动类上排除它SpringBootApplication(exclude {DataSourceAutoConfiguration.class}) public class DemoApplication { // 不使用数据源时排除掉自动配置可以避免无意义的报错 }6. 项目落地经验与避坑笔记6.1 接口路径规划与版本管理前后端连接不仅在技术层面也在人的层面。联调最怕的是接口改了没通知前端按旧文档请求后端按新逻辑返回两边鸡同鸭讲。我的做法是接口前缀统一所有接口加/api前缀方便通过Nginx统一转发、统一做权限过滤。接口版本化如果接口的存在会持续演进建议在URL里加版本号比如/api/v1/users。改版了再出/api/v2/users避免动老逻辑连累存量前端。Swagger/OpenAPI文档Spring Boot整合springdocSpring Boot 3.x或springfoxSpring Boot 2.x在页面上可视化调试接口。有了它前端可以直接看到每个接口的参数和返回结构联调效率提高一大截。6.2 从JDBC到Redis常用依赖的整合经验前后端连接往往还牵扯到数据存储。毕设和中小型项目最常见的组合是Spring Boot MyBatis-Plus MySQL Redis。下面几个依赖坐标都是可以直接抄的dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.5/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependencyRedis在前后端连接场景里最常见的用法是存登录token和缓存热点数据。比如登录成功后把用户ID和token写进Redis设置过期时间前端每次请求在请求头里带token后端通过拦截器拿着token去Redis里判断这个人是否登录、权限是什么。这套方案的优点是状态可以共享多实例部署时无需额外调整。实际操作中有一个小提示Redis的StringRedisTemplate和RedisTemplateObject, Object是两套API前者处理字符串最方便后者用JDK序列化容易产生乱码问题。如果不想折腾推荐直接用StringRedisTemplate自己把Java对象手动转JSON字符串再存取出时再手动转回来虽然多两行代码但可控性最高。6.3 部署阶段的前后端连接本地联调通了要考虑部署。前后端分离部署有两种形态同一服务器前端构建出静态文件扔到后端项目的src/main/resources/static下后端一个进程全搞定。这种方案最简单一个Tomcat端口没有跨域问题。分离部署前端静态资源放在Nginx后端Java进程独立运行Nginx做反向代理把/api开头的请求转发给后端。这种方案便于独立扩容生产环境主流做法但需要处理跨域或通过Nginx配置同源代理。Nginx配置示例把前端请求代理到后端server { listen 80; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }用这种配置前端访问http://服务器IP/api/xxxNginx会把请求转发给内网8080端口上的Spring Boot应用浏览器看到的是同源请求跨域问题直接消失。如果后端是多实例还可以在Nginx里加upstream做负载均衡。6.4 学习路径建议从会用到懂原理最后给准备系统学Spring Boot的朋友一条路径参考。第一阶段把项目跑起来会写Controller、会用MyBatis-Plus做CRUD能实现最基本的前后端连接。第二阶段理解Spring MVC的请求链路、参数绑定、拦截器、统一异常处理把接口写规范。第三阶段研究自动装配、Bean的生命周期、条件装配能看懂自动配置源码。第四阶段探索微服务相关组件Spring Cloud、Nacos、OpenFeign等了解Spring Boot在更大规模系统里的定位。这条路径不是线性的但每一阶段的核心能力都围绕同一个主题让Spring Boot成为你稳定可靠的后端能力出口让任何前端都能轻松、安全地调用你的接口。前后端连接这个事说白了没有高深莫测的技术真正的功夫都下在规范、细节和排查能力上。接口契约订好参数捋清楚跨域配明白版本对齐剩下的交给Spring Boot自动配置去处理。你把上面这些琢磨透了再回头看Spring Boot怎么连前端这个问题心里就有数了。