2004年的 Java 项目翻出来了我哭了——一个老程序员的回忆杀

前几天整理旧硬盘,翻出了 web_sy 这个项目。文件时间戳显示 2004年9月 ~ 2005年1月。那是我学 Java 的第一年,写出的第一个"能跑"的系统。20年后的今天再看这些代码,我笑着笑着就哭了。


这个项目是什么?

这是一个 社保信息系统(JBMIS),运行在:

  • WebLogic 8.1.2(BEA 公司,后来被 Oracle 收购了)
  • Oracle 数据库jdbc:oracle:thin:@localhost:1521:orcl
  • EJB 2.0(Stateless Session Bean)
  • Servlet + XML(前后端通信全靠 XML 字符串)

项目名 web_sy——“sy” 大概是"实验"或"实习"的缩写。从命名就能看出来,这是一个练手项目。


回忆杀一:那个 XML 通信协议

2004年没有 RESTful,没有 JSON,前后端通信全靠手搓 XML:

<Program>
    <FunctionID>80001</FunctionID>
    <parameters>
        <aab001>320123456789012345</aab001>
        <password>123456</password>
    </parameters>
</Program>

Servlet 收到 XML → 解析出 FunctionID → 查数据库找到对应的 Java 类名 → Class.forName(className).newInstance() 动态加载 → 执行业务逻辑 → 返回 XML。

这就是我当年理解的"框架":

// myServlet.java - 核心分发逻辑
Func_Id = XMLFunc.getFuncIDFromDoc(doc);
strClassName = SysFunc.getClassNameByFuncIdFromDB(Func_Id);
curBusiFunc = (BusiFunc)Class.forName(strClassName).newInstance();
retCode = curBusiFunc.Run(doc);
retMsg = curBusiFunc.getMessage();

现在回头看,这不就是一个简陋版的 Spring MVC 的 @RequestMapping 吗?只不过 Spring 用注解,我用数据库查表。

那个 BusiFunc 接口:

public interface BusiFunc {
    public int Run(Document doc);
    public String getMessage();
}

不就是现在 Controller 的雏形吗?


回忆杀二:那个年代没有 JSON

整个系统前后端的数据序列化全靠 手写 XML DOM

// XMLFunc.java - 把 ResultSet 转成 XML
while (rs.next()) {
    iRow++;
    nRow = doc.createElement("row" + iRow);
    for (iLoop = 0; iLoop < iFields; iLoop++) {
        sKey = (String)fields.get(iLoop);
        nField = doc.createElement(sKey.toLowerCase());
        sValue = rs.getString(sKey);
        if (!rs.wasNull()) {
            sValue = new String(sValue.getBytes("ISO8859_1"), "GBK"); // 经典的编码转换
            nValue = doc.createTextNode(sValue);
            nField.appendChild(nValue);
        }
        nRow.appendChild(nField);
    }
    nRs.appendChild(nRow);
}

每一行数据就是一个 <row1><row2>…每个字段是一个 XML 节点。没有 Jackson,没有 Gson,XML 全靠 DOM API 一点一点拼

看到 new String(sValue.getBytes("ISO8859_1"), "GBK") 这行了吗?每个 Java 程序员都写过这样的编码转换。2004年,中文编码是最大的噩梦。


回忆杀三:那个 EJB 2.0 的 Hello World

jsp/ejb/ 目录下有一个完整的 EJB 2.0 示例。EJB 2.0 写一个 “Hello World” 需要多少个文件?

至少4个

远程接口(Remote Interface)

public interface Hello extends EJBObject {
    public String sayHello() throws RemoteException;
}

Home 接口

public interface HelloHome extends EJBHome {
    Hello create() throws CreateException, RemoteException;
}

Bean 实现类

public class HelloBean implements SessionBean {
    private SessionContext ctx;
    private String words;
    public void setSessionContext(SessionContext ctx) { this.ctx = ctx; }
    public void ejbActivate() {}
    public void ejbPassivate() {}
    public void ejbRemove() {}
    public void ejbCreate() throws CreateException { words = "Hello World"; }
    public String sayHello() {
        System.out.println("I am in an EJB of Server ." + words);
        return words;
    }
}

