2004年的Java项目翻出来了我哭了——一个老程序员的回忆杀
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接口:
publicinterfaceBusiFunc{publicintRun(Documentdoc);publicStringgetMessage();}不就是现在 Controller 的雏形吗?
回忆杀二:那个年代没有 JSON
整个系统前后端的数据序列化全靠手写 XML DOM:
// XMLFunc.java - 把 ResultSet 转成 XMLwhile(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=newString(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):
publicinterfaceHelloextendsEJBObject{publicStringsayHello()throwsRemoteException;}Home 接口:
publicinterfaceHelloHomeextendsEJBHome{Hellocreate()throwsCreateException,RemoteException;}Bean 实现类:
publicclassHelloBeanimplementsSessionBean{privateSessionContextctx;privateStringwords;publicvoidsetSessionContext(SessionContextctx){this.ctx=ctx;}publicvoidejbActivate(){}publicvoidejbPassivate(){}publicvoidejbRemove(){}publicvoidejbCreate()throwsCreateException{words="Hello World";}publicStringsayHello(){System.out.println("I am in an EJB of Server ."+words);returnwords;}}客户端调用:
Propertiesh=newProperties();h.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");h.put(Context.PROVIDER_URL,"t3://localhost:7001");Contextctx=newInitialContext(h);Objecthome=ctx.lookup("HelloHome");HelloHomehelloHome=(HelloHome)PortableRemoteObject.narrow(home,HelloHome.class);Hellohello=helloHome.create();System.out.println(hello.sayHello());就为了打印一个 “Hello World”,你需要写 Home 接口、Remote 接口、Bean 类、客户端,还要写两个 XML 配置文件(ejb-jar.xml和weblogic-ejb-jar.xml),然后打包成.jar,再用 WebLogic 的工具生成.ear。
现在 Spring Boot 一个注解@RestController搞定。当年的 Java EE 是真的重。
回忆杀四:那个连接池
数据库连接的获取方式:
DrivermyDriver=(Driver)Class.forName("weblogic.jdbc.pool.Driver").newInstance();lconn=myDriver.connect("jdbc:weblogic:pool:OraclePool",null);用的是WebLogic 的连接池,通过config.xml配置:
<JDBCConnectionPoolDriverName="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()方法:
privatestaticConnectiontest(){Class.forName("oracle.jdbc.driver.OracleDriver");StringlUrl="jdbc:oracle:thin:@10.81.193.8:1521:hygeia";conn=DriverManager.getConnection(lUrl,"insur_test","test");returnconn;}内网 IP10.81.193.8,数据库 SIDhygeia(Hygeia 是希腊健康女神),用户名insur_test——这些信息告诉我,这是一个真实的社保/医保系统的开发环境。
回忆杀五:那个代码风格
SQL 注入风险
StringcsSql="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(SQLExceptionex){this.iFlag=-411002091;return(this.iFlag);// finally 块中的 return!}}在 finally 块中 return,这是 Java 的反模式,会吞掉 try 块中的异常。但现在看,当年我根本不知道这个规则。
回忆杀六:那些 AW/Swing 练习
jsp/目录下还有一些MouseDemo3.java、myDialog.java、ourButton.java——这些是用AWT/Swing写的桌面程序练习。
// MouseDemo3.java - 鼠标画线程序publicvoidmouseDragged(MouseEvente){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年前代码的老程序员的眼泪。
