论坛首页 海阔天空论坛

从毕业到2010的Java程序员生涯(二)

浏览 2099 次
精华帖 (0) :: 良好帖 (0) :: 灌水帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-02-01   最后修改:2010-02-09

 

       年纪大了就喜欢回忆从前?

       不。

       人生又走到了未知的分岔路口,停下脚步回眸过去的路,正是为坚定向前跋涉的心。

 

       过去通宵达旦努力研究的技术在现在回头看看都初级了点,但一个人处理问题的方式和态度不会过时。我心血来潮,花了不少时间来翻看工作日记和旧项目,力图总结一下工作5年的得失。殷鉴不远,可以前知也。

(二).混沌年代:JSP开发

       前面讲到我是在地税组,除此外开发中心还有国税组和项目组,一二十个开发人员。我在地税组做了2年多,后来公司成立技术组,我脱离地税组成为技术经理。

       地税组多的时候6、7人,少时2、3人,负责地税申报网站和一些相关项目的开发和维护,经常跑地税局。招我的时候,开发中心十几个程序员基本都是做php和delphi的,实话实说精通JAVA的一个也没有。从历史来看,以前3、4年一直用php做网站,用delphi做客户端,只是随着04年起J2EE平台在省税务行业逐渐占统治地位,公司才开始进行技术转型。一大批项目都要从C/S架构转向B/S架构、从PHP转到JSP,这是公司当时的技术大方向,我后来几年的个人发展其实是顺应了这个潮流——不过当时的我自然想不到这些。

       大家都知道,技术平台转型是很危险的。虽然说船小好调头,但其实也没这么容易,招聘一批Java高手?No,公司一向的策略是招应届生,以旧带新能干活就行,便宜嘛。而且最好是本地人,否则一年半载就跑掉的概率太高。也没有什么培训,完全靠自学。这是内部现状,外部呢?那时候电子政务还没兴起多少时间,税务局离成熟的客户还差得远,那时候往往是开一个会,说一堆模模糊糊的要求,然后给你一个最后交付时间,等你开发了一半会突然来个电话说:“唉呀,我又想起来还要什么什么”。而且他们会认为什么功能都很简单,“明天给我行不行?”,听到这种话就考验你的忽悠能力了。不像现在,国地税已经熟悉了软件开发的基本规律,甚至会自己写好需求文档再来跟你讨论。强势又不成熟的客户会让你疲于奔命,一定要学会拒绝客户的不合理要求并且把守住需求边界!这我深有体会。

       加上与中国大多数软件公司一样,这是个不讲究技术(实际也没多少技术含量)和代码质量的小公司,他的理念是服务至上(客服人员与程序员之比是1:1强),而实际上的生存之道是要与相关部门保持良好的关系,这才是首要的。因此技术微不足道,至少在老总眼里如此。你只要实现功能,谁也不来管你的代码是不是把一坨代码复制粘贴到另一处。很自由,是不是?

       在这种情况下,结果你也猜到了:从原先就很糟糕的php转换到对技术要求更高的J2EE,结局只会更加糟糕。开发效率低下,性能低下,天天忙着补漏救火。而我这个新丁纯粹出于个人的技术追求,无意中起到了不少正面作用,才脱颖而出,于2年多后升到公司的中层之一——技术经理。

 

       转型过程比较缓慢。

       首先用jsp重写的产品就是地税申报网站。05年3月份我进入地税组时,这个工作已经由地税组经理老唐完成。如前所述,我用了大概一个月学会了jsp开发——完全用php方式写的jsp,唯一的开发工具是UltraEdit,服务器是Weblogic,数据库用Sybase+PowerBuilder。这时候我完全是学习阶段,学sql学html学js学css学业务规则学xxx,什么都要学,没有文档没有培训,自己看着工程代码琢磨,实在搞不定就去请教老唐——被戏称第一调试高手的老唐对用out.print抓到bug实在是很有心得,令我获益匪浅。

 

1.连接池泄漏

show一下我们那时候的精彩代码,仔细看看,你能从中发现多少问题?

 

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>用户信息确认</title>
</head>

<body bgcolor="#F6F6F6" onLoad="fix()" onScroll="fix()" onResize="fix()">
<%@ include file="wsqz.jsp" %>
<%	  
  DBConnect conn=new DBConnect();
  String sql = "select xxx from ddd where id = ?";
  conn.setPstmt(sql);
  conn.setString(1,sessUserID);
  ResultSet result=conn.executeQuery();
	//$result = sybase_query($sql);
	//$row = sybase_fetch_row($result);
%>
请确认您的用户信息:
<table width="100%" align="center" border="0" cellpadding="0" cellspacing="0">
	<tr>
		<td height="20"></td>
	</tr>