客户端调用

Properties h = new Properties();
h.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
h.put(Context.PROVIDER_URL, "t3://localhost:7001");
Context ctx = new InitialContext(h);
Object home = ctx.lookup("HelloHome");
HelloHome helloHome = (HelloHome) PortableRemoteObject.narrow(home, HelloHome.class);
Hello hello = helloHome.create();
System.out.println(hello.sayHello());

就为了打印一个 “Hello World”,你需要写 Home 接口、Remote 接口、Bean 类、客户端,还要写两个 XML 配置文件(ejb-jar.xmlweblogic-ejb-jar.xml),然后打包成 .jar,再用 WebLogic 的工具生成 .ear

现在 Spring Boot 一个注解 @RestController 搞定。当年的 Java EE 是真的重。


回忆杀四:那个连接池

数据库连接的获取方式:

Driver myDriver = (Driver)Class.forName("weblogic.jdbc.pool.Driver").newInstance();
lconn = myDriver.connect("jdbc:weblogic:pool:OraclePool", null);

用的是 WebLogic 的连接池,通过 config.xml 配置:

<JDBCConnectionPool DriverName="oracle.jdbc.driver.OracleDriver"
    Name="OraclePool" Password="{3DES}n7C1FaJNa5k="
    Properties="user=jbmis" Targets="myserver"
    TestTableName="dual"
    URL="jdbc:oracle:thin:@localhost:1521:orcl"/>

密码用 3DES 加密存储——这在当年算是"安全"的了。现在谁还在 config.xml 里明文写数据库配置?都用 Nacos/Apollo 配置中心了。

还有一个 test() 方法:

private static Connection test() {
    Class.forName("oracle.jdbc.driver.OracleDriver");
    String lUrl = "jdbc:oracle:thin:@10.81.193.8:1521:hygeia";
    conn = DriverManager.getConnection(lUrl, "insur_test", "test");
    return conn;
}

内网 IP 10.81.193.8,数据库 SID hygeia(Hygeia 是希腊健康女神),用户名 insur_test——这些信息告诉我,这是一个真实的社保/医保系统的开发环境。


回忆杀五:那个代码风格

SQL 注入风险

String csSql = "SELECT aab001,password,sn FROM user_ab01 where AAB001 = '" + csAAB001 + "'";

字符串拼接 SQL,没有任何防注入处理。2004年,PreparedStatement 已经存在了,但我在查询时还是用了字符串拼接。只有在更新操作时才用了 PreparedStatement——大概是因为更新时参数太多,拼接太麻烦。

密码明文比较

if (!csPassWord.equals(dpassword)) {
    this.iFlag = -1;
    this.strErr = "ERR,密码不正确!";
}

密码明文存储、明文比较。没有 MD5,没有加盐哈希,更没有 BCrypt。那个年代大家都是这么干的

错误码硬编码

this.iFlag = -411002016;
this.strErr = "获取参数时出错";

错误码像电话号码一样:-411002016-490002165-411002090…完全没有枚举,没有常量定义,纯硬编码。现在看到这些数字,我已经完全不记得每个码代表什么了。

finally 中 return

finally {
    try {
        if (this.conn != null && !this.conn.isClosed()) {
            this.conn.close();
        }
    } catch (SQLException ex) {
        this.iFlag = -411002091;
        return(this.iFlag); // finally 块中的 return!
    }
}

在 finally 块中 return,这是 Java 的反模式,会吞掉 try 块中的异常。但现在看,当年我根本不知道这个规则。


回忆杀六:那些 AW/Swing 练习

jsp/ 目录下还有一些 MouseDemo3.javamyDialog.javaourButton.java——这些是用 AWT/Swing 写的桌面程序练习。

