前言:技术的"生死轮回"
"在这个行业里,唯一不变的就是变化本身。" —— 某位被迫学了三遍前端框架的后端程序员
如果你是一个有着十年以上工作经验的 Java 程序员,那么恭喜你,你已经见证了无数技术的兴衰更替。那些曾经让你熬夜加班、掉光头发的技术,如今可能已经静静躺在技术博物馆的角落里,等待着后人前来"考古"。
今天,让我们一起翻开历史的篇章,缅怀那些曾经辉煌一时、如今却逐渐淡出舞台的 Java 技术。别担心,这不是一篇让你伤感的文章——毕竟,送走它们之后,我们又迎来了更多让我们加班的新技术。
第一章:被淘汰的主要原因
在正式开始"悼念"之前,让我们先来分析一下,为什么这些技术会被淘汰:
1. 技术发展的自然规律
就像诺基亚被智能手机取代一样,技术的发展是不可阻挡的洪流。新技术往往能够提供更高的开发效率、更好的性能表现和更优雅的解决方案。当 Spring Boot 出现时,那些还在手动配置 XML 的程序员们,眼泪都流下来了——不是因为不舍,而是因为太香了。
2. 安全性和可靠性的硬伤
某些技术在设计之初就存在安全隐患,随着互联网的发展和攻击手段的升级,这些隐患变得越来越致命。当一个技术三天两头爆出高危漏洞时,再深厚的感情也扛不住运维的怒火。
3. 复杂性——程序员的头号敌人
"简单就是美,复杂就是加班。"
有些技术设计得过于复杂,学习曲线陡峭得像珠穆朗玛峰,配置文件多得像是在写一部长篇小说。当程序员需要花费 80% 的时间在配置和调试上,只有 20% 的时间在实际业务开发上时,这项技术就离被淘汰不远了。
4. 社区和生态的萎缩
技术的生命力很大程度上取决于其社区的活跃度。当 Stack Overflow 上的相关问题越来越少,GitHub 上的 star 数开始停滞,那些曾经的技术明星就开始走向黄昏了。
第二章:那些年,我们一起追过的"前任"
1. Servlet/JSP:Web 开发的"老祖宗"
生卒年代:1997年至今(名义上还活着,实际上已经退居二线)
辉煌时刻:
// 曾经,我们是这样写 Web 应用的
public class HelloServlet extends HttpServlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>Hello, World!</h1>");
out.println("</body></html>");
}
}淘汰原因:
- 在 Java 代码里写 HTML?在 HTML 里嵌 Java 代码?这操作简直是代码界的"油水混合"
- JSP 的
<% %>标签让代码变得像一锅乱炖 - 没有前后端分离的概念,改个页面样式都要重启服务器
接班人:Spring MVC + RESTful API + 前端框架(Vue/React)
墓志铭:"我不是被打败的,我只是太老了。"
2. Struts:曾经的"一哥"
生卒年代:2000年 - 2017年(Struts 2 在这一年爆出了致命漏洞)
辉煌时刻:
在 21 世纪的头十年,如果你不会 Struts,你都不好意思说自己是 Java Web 开发者。面试官问的第一个问题就是:"讲一下 Struts 的工作流程"。
<!-- 还记得这个经典的 struts-config.xml 吗? -->
<struts-config>
<form-beans>
<form-bean name="loginForm"
type="com.example.LoginForm"/>
</form-beans>
<action-mappings>
<action path="/login"
type="com.example.LoginAction"
name="loginForm"
scope="request"
validate="true"
input="/login.jsp">
<forward name="success" path="/welcome.jsp"/>
<forward name="failure" path="/login.jsp"/>
</action>
</action-mappings>
</struts-config>淘汰原因:
- 配置文件多得能绕地球一圈
- Struts 2 的安全漏洞层出不穷,2017年的 S2-045 漏洞直接让无数企业的服务器"裸奔"
- 架构设计过于复杂,ActionForm、Action、ActionForward... 概念多到让人头秃
接班人:Spring MVC(以优雅的注解配置完胜)
墓志铭:"我曾经是最受欢迎的,直到安全漏洞让我身败名裂。"
3. EJB(Enterprise JavaBeans):企业级的"噩梦"
生卒年代:1998年 - 2010年前后(EJB 3.0 虽然有所改善,但已经难以挽回颓势)
辉煌时刻:
在 Sun Microsystems 的愿景中,EJB 是构建企业级分布式应用的终极解决方案。PPT 上的架构图看起来无比优雅...
// 这只是冰山一角
// 你还需要写一堆 XML 配置文件、Home 接口、Remote 接口...
@Stateless
@Remote(HelloRemote.class)
@Local(HelloLocal.class)
public class HelloBean implements HelloRemote, HelloLocal {
public String sayHello() {
return "Hello, EJB!";
}
}淘汰原因:
- 开发一个简单的 Hello World 需要写 N 个文件
- 部署流程复杂得像发射火箭
- 重量级容器(WebLogic、WebSphere)价格昂贵,启动慢得能泡一杯咖啡
- 测试困难,单元测试几乎是不可能完成的任务
接班人:Spring Framework(轻量级、易测试、控制反转)
墓志铭:"我不是不够强大,我只是太重了,重到程序员扛不动。"
4. Java Applet:浏览器里的"失落世界"
生卒年代:1995年 - 2017年(Oracle 正式宣布弃用)
辉煌时刻:
在 Flash 还没有一统天下之前,Java Applet 曾经是网页动态内容的希望之星。
<!-- 曾经的浏览器页面 -->
<applet code="GameApplet.class" width="400" height="300">
Your browser doesn't support Java Applets.
</applet>淘汰原因:
- 安全漏洞多到让安全专家都怕了
- 需要安装 Java 插件,用户体验极差
- 启动速度慢,每次加载都像是在等待宇宙大爆炸
- HTML5 + JavaScript 的崛起彻底终结了它的命运
- 各大浏览器纷纷移除对 NPAPI 插件的支持,直接判了死刑
接班人:HTML5、JavaScript、WebAssembly
墓志铭:"我曾让网页动起来,可惜我自己却停下了脚步。"
5. OSGi:模块化的"先驱者"
生卒年代:1999年至今(还活着,但在应用开发领域已边缘化)
辉煌时刻:
OSGi 是 Java 模块化的先驱,Eclipse IDE 就是基于它构建的。
// Bundle-SymbolicName: com.example.hello
// Bundle-Version: 1.0.0
// Export-Package: com.example.hello.api
// Import-Package: org.osgi.framework
public class Activator implements BundleActivator {
public void start(BundleContext context) {
System.out.println("Hello, OSGi!");
}
public void stop(BundleContext context) {
System.out.println("Goodbye, OSGi!");
}
}淘汰原因:
- 类加载器地狱(ClassLoader Hell)——当你以为解决了类冲突,新的冲突又出现了
- 配置复杂,对开发者不友好
- 大多数应用不需要运行时动态安装/卸载模块的能力
- Java 9 推出了原生的 JPMS(Java Platform Module System)
接班人:Java 9 Module System、Spring Boot 的 Fat JAR、容器化部署
墓志铭:"我太超前了,超前到世界还没准备好接受我。"
6. XML Web Services(SOAP/WSDL):臃肿的"老绅士"
生卒年代:2000年 - 2010年代(仍在银行、政府系统中苟延残喘)
辉煌时刻:
在 RESTful API 出现之前,SOAP Web Services 是企业级系统集成的标准方案。
<!-- 一个简单的请求,需要这么多"废话" -->
<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<!-- 可能还有一堆安全相关的头 -->
</soap:Header>
<soap:Body>
<m:GetPrice xmlns:m="http://www.example.org/stock">
<m:StockName>IBM</m:StockName>
</m:GetPrice>
</soap:Body>
</soap:Envelope>淘汰原因:
- 报文臃肿,一个简单的调用可能 90% 都是 XML 标签
- WSDL 文件复杂到需要专门的工具来阅读
- 对开发者不友好,调试困难
- 性能开销大,解析 XML 本身就是一种浪费
接班人:RESTful API + JSON
墓志铭:"我很严谨,但世界需要的是简单。"
7. Hibernate Criteria API(旧版):ORM 的"繁文缛节"
生卒年代:早期 Hibernate 版本 - Hibernate 5.x(被 JPA Criteria API 取代)
辉煌时刻:
// 旧版 Criteria 查询
Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.eq("name", "Joey"));
criteria.add(Restrictions.gt("age", 18));
criteria.addOrder(Order.asc("createTime"));
List<User> users = criteria.list();淘汰原因:
- 类型不安全,字段名用字符串表示,拼写错误只能在运行时发现
- 不符合 JPA 规范
- 链式调用的方式不够优雅
接班人:JPA Criteria API、QueryDSL、Spring Data JPA
墓志铭:"我尽力了,但类型安全才是王道。"
8. Java Date/Calendar:时间处理的"噩梦"
生卒年代:JDK 1.0 - JDK 8(虽然还能用,但已被官方"劝退")
辉煌时刻:
// 曾经,我们是这样处理日期的
Date date = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DAY_OF_MONTH, 1);
// 月份从 0 开始???
int month = calendar.get(Calendar.MONTH); // 0 = January, WTF?
// 格式化还要用 SimpleDateFormat,还不是线程安全的
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String formatted = sdf.format(date); // 多线程下可能出问题淘汰原因:
Date类设计糟糕,很多方法早已废弃Calendar月份从 0 开始,这是什么反人类设计?SimpleDateFormat线程不安全,生产事故的常客- 时区处理复杂且容易出错
- 可变对象,容易被意外修改
接班人:Java 8 的 java.time 包(LocalDate、LocalDateTime、ZonedDateTime)
墓志铭:"月份从 0 开始这件事,我知道错了,但我改不了。"
9. Ant:构建工具的"原始人"
生卒年代:2000年 - 2010年代初(仍有遗留系统在使用)
辉煌时刻:
<!-- build.xml 的回忆 -->
<project name="HelloWorld" default="compile">
<property name="src.dir" value="src"/>
<property name="build.dir" value="build"/>
<property name="lib.dir" value="lib"/>
<path id="classpath">
<fileset dir="${lib.dir}" includes="*.jar"/>
</path>
<target name="clean">
<delete dir="${build.dir}"/>
</target>
<target name="compile" depends="clean">
<mkdir dir="${build.dir}"/>
<javac srcdir="${src.dir}" destdir="${build.dir}">
<classpath refid="classpath"/>
</javac>
</target>
</project>淘汰原因:
- 没有内置依赖管理,JAR 包要手动下载和管理
- XML 配置繁琐,写构建脚本像在写小说
- 没有约定优于配置的理念,每个项目的构建脚本都可能不一样
- 对于复杂项目,构建脚本可能比业务代码还多
接班人:Maven → Gradle
墓志铭:"在没有包管理器的年代,我们手动管理 JAR 包,那是一段艰苦但难忘的岁月。"
10. Log4j 1.x:日志界的"老前辈"
生卒年代:2001年 - 2015年(官方宣布停止维护)
辉煌时刻:
# log4j.properties,多少人的青春回忆
log4j.rootLogger=INFO, console, file
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n淘汰原因:
- 架构老旧,不支持异步日志
- 配置文件格式不够灵活
- 性能不如后来者
- 不再维护,安全漏洞无人修复
接班人:Logback、Log4j 2.x(然后 Log4j 2 在 2021 年爆出了核弹级漏洞 Log4Shell,安全团队又哭了)
墓志铭:"我是日志框架的开创者,我的继任者也没让我省心。"
11. Swing/AWT:桌面应用的"遗老遗少"
生卒年代:1995年(AWT)/ 1998年(Swing)- 至今(名存实亡)
辉煌时刻:
// 曾经的桌面应用开发
public class HelloSwing extends JFrame {
public HelloSwing() {
setTitle("Hello Swing");
setSize(400, 300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Click Me!");
button.addActionListener(e ->
JOptionPane.showMessageDialog(this, "Hello, World!"));
add(button);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() ->
new HelloSwing().setVisible(true));
}
}淘汰原因:
- 界面样式老旧,像是上世纪的产物(因为它确实是)
- 跨平台意味着在所有平台上都一样丑
- Web 应用和移动应用的崛起,桌面应用市场萎缩
- JavaFX 也没能拯救 Java 桌面开发,最终也被 Oracle 放弃
接班人:Electron、Web 应用、移动应用
墓志铭:"Write Once, Look Ugly Everywhere."
第三章:正在走向衰落的技术
有些技术虽然还没有完全被淘汰,但已经显露出衰落的迹象:
1. JSF(JavaServer Faces)
虽然还在更新,但使用率持续下降。组件化的思想没有错,但 JSF 的实现方式太过笨重。
2. XML 配置
从 Spring Boot 的成功可以看出,开发者更喜欢注解和约定优于配置。XML 配置文件正在逐渐消失。
3. WAR 部署
传统的 WAR 包部署方式正在被 Spring Boot 的可执行 JAR 和容器化部署所取代。
4. 传统 MVC 服务端渲染
前后端分离已成主流,传统的服务端渲染(Thymeleaf、Freemarker)使用场景越来越少。
第四章:从历史中学习
技术选型的智慧
- 不要追新过度:新技术需要时间来验证,1.0 版本的框架慎用。
- 关注社区活跃度:没有活跃社区的技术,就像没有售后服务的产品。
- 学习思想而非工具:Spring 可能会被淘汰,但依赖注入的思想不会。
- 保持学习能力:今天的最佳实践,可能是明天的反面教材。
程序员的生存法则
public class Programmer {
private Stack<String> skills = new Stack<>();
public void survive() {
while (true) {
String oldSkill = learnNewTechnology();
if (skills.size() > 10) {
String forgotten = skills.remove(0);
log.info("忘掉了: {}, 反正也用不上了", forgotten);
}
skills.push(oldSkill);
}
}
private String learnNewTechnology() {
// TODO: 又要学新东西了...
return "某个三个月后就会被淘汰的框架";
}
}结语:技术的轮回
"太阳底下无新事。"
技术在变,但解决问题的本质需求不变。从 Servlet 到 Spring Boot,从 SOAP 到 REST,从单体到微服务——每一次变革都是对"如何更高效地解决问题"这个命题的新回答。
那些被淘汰的技术,不是失败者。它们是铺路石,是垫脚石,是站在它们肩膀上才有了今天这些"更好"的技术。
作为程序员,我们要做的不是为旧技术的消亡而惋惜,而是要:
- 理解技术演进的规律——知道为什么会变,才能预测未来会怎么变
- 掌握不变的核心原则——设计模式、架构思想、编程范式,这些比具体框架更有价值
- 保持开放的学习心态——今天学的技术,可能五年后就会出现在"被淘汰的技术"列表里
最后,让我们向那些曾经陪伴我们的技术致敬。感谢 Struts,让我们知道了配置地狱的可怕;感谢 EJB,让我们学会了珍惜轻量级框架;感谢 Java Date,让我们明白了好的 API 设计有多重要。
没有你们的"牺牲",就没有今天这些"更好"的技术。
当然,今天的"更好",也可能是明天的"被淘汰"。
这就是技术的轮回,这就是程序员的宿命。
共勉。
本文作者正在学习第 N 个新框架,预计该框架的生命周期为 3-5 年。