</table>
<table width="100%" align="center" border="0" cellpadding="0" cellspacing="0">
	<tr valign="middle">
		<td >纳税人税号:</td>
		<td><%=result.getString(1)%> </td>
	</tr>
	<tr>
		<td>&nbsp;微机代码:</td>
		<td><%=result.getString(2)%></td>
	</tr>
	<tr>
		<td>纳税人名称:</td>
		<td><%=result.getString(3)%></td>
	</tr>
</table>
<table width="100%" align="center" border="0" cellpadding="0" cellspacing="0">
	<tr>
		<td height="20"></td></td>
	</tr>
</table>
请确认您。。。<br>
联系电话:xxx
<%
	result.close();
  conn.close();



%>
</body>
</html>

 这批jdbc代码后来直接导致weblogic在高峰期频频崩溃假死,老唐天天盯着weblogic的监控手动重启。客户投诉暴涨,不管服务部还是开发部都承受着巨大的压力,请bea的工程师远程会诊也没查出所以然来,还是老唐放狗搜索发现这会导致连接池泄漏。我接到任务开始改写所有的jdbc代码,给他们加上try..catch..finally..close ,大概几千处,足够我加班干个几天几夜的。不行,我决定发挥程序员的懒惰,写个程序自动修改。

     怎么写呢?页面很杂乱,嵌套多得要死,代码风格也多,没有时间写一个面面俱到的独立程序来兼容所有可能性,我决定以一种所见即所得的修改方式来最快完成任务——用jEdit写一个macro

     那段时间我正好研究了几个月jEdit和beanshell,打开jEdit的API边查边写:

 

/*
 */
// beginning of Run_Make_New_JSP.bsh
public ArrayList getConnList(){
   ArrayList list = new ArrayList();
   int index = 0;
   String text = textArea.getText();
   while((index = text.indexOf("= new DBConnect();",index))>0){
      //reference name
      String text = textArea.getLineText(textArea.getLineOfOffset(index));
      StringTokenizer tok = new StringTokenizer(text,"= tnr");
      
      if(tok.nextToken().equals("DBConnect"))//reference name
                  list.add(tok.nextToken());
      index = index+20;
   }
   return list;
}
public String[] getRsArray(String connName){
            int index = 0;
            String[] a = null;
            ArrayList al = new ArrayList();
        //find rs line
        String rstmp = "= " + connName + ".executeQuery";
            while((index = textArea.getText().indexOf(rstmp,index)) > 0) {
            lineOfRs = textArea.getLineOfOffset(index);
            //token
            String rsLineText = textArea.getLineText(lineOfRs);
            tok = new StringTokenizer(rsLineText,"= trn");
            tok.nextToken();
            rsName = tok.nextToken();
                  al.add(rsName);
                  index+=rstmp.length();
        }
            if(al.size()==0)
                        return null;
            else
            {
               a = new String[al.size()];
               Iterator it = al.iterator();
               for(int i=0;i<al.size();i++)
               {
                  a[i] = (String)(it.next());         
               }
            }
            return a;   
}
public int[] getLineOf(String word)
{
   int index = 0;
   ArrayList al = new ArrayList();
   String text = textArea.getText();
   while((index = text.indexOf(word,index))>0){   
      al.add(textArea.getLineOfOffset(index));
      index = index+word.length();
   }
   int[] a = new int[al.size()];
   Iterator it = al.iterator();
   for(int i=0;i<al.size();i++)
   {
      a[i] = (int)(it.next());         
   }
   return a;
}
//按照被替换行的缩进进行替换,每一行的缩进与前一行相同,但最后一行有点小问题
public void replaceLines(int line,String t){
      int offset = textArea.getLineStartOffset(line);
      String text = textArea.getLineText(line);   
      i=0;
      String pre = "";
      while(text.charAt(i)=='t'||text.charAt(i)==' ')
      {
         if(text.charAt(i)=='t')   pre+="t";
         if(text.charAt(i)==' ') pre+=" ";
         i++;
      }
      textArea.setCaretPosition(offset);
      textArea.deleteLine();
      buffer.insertIndented(offset,pre+t);
}
// main routine
public void makeOne(String connName) {
   //Macros.message(view,""+map.size());

        int  lineOfConn = 0, lineOfRs = 0,lineOfRsClose = 0,lineOfConnClose = 0;
            int lineOfConn = getLineOf(connName)[0];
            das = getRsArray(connName);
            if(das!=null)
            {
                        String rsName = getRsArray(connName)[0];
                        //DBConnect conn = ...
                        String j = "DBConnect "+connName+" = null;rnResultSet "+rsName+
                        " = null;rntry{rn"+connName+" = new DBConnect();rn";
                        replaceLines(lineOfConn,j);   
                        //ResultSet rs = ...
                        int rsn = getLineOf("ResultSet "+rsName+" = "+connName)[0];
                        rsnn = textArea.getLineText(rsn);
                        suffix = rsnn.substring(rsnn.indexOf(rsName));                                    
                        replaceLines(rsn,suffix+"rn");
                        //delete rs.close()
                        //textArea.setCaretPosition(textArea.getLineStartOffset(getLineOf(rsName+".close()")[0]));
                        //textArea.deleteLine();
                        replaceLines(getLineOf(rsName+".close()")[0],"");
                        //conn.close();
                        String d = "}rn"+
                                        "catch(Exception e)rn"+
                                        "{rn"+
                                        "      e.printStackTrace();rn"+
                                        "}rn"+
                                        "finallyrn"+
                                        "{rn"+
                                        "  tryrn"+
                                        "  {rn"+
                                        "    if("+rsName+"!=null)rn"+
                                        "     "+rsName+".close();rn"+
                                        "     if("+connName+"!=null)rn"+
                                        "        "+connName+".close();rn"+
                                        "  }rn"+
                                        "  catch(Exception e)rn"+
                                        "  {rn"+
                                        "    e.printStackTrace();rn"+
                                        "  }rn"+
                                        "}rn";
                        replaceLines(getLineOf(connName+".close()")[0],d);
                        //Macros.message(view,connName+getLineOf(connName+".close()")[0]);
            }else{
                        //DBConnect conn = ...
                        String j = "DBConnect "+connName+" = null;rntry{rn";
                        replaceLines(lineOfConn,j);   
                        //conn.close();
                        String d = "}rn"+
                                        "catch(Exception e)rn"+
                                        "{rn"+
                                        "      e.printStackTrace();rn"+
                                        "}rn"+
                                        "finallyrn"+
                                        "{rn"+
                                        "  tryrn"+
                                        "  {rn"+
                                        "     if("+connName+"!=null)rn"+
                                        "        "+connName+".close();rn"+
                                        "  }rn"+
                                        "  catch(Exception e)rn"+
                                        "  {rn"+
                                        "    e.printStackTrace();rn"+
                                        "  }rn"+
                                        "}rn";
                        replaceLines(getLineOf(connName+".close()")[0],d);      
            }
       
       
    }