// MouseDemo3.java - 鼠标画线程序
public void mouseDragged(MouseEvent e) {
    flag = 2;
    x = e.getX();
    y = e.getY();
    repaint();
}

还有一个极其简单的 JSP:

<HTML>
<BODY>
    Hello! The time is now <%= new java.util.Date() %>
</BODY>
</HTML>

这大概是那个年代每个人的第一个 JSP 吧。


回忆杀七:那个编码问题

整个项目的中文注释全是 GBK 编码,用现代编辑器打开全是乱码:

// ÔÚXMLÎĵµÖдÓParameters½ÚµãÖеÄÈ«²¿×Ó½ÚµãÈ¡³ö
// ×÷ΪkeyÖµÌí¼Óµ½HashtableÖзµ»Ø

Avector.java 里甚至有一行直接是乱码的中文:

中文内容 v.setElementAt("four",4);

这不是 bug,是 GBK 编码在 UTF-8 环境下的惨剧。当年每个项目都要处理编码问题,现在谁还操心这个?


回忆杀八:那个深夜加班

看文件时间戳:

2004/11/21 00:16:50  a.jsp
2004/12/15 23:23:32  JBSetAC01_sy.java
2004/12/15 23:24:14  JBSetAC01_sy.class
2004/12/16 01:32:08  JBSetAC01_zj.class
2004/12/16 01:36:30  JBSetAB01.java

凌晨1点半还在写代码JBSetAC01_sy(失业)、JBSetAC01_zj(在职)、JBSetAC01_js(就业)——社保系统的各种人员状态,一个状态写一个类,凌晨还在加班。

20年前如此,20年后…也是如此。程序员的加班宿命,从未改变。


回忆杀九:那个自学路线

从时间戳可以还原当年的学习路线:

2004/09  Servlet 基础
2004/11  XML 解析(DOM)、数据库操作、工具类
2004/11  EJB 2.0(Stateless Session Bean)
2004/11  AWT/Swing 桌面编程
2004/12  业务功能开发(社保系统 CRUD)
2005/01  收尾、打包

那是一个没有 Maven、没有 Gradle、没有 Spring 的年代:

  • 依赖管理?手动拷贝 .jar 文件
  • 项目构建?写个 build.bat 或者直接在 IDE 里点"编译"
  • 部署?把 .war.ear 丢到 WebLogic 的 applications 目录
  • 版本控制?这个项目根本没有 .svn.vss 目录,可能连版本管理都没用

20年后的感悟

2004年 2024年
WebLogic 8.1 Spring Boot 3.x
EJB 2.0 Spring @Service
XML DOM 解析 Jackson/Gson
Servlet + XML RESTful + JSON
手动管理连接池 HikariCP + MyBatis
字符串拼接 SQL MyBatis/JPA
GBK 编码噩梦 统一 UTF-8
无版本管理 Git + GitHub/GitLab
深夜加班写代码 深夜加班写代码

20年过去了,技术栈换了三轮,但深夜加班这一点从来没变。

看这些代码的感觉很复杂:既为自己当年的笨拙感到好笑,又为那种不怕困难、从零开始的学习劲头感到怀念。那时候没有 StackOverflow,没有 ChatGPT,遇到问题只能翻书和查 API 文档。

那个写 Class.forName(strClassName).newInstance() 的年轻人不会想到,20年后他会在一个完全不同的技术栈上做架构师,但那个"用反射动态加载业务类"的思想,他用了整整20年。


那些消失的东西

  • BEA WebLogic — 被 Oracle 收购,风光不再
  • EJB — 被 Spring 彻底取代
  • XML 配置 — 被注解和 YAML 替代
  • AWT/Swing — 被 Web 前端取代
  • ISO8859_1/GBK — 被 UTF-8 统一
  • 那个 10.81.193.8 的开发服务器 — 大概早就报废了

但那个在凌晨1点半还在调代码的人,还在。


写在2024年,一个看了20年前代码的老程序员的眼泪。

更多推荐