下集:一条打包到底的静态部署之路

在这里插入图片描述

说完坑,再来讲正经操作。

这次我决定写得明明白白,清清楚楚,把那条“Spring Boot 扛着 Next.js 上线”的路子掰碎了揉细了告诉你。

无废话、无迷信、无AI幻觉。

第一步:Next.js 静态导出

项目根目录下的 next.config.js 要写得很清楚,不要留一丝幻想:

/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'export',           // 重点!导出为纯静态
  trailingSlash: true,       // 每个路径带斜杠,方便生成 index.html
  images: {
    unoptimized: true        // 否则图片优化会报错
  },
};

module.exports = nextConfig;

然后直接命令:

npm run build
npx next export

这时候,会生成一个 out/ 文件夹,里面是你真正能用来部署的东西。

结构大概长这样:

out/
├── admin/
│   └── login/
│       └── index.html
├── register/
│   └── index.html
├── index.html
├── _next/static/chunks/...js
└── ...

注意,每个页面其实就是一个路径+index.html 文件结构,所以根本不用搞什么“SPA跳转”那一套逻辑!

第二步:复制到Spring Boot资源目录

假设你后端目录结构是标准的:

backend/
└── src/
    └── main/
        └── resources/
            └── static/

那么执行这条命令:

cp -r out/* ../backend/src/main/resources/static/

清空 static 目录再复制比较稳妥。

第三步:配置 Security —— 非 /api/** 一律放行

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .cors(cors -> cors.configurationSource(corsConfigurationSource()))
        .csrf(AbstractHttpConfigurer::disable)
        .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/admin/**").hasRole("ADMIN")
            .requestMatchers("/api/**").authenticated()
            .anyRequest().permitAll()
        )
        .authenticationProvider(authenticationProvider())
        .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

    return http.build();
}

千万不要用 .anyRequest().authenticated(),那是开发环境用来封杀一切的,不适合你现在这个"一切靠静态"的部署场景。

第四步:配置 WebConfig —— 精准转发到 index.html

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
            .addResourceLocations("classpath:/static/")
            .resourceChain(true)
            .addResolver(new PathResourceResolver() {
                @Override
                protected Resource getResource(String resourcePath, Resource location) throws IOException {
                    Resource requested = location.createRelative(resourcePath);

                    if (requested.exists() && requested.isReadable()) {
                        return requested;
                    }

                    // 对于非静态资源请求,尝试回退到 index.html
                    if (resourcePath.contains(".")) {
                        return null; // 明确要请求静态资源的,找不到就报404
                    }
                    if (resourcePath.startsWith("api/")) {
                        return null; // 让Controller处理
                    }

                    return new ClassPathResource("/static/index.html");
                }
            });
    }
}

这个配置非常关键。它做到了:

  • 能访问的静态资源直接返回
  • 明确的 API 请求让 controller 去处理
  • 只有“没有扩展名”的页面路径,比如 /admin/login,才回退到 index.html

AI 给你那种“全都 forward 到 index.html” 的 Controller 方案,在这个结构下是没必要、也极其危险的。会白屏、跳错页,还极难调试。

第五步:打包运行!

用 Maven 构建:

mvn clean package -DskipTests

运行:

java -jar target/backend-0.0.1-SNAPSHOT.jar

然后你可以打开:http://localhost:8082/

  • / 首页会显示出来
  • /admin/login 会跳转到你静态导出的 admin/login/index.html
  • /register/?token=abc123 会正确加载 register 页并保留查询参数
  • /api/** 接口仍然走后端 Controller,毫无冲突

最终效果:

你得到了一个:

  • 单一jar包即可部署
  • 不需要Node环境
  • 支持多页访问和刷新
  • 前后端分工明确但部署合体
  • 操作简明、老板无感知的系统

结语:这条路走通了,我回头一看

  • AI教的套路,不一定错,但常常不适合你这种“野路子结合小团队现状”的打法
  • 静态部署这条线,只要搞清楚目录结构和路径映射,真的比你想的简单
  • Spring Boot 其实是个好平台,只要你别逼它理解前端的世界

这次,我们用文件系统打败了抽象逻辑。

前端静态导出 + 后端反向代理 + 安全放行策略,这三板斧下去,小项目不管你多么前后端分离,最终都能“一锅端”。

别再迷信什么服务拆分了,小项目,能合就合。

回头再看那些鼓吹“必须前后端分离”的架构课件……我只想笑着对他们说一句:

“你把前端扔进static再说这话。”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十步杀一人_千里不留行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值