// this single line of code is the script's main routine
// it calls the methods and exits
if(buffer.isReadOnly())
   Macros.error(view, "Buffer is read-only.");
else{
             ArrayList list = getConnList();
             it = list.iterator();
             while(it.hasNext())
             {
                        makeOne((String)(it.next()));
                        //sa+=" "+(String)(it.next());
             }
             it = list.iterator();
             sa = "";
             while(it.hasNext())
             {
                        //makeOne((String)(it.next()));
                        sa+=" "+(String)(it.next());
             }
             Macros.message(view,sa);
   }

/*
   Macro index data (in DocBook format)

<listitem>
    <para><filename>run_Make_New_JSP.bsh</filename></para>
    <abstract>
    <para>
        Adds user-supplied <quote>prefix</quote> and <quote>suffix</quote>
        text to each line in a group of selected lines.
    </para>
    </abstract>
    <para>s
        Text is added after leading whitespace and before trailing whitespace.
        A dialog window receives input and <quote>remembers</quote> past entries.
    </para>
</listitem>

*/

// end Add_Prefix_and_Suffix.bsh

   这是我首次为开发写辅助工具,马上就尝到了甜头。只要用jEdit打开jsp文件按一下快捷键,rs、conn等就自动改成了关闭形式,我可以立刻检查改动是否正确。我感到很自豪,避免了几万次枯燥无味的copy/paste,嘿嘿,这才是程序员的做事风格!虽然写macro用了一天时间,但仍然比手工修改要合算得多,不但省力,更避免了引发新的错误——以我的经验,机械麻木的copy/paste过程必然引发忘改变量名之类的低级问题。

   后来我写了一些jEdit的应用经验在blogjava 上(留言里有pi1ot ,他是个jEdit铁杆,甚至为jEdit修改了一套字体,前段时间还在javaeye发了个jedit4.3的新闻 ),本来还想写高级篇,但是随后杯具——某天我没有关机导致compaq笔记本硬盘在颠簸中报废了——大量代码永远安息,也包括我为jEdit写的十几个bsh。我很受伤,兴致缺缺,这个计划就搁到脑后了。

 

 

   发表时间:2010-02-02  
继续写 加油
0 请登录后投票
论坛首页 海阔天空版

跳转论坛:
Global site tag (gtag.js) - Google Analytics