三年前我接手过一个号称“全栈微服务”的项目。它的pom.xml里躺着二十多个依赖Spring Cloud、Netflix OSS、MyBatis Plus、ShardingSphere……那段时间我每天的工作就是对照文档调整配置偶尔写点业务代码。后来我离开了那个项目也开始写一些自己的小项目。如今我的一个核心服务全靠JDK的内置并发工具和HttpClient完成没有Spring Boot启动器没有ORM甚至没有一行XML配置。复杂框架带给你的不是效率而是另一种形式的负债。坐在屏幕前的我第一次感受到什么叫真正的清爽。从魔法到黑盒再往前推几年我一度是框架的狂热信徒。Spring Boot那种“约定大于配置”的魔法让我着迷——别人写几十行Spring XML我只要一个SpringBootApplication就能启动。项目搭建速度快得惊人新手也能秒上手。我甚至觉得不学框架就是落后于时代。框架用复杂的抽象换走了我对代码的控制权。自动装配的背后是条件注解、Bean扫描、代理增强、上下文刷新每一层都在替我做出隐含决策。等我在某个深夜试着排查一个循环依赖时才意识到这个黑盒要比我预想得庞大得多。我开始产生一种错觉是我在用框架还是框架在用我升级地狱一次版本迭代的痛真正让我动摇的是一次框架升级。Spring Boot从2.1升到2.4本来只想修复一个安全漏洞结果依赖传导冲突、配置属性改名、自动配置类失效接踵而至。我花了两天时间用exclude和exclusion排除各种依赖又在文档里翻到凌晨项目终于能重新启动那感觉不是胜利而是心累。每一次框架升级都像是给一个会动的迷宫重新装修你不知道哪些墙会被打掉。从那以后我每次看到依赖更新提示心里都先颤一下。后来在另一个项目里我甚至遇到官方文档都找不到对应版本的配置只能靠翻Github issue来拼凑答案。那一刻我忽然意识到框架的版本号背后不是版本而是一座不断移动的沙丘。性能迷雾如果你只用本地开发来评估框架永远看不到它的真实代价。一个空的Spring Boot应用启动都要花几秒内存轻松占上三百多兆同样的功能用原生Java写哪怕是最简服务也能控制在几十兆。你可能会说“这点内存无所谓”可当你有几十个微服务每个都叠加一层框架的运行时开销时成本就变成了真金白银。框架的性能问题往往要到生产环境才真正显现。更让人沮丧的是排查性能瓶颈时框架的反射调用、动态代理、AOP拦截会把调用栈拉得又长又乱你根本看不清是谁在磨蹭。我曾经在一个接口上加了几个看似无害的注解结果QPS直接掉了一半。没有人能立刻解释为什么最后发现是某个切面拦截了所有请求还偷偷做了一堆字符串序列化。业务逻辑的湮没我见过一个CRUD接口业务代码总共不到二十行但Controller、Service、Mapper、DTO、VO、Converter、Validation、统一异常处理林林总总加起来有上百个文件和上千行代码。为了支撑这些结构项目引入了Lombok、MapStruct、MyBatis-Plus、Spring Validation……每一个都声称让开发更简单合在一起却让简单变得极其复杂。框架让你以为自己在解决问题实际上只是制造了更多需要解决的框架问题。业务逻辑被层层包裹真正需要改的那一行代码往往藏在五层标记和四个注解之后。你开始怀疑这究竟是开发系统还是在给代码做标本。最讽刺的是当你试图删掉一个多余的依赖时连依赖它的人都不知道为什么要依赖它整个团队陷入一种集体性的技术惯性。隐性知识与团队成本框架还有一个很少被提及的代价隐性知识会不断累积并且随时间贬值。Spring Boot的自动配置方案几个月就可能调整一次AOP的细节在不同版本之间也可能发生微妙变化。你之前掌握的一切很快变成过时的经验。新加入的开发者更是苦不堪言他们必须同时学习业务逻辑、框架机制、以及二者之间纠缠不清的适配关系。框架的优雅是有代价的那个代价就是每一代开发者都要重新学习一份庞大而短暂的语法。原生Java反而没有这种烦恼。JDK的API几十年保持稳定你学会List和Map之后几乎能一直用下去。这种稳定性在漫长的时间面前是极其珍贵的资产。原生Java的朴素之美回归原生Java以后画风突然变了。写一个内部工具我用com.sun.net.httpserver.HttpServer起个接口三五十行代码搞定处理并发用CompletableFuture和ConcurrentHashMap逻辑清晰可读。编译时间从几十秒缩短到一秒内IDE的自动补齐也变得更准确。没有框架的代码像一张干净的白纸每一行都清晰可见。我做了一个简单实验用JDK自带的东西写一个REST API不用Spring Boot结果包体积从十几兆变成了一百多千字节启动时间从秒级变成毫秒级。那种由简驭繁的体验让我重新爱上了Java本身。我不再需要去记“这个注解到底该放在哪个方法上”也不需要关心“那个配置项是不是被新版本废弃了”剩下的就是纯粹的思考、调用、实现。别让框架成为你唯一的答案有人听到“原生Java”就皱眉觉得这是倒退仿佛又回到Servlet时代手动处理字符串拼接。但今天Java的内置能力已经远非从前——Files、HttpClient、Stream、Records、Sealed Class、Virtual Threads尽管还在预览。很多项目其实不需要重框架。如果你的项目只需要几百行代码就不要用重框架。我最近做一个定时抓取任务用ScheduledExecutorService和HttpClient就实现了连数据库都用不上直接处理CSV文件。代码量少测试也简单部署时只扔一个JAR就完事。团队里的同事一开始也怀疑后来他们发现这套东西的调试体验异常顺畅没有任何代理隐藏在你的代码之前你看到什么就是什么。框架不是敌人但清醒必须是当然我并不主张在一切场景下都扔掉框架。大型企业系统需要统一的事务模型、安全管理、配置中心Spring生态依旧是成熟的选择。但关键词是“选择”而不是“默认”。每引入一个依赖前请先问自己它解决了什么问题这个问题能不能用十行原生代码解决框架是工具不是信仰。信仰要求你无条件服从而工具用完了就可以放下。可惜的是很多团队把框架当成了唯一的答案招人的时候要求“精通Spring”写代码的时候宁愿踩坑也要套上它却从没想过原生Java可能已经足够。当你把决策权拱手让给框架时你也就同时放弃了对你自己的代码负责的能力。尾声再回到三年前的那个微服务项目。它最后并没有失败只是每个迭代都异常沉重没有人敢于重构因为改动一处依赖可能引发雪崩。后来我用原生Java重写了其中最核心的模块代码量减少了将近一半测试通过率反而提升了。那次经历给了我一个扎心的结论复杂框架最擅长制造虚幻的进度感你加一个依赖、写一个配置项目看起来在前进实际上只是在堆积复杂度。真正的生产力来自对底层的理解而不是对框架的依赖。放下那些花哨的注解和自动配置回到最基础的Java语言本身你会发现一个更朴素、也更真实的世界。