互联网出现了许多神话,youtube, facebook, 还会有更多。
它们是业内人士的梦想,在圈外人看来是传奇。
实际上,梦想和传奇非常不简单。创意、技术、管理、营销,任何一个环节失误都不行。
实际上我并不期望做成什么大事,我只想有个比较舒适的工作,可以在累了的时候休息,到处旅游。
互联网出现了许多神话,youtube, facebook, 还会有更多。
它们是业内人士的梦想,在圈外人看来是传奇。
实际上,梦想和传奇非常不简单。创意、技术、管理、营销,任何一个环节失误都不行。
实际上我并不期望做成什么大事,我只想有个比较舒适的工作,可以在累了的时候休息,到处旅游。
我把Gmail作为Firefox的主页,打开浏览器就一直开着。我用Google Reader来读RSS.
曾经用Google Reader Notifier这个扩展来显示未读数目,后来嫌烦,不用了,并且清理了许多无聊的RSS订阅。
不过没有了未读提醒,有时候好多天才想起去看一眼,又觉得不爽。真是很矛盾。
看到Gmail顶上有个Reader的链接,哎,这是个好地方,于是把未读数目显示在那里,挺好:
当然了,这个脚本似乎就是Google Reader Notifier的缩减版,只是有趣而已……
一般情况下8分钟检查一次。点击Reader链接打开以后,改为1分钟后检查,每检查一次,间隔时间加长一分钟,直到恢复为8分钟。
你需要先安装Greasemonkey,然后安装该脚本。
某些情况下,一个表单中提交的内容数量是不一定的。比如Facebook的编辑照片界面,可以把整个相册的照片(最多60张)放在一起进行批量编辑,或者把一次上传的若干张照片一起编辑。这种情况下,如何用ActionForm来获取这些数据呢?
这个ActionForm(假设名为PhotoForm)应该有一个字段:
List<Photo> photos;
生成这个表单的时候很简单,服务器端当然清楚有多少张照片要被编辑,于是对这些照片遍历一遍,生成表单。实际上,struts form的设计考虑了链表数据的提交。以照片编辑页面为例,生成表单时,可以这样:
<logic:iterate id=”photo” name=”photos”>
<html:hidden name=”photo” property=”id” indexed=”true” />
<html:text name=”photo” property=”name” indexed=”true” />
</logic:iterate>
indexed属性便是专为链表设计的。这样的jsp代码生成的HTML类似:
<input type=”hidden” value=”5″ name=”photo[4].id”/>
<input type=”text” value=”编辑前的名字” name=”photo[4].name”/>
然而在用户将修改过的信息提交的时候,由于HTTP协议的stateless特性,服务器端已经“忘记”有多少张照片了(除非你在session中记录了这个数字,不过处于性能考虑大多数人在避免使用session,另外,假如用户在编辑信息的途中出去玩了几小时,回来继续提交的话,session已经不存在了)。
photos这个List即使放在对应的ActionForm中也无济于事,因为用户看到表单时,这个ActionForm对象已经消失了(当然,不要在那个mapping中设置scope=”session”)。
究其源,溯其本,很容易解决这种问题。请求提交给struts,struts首先根据mapping创建一个对应的ActionForm对象,当它看到表单中的属性名为photo[4].id,就会调用PhotoForm的getPhoto(4).setId(xxx)来填充表单。
注意,struts不是生成一个Photo对象,把id、name填充进去,再加到photos这个List中,它不知道要生成Photo对象。它只知道利用发射机制调用getPhoto(4).setId(xxx)。
所以我们应该在PhotoForm的getPhoto这个方法里做文章,根据参数将List扩展到适当的大小避免IndexOutOfBoundException,并且生成Photo对象保证不出现NullPointerException. 示例如下:
public Photo getPhoto(int index) { // 如果链表不存在则创建 if (photos == null) { photos = new ArrayList<Photo>(index + 1); } // 如果链表长度不够,扩展 if (photos.size() < index + 1) { for (int i = photos.size(); i < index + 1; i++) { photos.add(null); } } // 取得photo,如果为空则创建 Photo photo = photos.get(index); if (photo == null) { photo = new Photo(); photos.set(index, photo); } return photo; }
这样就可以了,没有用到session. 看起来比较繁琐,其实道理很简单,明白了struts form的填充方式,就没什么困难了。
另一个例子是客户端可以用JavaScript生成一堆这样的file input:
<input type=”file” name=”file[0]” />
<input type=”file” name=”file[1]” />
<input type=”file” name=”file[2]” />
<input type=”file” name=”file[3]” />
struts怎么填充呢?它会首先读取文件数据,创建FormFile对象,然后调用对应的ActionForm的setFile(index, file)方法填充,index就是那个“数组下标”。所以你需要写一个setFile方法来正确处理。
在合肥的时候,靠骗钱维生的电脑学校很多,而且确实骗到了许多钱,于是形成了良性循环,在电视上打广告,骗更多的钱。有个广告的末尾就是一憨厚的老大爷高兴地说:只有正规,才有机会啊!
某些情况下这句话很对。那些在网上被到处转贴的代码片段,我就不敢信赖,而且格式很乱也看不清楚。在找汉字转拼音程序的时候,我还是选择了pinyin4j,Source Forge上的项目,相对值得信赖。哦对了,这是Java的library.
一个简单的方法,使用pinyin4j转换字符串中的汉字为拼音,示例如下:
public static String toPinyin(String input) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < input.length(); i++) { char c = input.charAt(i); if (c <= 255) { sb.append(c); } else { String pinyin = null; try { String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c, PINYIN_FORMAT); pinyin = pinyinArray[0]; } catch (BadHanyuPinyinOutputFormatCombination e) { logger.error(e.getMessage(), e); } catch (NullPointerException e) { // 如果是日文,可能抛出该异常 } if (pinyin != null) { sb.append(pinyin); } } } return sb.toString(); }
其中用到的常量PINYIN_FORMAT是这样初始化的(输出时去除声调标记,发音“驴”的那个韵母用v表示):
PINYIN_FORMAT = new HanyuPinyinOutputFormat();
PINYIN_FORMAT.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
PINYIN_FORMAT.setVCharType(HanyuPinyinVCharType.WITH_V);
因为汉字有多音字,所以PinyinHelper.toHanyuPinyinStringArray返回一个数组。这个方法图省事,就直接选择了第一个读音。如果可能的话,可以将遇到这种多音字的地方提出来,人工进行处理。
在当下这个项目开始的时候,我们尝试使用NetBeans作为开发工具。也许是由于之前一直在用Eclipse,先入为主了,我总是发现NetBeans在一些地方似乎有先天的不足。于是在一周后,项目移到了Eclipse中进行开发,到现在依然在为没有太晚做这个决定而庆幸。
先从表面现象来比较。Eclipse的图形界面是SWT,而NetBeans作为Sun的产品,当然不好意思不用Swing了,于是给人第一印象就不好。某些系统上,dockable的窗口标题字体与标题栏高度明显不符,很不协调。输入中文时,候选窗也不能跟随光标移动——现在的Web开发一般都使用大屏幕显示器,你需要不时地看看屏幕的角落。别人可能会觉得奇怪——你有什么急事吧?要不然怎么不停地看时间呢?
在Eclipse中我习惯打开一个资源管理器窗口,从中复制一些文件,然后切换到Eclipse窗口直接粘贴到Package Explorer合适的目录。这一习惯在NetBeans里行不通,只能在IDE内部复制、粘贴。
然后透过现象看看本质。在NetBeans选中一个文件,按F2,你就发现,文件后缀是不能修改的!如果你想把一个.jspf改为.jsp,只能在资源管理器中做。看来NetBeans的理念就是傻瓜——比Windows还要傻瓜。
SVN集成。NetBeans默认集成了SVN,乍一听很让人兴奋。确实,当我看到单个文件中都有图形化的svn diff的时候,兴奋地跑到隔壁去找人倾诉。不过等到commit的时候我就开始沮丧了,完全没有subclipse方便、直观(subclipse 1.4不好,到现在我仍然在用1.2)。
最让人郁闷的是,J2EE项目中,在资源管理器里新加一个文件,在NetBeans刷新出来,部署时仍然会被忽略掉!仔细想想才明白,原来是要执行svn add才可以……合理乎?不合理乎?这svn集成,用一个词来形容就是“天衣无缝”。Web application部署都考虑到svn了,真是周到,佩服,佩服。
NetBeans的项目配置文件太过复杂,不适合作代码管理。nbproject下一堆文件,大部分是ant build file. 如果有library引用,肯定是绝对路径。在Eclipse里,即使有定制classpath,只要手动改成相对目录就一劳永逸了,svn co到任何地方都直接打开没有任何问题。NetBeans可不一般,改成相对目录可以,只要再新加入一个jar包,已改为相对目录的还会变成原来的绝对路径。
这不由得让人怀疑,NetBeans傻瓜吗?不,它很强大,很灵活,要不然配置文件怎么会一大坨。那这么复杂的配置文件,svn怎么管理啊?哦……svn集成很完美——web app部署都会考虑到代码管理。
x坐标一端是傻瓜,一端是灵活;y坐标一端是天衣无缝的svn集成,一端是复杂到天衣无缝的svn集成都无能为力的配置文件。天才的NetBeans在这个二维空间里找到了那个独一无二的完美的点,就像宇宙中只有一个地球一样。我错了,我是火星人。
Refactoring. Refactor – Encapsulate field,没有全选、取消全选的按钮。同时已有getter/setter的方法没有体现出来,但是你硬要选上的话,它又会抛出exception.
Refactor – Rename,没有同时重命名getter/setter的选项。
从某种程度上说,NetBeans很像MS的产品——大部分用户做傻瓜,小部分人是精英,为傻瓜们解决不应该是问题的问题。我做不了精英,却不想做傻瓜,于是还有一条路——我逃了。
嘿嘿,有许多人推崇NetBeans。确实,NetBeans某些方面比Eclipse做得好,比如上面提到的文件内svn diff,还有对于web开发很重要的——更好的JavaScript编辑器、HTML编辑器。还有其它的我还没来得及发现就放弃了。本文也就是发发牢骚,顺带给想比较这二者的同仁们抛砖引玉。
说到Eclipse 3.4,大部分人只会注意eclipse核心功能的改进和新特性比如Breadcrumb。
不久前发现Eclipse 3.4对于Web developer来说也有不少值得注意的改进,具体链接在这里。
Web开发这一块功能属于Web tools platform. 我最关心的是这一段:
A new JavaScript IDE, called JSDT, provides the same level of support for JavaScript as the JDT provides for Java. Some of the new features include code completion, quick fix, formatting and validation. All the functions are, naturally, aware of libraries you specify for your project.
自带的JavaScript编辑器终于可以凑合用了。
另外,编辑XML/HTML/JSP页面时,编辑器会自动高亮匹配的标签,这一点太重要了,让我们不至于在复杂的标签中迷失。
不过,JSP的格式化依旧很烂,另外,回车时竟然不能保持缩进了!真是有得有失。
经过不知道多少次的推迟之后,地铁10号线终于“抢”在单双号施行前开通了。我就住10号线站台不远,今天和弟弟闲着没事,就想去看看。到哪里去呢?正巧大陆第一家apple store开业,位置正好在10号线边上,这是最佳的目的地!
大约是周末的原因,进入地铁站发现乘客很少,空调又开得很大,让人感觉异常的冷,而里面的警察、保安数目却是异常的多。
相对1号线、2号线,10号线运行起来相当平稳。不过由于刚开始运行,难免出些小差错。上车后连续两站都是在进站前就停下,因为我坐第一节车厢,透过玻璃看见前面一辆车还停在站台上没走。第二次遇到这种情况后,列车故意延迟了一会出站。车头仪表盘:
还有一点非常让人郁闷的是不光隧道内部,刚进地铁站,手机就一点信号都没有了。这一点比较致命,相信以后会改的,到时候应该会覆盖所有区域。
————-一定要有个分割线————-
在团结湖站下车,出站后顺工体北路向西,没多远就到了Adidas的商店,从旁边进去,就看见传说中的apple store了。
商店旁边还专门有一个做活动用的大厅,似乎比商店本身还大:
从门口进去,店员给每个人戴一个手环,上面是排队号码:
昨天就在网上看到有人在排队了,真是够辛苦。好在地铁10号线是下午两点以后才开通,这时排队的人相对来说已经比较少了,我们等了十几二十分钟,就进入了商店。主要是玩了玩iPod touch,看了看Macbook Air. 店里没有iPhone,不过iPod touch上面的firmwire已经是2.0版本了,主界面上有app store. 在safari中输入网址的时候,按住.com键不放,就会出来.edu, .org, .net这些选择,这个改进很不错。Air是第一次见,除了薄之外,实在是没什么吸引力。
apple store开到中国来,还是很让人兴奋的。可是,关税啊关税……是让人从美国带回来(或者买水货)免掉高额的关税,还是在这里多花几千大洋得到优质的售后服务呢?许多用户估计都得做这个选择。也许光靠这个商店的销售是收不回高额的成本(雇员薪水、地租、明星出场费)的,不过这个商店可以让更多的人了解苹果的产品,对于扩大中国的市场,还是很有作用的。但是销售额如何也不能过早下定论,中国人平均购买力是低了点,苹果的产品是贵了点(尤其是过了中国海关),不过十几亿的人口摆在那里,富人还是很多的。
在Hibernate 2中习惯了将limit ?, ?语句写在HQL中,使用Hibernate 3时突然发现limit语句不起作用了,hibernate生成的SQL中完全不见limit的踪影,它被忽略了!
limit在分页中可是至关重要的工具。当然某些牛人把整个表都取出来在内存中作分页,我却没有这种魄力。
HQL的文档中也没有提到LIMIT,可能真的是不支持了。不过我们可以用Criteria这个接口的setFirstResult和setMaxResults来实现limit。Query接口也有类似的方法。
如果用的是Spring的HibernateTemplate,则可以调用其findByCriteria方法来实现相同的功能。