|
2005年12月26日
现在做互联网产品的团队都比较小,也可能没有特别多运维人员。因此特别需要用一些系统或是工具来监控服务器或者是服务是否正常。之前比较直接的做法是自己搭建一套开源的监控系统,现在随着云服务器的流行,也有越来越多的人用户会使用云端的监控平台。
从我的经验来看,云服务器监控是有些特别的好处的:
1.自建的监控平台,有一部分问题是没办法发出警报。比如,一般监控服务器也会在内部网络中,如果出现外部网络问题,监控服务是没办法通知到相关人员(因为发邮件或者是发短信的通路也会出现问题)。
2.自建的监控平台,只能检测到一个点的访问情况。阿里云监控平台可以检测杭州和青岛两个节点(这是我的帐号看到的),可以比较有效地了解非监测点的一个访问情况。
3.云监控平台都有一套不错的管理界面,可有效减少部署维护和使用成本。
下面我介绍一下,我使用过的一些云监控平台,并对他们的优点和缺点进行比较。
阿里云监控
阿里的云服务器在市场中是做的很不错的,我的很多朋友都购买了阿里的云服务器。以阿里的公司实力,做一款云监控的产品应该不会差到哪里去。
优点:
1.产品体验好,进入住界面就能轻而易举找到你想要的功能。
2.监控功能全,包括站点监控、服务器监控和自定义监控。
3.多点监控,可以在全国提供几个点的监控。从用户的角度了解服务是否可用。
4.站点监控功能也很全面,包含SMTP、POP3、FTP监控。
5.免费,且监控站点数没限制。
缺点:
1.服务器监控和自定义监控,要求是云服务器,如果不是云服务器,只能使用站点检测功能。
360网站服务监控
360的个人用户产品很少使用,但是360的企业产品还真有不少做的不错的。说说360监控的优缺点。
优点:
1.提供服务器监控。可以监控到服务器的磁盘,CPU,内存等情况。
2.UI和告警都设计的不错。
3.免费。
缺点:
1.需要在服务器上开SNMP协议。(不过360提供很多脚本工具,可以一键安装)
2.有20台服务器的限制。(这个基本上够用,超过20台服务器,可以购买服务了)
监控宝
名字很专一,一看就知道是专业做监控的。
优点:
1.专注监控,界面设计还算可以。
2.监控功能全面,内网采集的方式较多。可以采集数据库数据。
缺点:
1.免费用户,服务器和网站监控都有限制,而且数量极少2台服务器监控,5台网站监控。
作者简介: qiyadeng(www.qiyadeng.com)对互联网技术、运营及市场领域有浓厚的兴趣,喜爱思考、阅读、讨论;擅长Java开发及分布式技术。现专注于互联网的创新产品– 老来宝(http://www.laolaibao.com),帮助年轻用户获得补充养老金,并提供养老金增值产品。
比较少参加这类大型的会议,进到会场的第一眼,发现会议室已经全部坐满,后来主办方发现站的人太多了,找来了一些小板凳,我快速找到一个小板凳坐下。坐下开始认真听,非常开心地听到广告时间结束,和我计划的时间完美一致。
回顾一下我比较关系的几个主题
基于用户画像的大数据实例
演讲嘉宾是联通沃商店的大数据技术经理,该大数据实例主要是通过联通营运商的数据和沃商店进行分析,通过绘制用户画像的形式,在其他应用场景,如广告、游戏下载中为用户推荐用户喜欢的产品。可以看出来嘉宾技术实例及基础功是十分不错,至少是一个硕士毕业。近些年被大家挂在嘴边的机器学习算法、推荐算法、语义分析都有部分介绍,实在是接受不过来;比较熟悉的还是我们当初硕士的专业方向推荐算法,看到了简单的介绍觉得很亲切。不过后来提问环节看,现场还是很多高人,有不少是做这个领域的。不过归根是国有企业和类似研究机构,是否能产生非常大的价值,我表示怀疑,不过这些算法一罗列,对经费的分配还是很有好处的。
电商系统的心得分享
这又是一个国有企业,号称是线上卖大力丸的人(国药1健康)。从技术成长为总经理,有很多心得体会。感觉和我有那么一点像,有一些体会也迫不及待的分析给这些年轻的IT从业者,为人严肃,总是会把困难估计的充分一点(估计年轻也没少教学费)。演讲中说了构建系统中的四个原则
权限独立,相互制约
非常务实的看到某些大型企业的,部门斗争。从系统层面开始设计制约(这个应该非常符合老板心意)。这个对很多小型企业在成才过程中是非常有帮助的。
设计流程 减少犯错
在电商行业非常清楚客服和仓库的员工流动性,以及普遍受教育程度偏低,通过流程设计,而不是提高对用人的要求。这也是非常务实的方法。回顾之前在系统层面独自设计支持中央预订系统,设计出来的自动传真(当时网络不如现在易得)及新订单提醒(感谢施总的支持,增加音响进行声音)等等,简直觉得找到了知音。
多了解一些财务知识。
谈到的两点是数据之间需要有勾稽关系和不能修改历史数据,很骄傲我对财务的理解还是不错,从未犯过这种不靠谱的错误。
跨平台大型在线客服系统的技术构架
嘉宾谈了的是一套客服系统,比较多的关键字是客服妹子,可以看出IT从业者苦中作乐的精神。给我的体会是,客服系统都可以做成这样。从一个项目到一个产品,在云计算的世界,可以好一个客服的组件,也是有很大的价值。和我的理想事业很接近,可以花上一生中最精华的时间,做好一个有价值的小众专业的行业。
阿里分布式数据库服务实践
阿里的人就是高调,上场就调戏京东双11前系统崩溃。我也经历过很多系统崩溃,简直是开发人员的噩梦,也是IT人员信用受损的严重事件(因此我一直比较注意防止崩溃及崩溃后的快速恢复)。回到分布式数据库,这个是收获最大的一个演讲。虽然这个演讲看上去是再给阿里云的DRDS做宣传,但是嘉宾演讲的很进行,深入浅出地介绍了分布式数据库和单机数据库的区别。对分布式事务的重新认识是一个很大的收获,以前一直把教程中的数据库原理中的事务定义,作为分布式事务需要解决的问题,其实不是。需要更加务实,在淘宝阿里这类订单处理系统中,有一类对分布式事务的模式(异步消息机制);在其他领域会有其他模式分布式的事务模型,这些分布式模型肯定都不满足单机的事务模型,但是可以满足和解决相应领域的问题。
平台架构的服务器监控
一个APP的监控模型,猜测项目立项的原因,有两个。一个是和竞争对手的数据比较(UPYUN的对手主要是七牛),一个其实可以真正从用户的角度看,用户的体检速度如何,以及影响用户体验速度的真实原因。目前一般行业还不会做的这么细,因为UPYUN是技术支持公司,因此一定需要用这些数据去说服和支持用户。我们现在做的比较多的服务器的可用性、性能和应用的可用性、性能监控。前端时间刚好再比较,发现互联网上有不少好的监控平台,一般的创业公司,可以无需自己搭建监控平台,接入到相应的监控平台即可。下次再开文进行讨论。
作者简介:qiyadeng(www.qiyadeng.com)对互联网技术、运营及市场领域有浓厚的兴趣,喜爱思考、阅读、讨论;擅长Java开发及分布式技术。现专注于互联网的创新产品--老来宝(http://www.laolaibao.com),立志于帮助广大凤凰(diao)男(si)提供补充养老金管理平台。
摘要: guava是Java的一个扩展类库,在google的许多项目中使用过了,现在最为一个 开源的Java类库广泛使用(http://code.google.com/p/guava-libraries/)。
guava类库扩展的主要是这些相关类:collections(集合类),concurrency(并发),primitives,reflection(反射),comparison,I/O,hashi... 阅读全文
简单介绍一下8个Java牛人,他们为Java社区,创建了框架(framework),产品或者是写书,影响甚至改变了Java开发的方法(根据个人喜好排序)。 8.Tomcat创始人 James Duncan Davidson,是当时Sun公司的软件工程师(1997-2001),创建了Java的Web服务器Tomcat,Tomcat广泛应用于Java Web开发的各个领域。 7.测试驱动开发JUnit创始人 Kent Beck,极限编程和测试驱动开发方法的缔造者。此外,他还创造了JUnit,JUnit目前一次成为Java开发测试的事实标准。基于测试驱动的开发方法和JUnit给Java开发的方法带了巨大的变化。 6.Java Collections框架设计者 Joshua Bloch,领导设计了Java平台的许多功能,包括Java 5.0 版本中饱受赞誉的Java Collections框架。2004年他离开Sun公司,成为Google的首席Java架构师,此外他的著作“Effective Java”基本上是学习Java的必读之书。 5.JBoss创始人 Marc Fleury,在2001年创造了JBoss,JBoss是一个Java开源的应用服务器,也已经成为Java Web应用部署中的事实标准。后来他把JBoss买给了RedHat,之后继续从事JBoss的开发工作。不过2007年他离开了RedHat去追求他的个人爱好。 4.Struts创始人 Craig Mcclanahan,创建了Struts,一个流行的基于Java的MVC开源框架,基本上很多Java开发者都知道如何开发Struts的应用程序。 3.Spring创始人 Rod Johnson,Spring框架的创始人,Spring Source的CEO。Spring是一个非常流行的Java应用程序开发的开源框架。此外,他的著作Expert One-to-One J2EE Design and Development,是J2EE最有影响力的一本书。 2.Hibernate创始人 Gavin King,Hibernate的创始人,一个流行的Java ORM解决方案;同时他也是Seam的创始人,此外他为EJB3.0和JPA也做出了突出的贡献。 1.Java之父 James Gosling,1994年发明了Java语言,他创建了Java编译器和虚拟机。在2010年,当Oracle收购Sun公司时,他离开了Sun公司。 原创文章,作者:qiyadeng,转载请注明: 转载自http://www.qiyadeng.com/ 本文链接地址: 你应该知道的8个Java牛人
1.申请开发者帐号 首先注册百度的帐号,然后申请成为百度开发者(需要通过手机进行身份证验证)。 2.新建应用 点击菜单中的创建应用,我们目前选择的是Web应用。 应用创建之后,选择左边菜单的云环境,环境类型需要选择JAVA。 并新创建一个版本,输入1作为版本号 3.在百度集成开发环境中开始开发 百度提供了基于Eclipse的插件,由于该插件不能支持最新的Eclipse版本。建议下载百度的一键安装版本。百度文档中介绍了如何使用开发环境,详细请看集成开发环境使用。 打开百度集成开发环境,在Eclipse左下角点击Login to Baidu,使用你的账号登陆。然后点击工具栏中百度Logo,选择Import BAE Project,填入application和version 之后选择Java作为Project Language。 4.解决项目错误 刚导入的BAE project,在Eclipse中会报错。通过problems view可以看到是因为JRE环境配置不正确和Web运行环境设置不正确。 A.右键项目属性--选择JavaBuildPath,在Libraries中选择Add Library,之后再选择JRE System Library。 B.接下来把Java project转换为Java Web Project(Eclipse中Java Project转换为Java web Project),注意如果你的tomcat是6版本的话,请注意选择Dynamic web Module的版本不超过2.5。 设置Web应用的运行环境,在servers view中新建一个tomcat服务器。 C.和A类似,在Java Build Path中加入 Server Runtime,选择Tomcat。 D.修改hello.jsp,在hello.jsp中加入如下代码 <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %>
E.把项目部署到tomcat中。
至此项目错误全部解决,应该可以看到运行结果。
5.新建Servert测试
新建一个Servlet,HomeServlet,Eclipse会自动在web.xml中加入配置信息,HomeServet.java和web.xml的部分代码如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); }
/** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.println("<h1>BAE Servlet Test.</h1>"); }
web.xml中部分代码
<servlet> <description></description> <display-name>HomeServlet</display-name> <servlet-name>HomeServlet</servlet-name> <servlet-class>com.qiyadeng.HomeServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HomeServlet</servlet-name> <url-pattern>/HomeServlet</url-pattern> </servlet-mapping> 运行tomcat,可以看到如下运行成功,这样你就可以像一般的Java Web Project一样进行开发。
6.最后
BAE中Java环境中百度使用的Jetty,而不是tomcat,Jetty的好处是不需要频繁的重启,修改的代码即时就可看到运行结果。
通过SVN提交代码到BAE,如果有需要做小的修改,可以通过百度的在线编辑工具直接修改。
原创文章,转载请注明: 转载自http://www.qiyadeng.com/
本文链接地址: 百度开发者中心BAE新建Java应用
1.申请开发者帐号 首先注册百度的帐号,然后申请成为百度开发者(需要通过手机进行身份证验证)。 2.新建应用 点击菜单中的创建应用,我们目前选择的是Web应用。 应用创建之后,选择左边菜单的云环境,环境类型需要选择JAVA。 并新创建一个版本,输入1作为版本号 3.在百度集成开发环境中开始开发 百度提供了基于Eclipse的插件,由于该插件不能支持最新的Eclipse版本。建议下载百度的一键安装版本。百度文档中介绍了如何使用开发环境,详细请看集成开发环境使用。 打开百度集成开发环境,在Eclipse左下角点击Login to Baidu,使用你的账号登陆。然后点击工具栏中百度Logo,选择Import BAE Project,填入application和version 之后选择Java作为Project Language。 4.解决项目错误 刚导入的BAE project,在Eclipse中会报错。通过problems view可以看到是因为JRE环境配置不正确和Web运行环境设置不正确。 A.右键项目属性--选择JavaBuildPath,在Libraries中选择Add Library,之后再选择JRE System Library。 B.接下来把Java project转换为Java Web Project(Eclipse中Java Project转换为Java web Project),注意如果你的tomcat是6版本的话,请注意选择Dynamic web Module的版本不超过2.5。 设置Web应用的运行环境,在servers view中新建一个tomcat服务器。 C.和A类似,在Java Build Path中加入 Server Runtime,选择Tomcat。 D.修改hello.jsp,在hello.jsp中加入如下代码 <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %>
E.把项目部署到tomcat中。
至此项目错误全部解决,应该可以看到运行结果。
5.新建Servert测试
新建一个Servlet,HomeServlet,Eclipse会自动在web.xml中加入配置信息,HomeServet.java和web.xml的部分代码如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); }
/** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.println("<h1>BAE Servlet Test.</h1>"); }
web.xml中部分代码
<servlet> <description></description> <display-name>HomeServlet</display-name> <servlet-name>HomeServlet</servlet-name> <servlet-class>com.qiyadeng.HomeServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HomeServlet</servlet-name> <url-pattern>/HomeServlet</url-pattern> </servlet-mapping> 运行tomcat,可以看到如下运行成功,这样你就可以像一般的Java Web Project一样进行开发。
6.最后
BAE中Java环境中百度使用的Jetty,而不是tomcat,Jetty的好处是不需要频繁的重启,修改的代码即时就可看到运行结果。
通过SVN提交代码到BAE,如果有需要做小的修改,可以通过百度的在线编辑工具直接修改。
空指针异常(Null Pointer Exception)是我们平时最容易碰到的,也是最令人讨厌的异常。本文介绍如何避免出现空指针异常。 首先我们看如下的示例 private Boolean isFinished(String status) { if (status.equalsIgnoreCase("Finish")) { return Boolean.TRUE; } else { return Boolean.FALSE; } } 如果status的值为空的话,那么将会出现空指针异常(本例第2行)。所以我们应该使用如下的方法
private Boolean isFinished(String status) { if ("Finish".equalsIgnoreCase(status)) { return Boolean.TRUE; } else { return Boolean.FALSE; } } 这样的话,如果status为空,也不会出现空指针异常。相信我们大多数朋友已经知道这样的方法了,如果一个对象可能为null,那么不需要直接调用它的方法。
接下来我将接着提供几种避免空指针的建议。
1.判断Collection是否为空。
2.使用一些判断方法。
3.assert关键字。
4.Assert类。
5.异常处理。
6.太多的点.操作语法。
7.使用StringUtils类
1.判断Collection是否为空
Collection 为空是指Collection中没有元素。一些开发者如果碰到Collection中没有元素的时候,经常return null,更好的做法是,你应该return Collections.EMPTY_LIST,Collections.EMPTY_SET或者是Collections.EMPTY_MAP.
错误的代码
public static List getEmployees() { List list = null; return list; }
正确的代码
public static List getEmployees() { List list = Collections.EMPTY_LIST; return list; }
2.使用一些判断方法
使用一些方法如contains(),indexOf(),isEmpty(),containsKey(),ContainsValue和hasNext()等来判断,确保不存在空值。
示例:
String myName = "qiyadeng"; List list = Collections.EMPTY_LIST; boolean exist = list.contains(myName); int index = list.indexOf(myName); boolean isEmpty =list.isEmpty(); Map map =Collections.EMPTY_MAP; exist=map.containsKey(myName); exist=map.containsValue(myName); isEmpty=map.isEmpty(); Set set=Collections.EMPTY_SET; exist=set.contains(myName); isEmpty=set.isEmpty(); Iterator iterator; exist = iterator.hasNext();
3.assert关键字
在Java1.4版本之后,提供了断言assert来确定你的代码中的假设。使用的语法如下:
expression1是一个boolean表达式,如果expression1返回的false,系统将会抛出AssertError(没有详细信息)。
另外一种使用方法
assert expression1:expression2 如果expression1返回false,那么系统将会抛出AssertError,并且详细信息为expression2。
示例:
public static String getManager(String employeeId) { assert (employeeId != null) : "employeeId must be not null"; return "qiyadeng"; } 我使用getManager(null)来调用getManger方法,最后运行的结果是"java.lang.AssertionError:employeedId must be not null"
注意记得使用java选项中加入-enableassertion开启assertion功能。
4.Assert类
Assert类在com.bea.core.repackaged.springframework.util包中,有许多方法可以用于断言。
public static String getManager(String employeeId) { Assert.notNull(employeeId, "employeeId must be not null"); Assert.hasLength(employeeId, "employeeId must has length greater than 0"); return "qiyadeng"; } 当我同样使用getManager(null)来调用getManager方法,将获得信息"java.lang.IllegalArgumentException: employeeId must be not null"。
5.异常处理
使用try catch处理异常或是检查变量是否为空。
public static String getManager(String employeeId) { return null; } 如上代码,我使用下面方法调用
String managerId = getManager("A015"); System.out.println(managerId.toString()); 将会发生"java.lang.NullPointerException",为了处理这个异常,我们应该使用try catch来处理异常或者是检查变量是否为null。
try-catch方法
String managerId = getManager("A015"); try { System.out.println(managerId.toString()); } catch (NullPointerException npe) { //write your code here } 或者是对变量进行检查
String managerId = getManager("A015"); if (managerId != null) { System.out.println(managerId.toString()); } else { //write your code here }
6.不要太多的点.操作语法
一些开发者使用太多的这样的方法来减少代码,但是这个对后面的维护和异常处理都是不太好的。
错误的写法
String attrValue = (String)findViewObject("VO_NAME").getCurrentRow().getAttribute("Attribute_NAME"); 正确的写法
ViewObject vo = findViewObject("VO_NAME"); Row row = vo.getCurrentRow(); String attrValue = (String)row.getAttribute("Attribute_NAME");
7.使用StringUtils类
StringUtil是org.apache.commns.lang包中的类,我们可以使用该类来避免空指针异常。
例如 StringUtils.isEmpty(),StringUtils.isBlank,StringUtils.equals()等等,更多的你可以参考文档。
为了不出现空指针异常,在写代码的过程中需要时刻检查你的代码是否会抛出NullPointerException,如果你没有时间及时调整的话,使用//TODO标记,便于你后面解决问题。
现在经常需要根据用户提供的位置,提供一些和位置相关的信息。有时可以直接确定用户的经度和纬度,有时不一定可以确定用户的经度和纬度信息,用户是 通过输入一些路名、标志性建筑或是商场名等位置,但是我们的数据库可能并没有存法用户可能输入的这些位置信息的经度纬度,这时候可以使用一些地图提供的 API来确定,用户所输入的位置信息的经度和纬度。 我们使用百度地图提供的GeoCoding API实现从位置信息到经度纬度的转换,详细的使用说明可以参考 GeoCoding API。我们这里做一个简单的演示 public String getGeoCode(String query) throws ClientProtocolException, IOException{ HttpClient httpClient = new DefaultHttpClient(); String url = geoCodeRequestUrl(query); logger.log(Level.INFO, url); HttpGet httpget = new HttpGet(url); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String responseBody = httpClient.execute(httpget, responseHandler);//百度返回的经度纬度信息xml logger.log(Level.INFO,"baidu response:"+responseBody); return responseBody; } public String geoCodeRequestUrl(String query) throws UnsupportedEncodingException{ String url = WeChatConstant.BASEURL + "geocoder?address=" + URLEncoder.encode(query,"UTF-8") + "&key=" + WeChatConstant.MAPKEY + "&output=" + WeChatConstant.OUTPUTFORMAT; return url; } 使用JUnit进行测试 @Test public void testGeoCode() throws Exception { BaiduMapService bms = new BaiduMapService(); String response = bms.getGeoCode("上地十街十号"); BaiduGeoCodeResponse res = BaiduGeoCodeResponse.getBaiduGeoCode(response);//解析xml System.out.println(res.toString()); } 输出的结果 <GeocoderSearchResponse> <status>OK</status> <result> <location> <lat>40.057098</lat> <lng>116.307175</lng> </location> <precise>1</precise> <confidence>80</confidence> <level>道路</level> </result> </GeocoderSearchResponse> BaiduGeoCodeResponse [lat=40.057098, lng=116.307175]
到了一个较陌生的环境,经常会在周边找一些基础设施,比如银行,商场,餐厅等(还有一种更急切的是找厕所)。通过百度提供的地图API,可以在你的应用中简单做到,详情可阅读 Place API。我们以查找周边银行作为示例,需确定的参数至少有三个,要查找的位置的经度和纬度,需要查找的内容的类型或是关键字。 public String getPalace(String query,String lat,String lng) throws ClientProtocolException, IOException{ HttpClient httpClient = new DefaultHttpClient(); String url = palceRequestUrl(query,lat,lng); logger.log(Level.INFO, url); HttpGet httpget = new HttpGet(url); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String responseBody = httpClient.execute(httpget, responseHandler);//位置xml logger.log(Level.INFO,"baidu response:"+responseBody); return responseBody; } public String palceRequestUrl(String query,String lat,String lng) throws UnsupportedEncodingException { String url = WeChatConstant.BASEURL + "place/search?query=" + URLEncoder.encode(query,"UTF-8") + "&key=" + WeChatConstant.MAPKEY +"&location="+lat+","+lng +"&radius=2000"+"&output=" + WeChatConstant.OUTPUTFORMAT; return url; } Junit测试 @Test public void testGetBaiduPlace() throws Exception{ BaiduMapService bms = new BaiduMapService(); String response = bms.getPalace("银行", "39.915", "116.404"); List<BaiduPlaceResponse> list = BaiduPlaceResponse.getBaiduPlace(response); for(BaiduPlaceResponse res:list){ System.out.println(res.toString()); } } 输出内容(省略部分内容) <?xml version="1.0" encoding="utf-8" ?> <PlaceSearchResponse> <status>OK</status> <results> <result> <name>中国工商银行东长安街支行</name> <location> <lat>39.915891</lat> <lng>116.41867</lng> </location> <address>东城区东长安街1号东方广场西三办公楼1楼</address> <uid>a025683c73033c35a21de987</uid> <detail_url>http://api.map.baidu.com/place/detail?uid=a025683c73033c35a21de987&amp;output=html&amp;source=placeapi</detail_url> <tag>银行,王府井/东单</tag> </result> </results> </PlaceSearchResponse> BaiduPlaceResponse [name=中国工商银行东长安街支行, telephone=null, address=东城区东长安街1号东方广场西三办公楼1楼, lat=39.915891, lng=116.41867, tag=null, detailUrl=http://api.map.baidu.com/place/detail?uid=a025683c73033c35a21de987&output=html&source=placeapi]
位置识别这是实际应用经常应用的消息,特别是很多商家,通过了解用户位置,给用户提供特别的产品或是商场的推荐。其中用户可能发送两种类型的消息: 1.微信地理位置信息 2.路名、标志性建筑或是商场名称 1.微信地理位置消息认识一下,微信地理位置消息,包含一些什么信息 <xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1351776360</CreateTime> <MsgType><![CDATA[location]]></MsgType> <Location_X>23.134521</Location_X> <Location_Y>113.358803</Location_Y> <Scale>20</Scale> <Label><![CDATA[位置信息]]></Label> <MsgId>1234567890123456</MsgId> </xml>
包含的主要信息有经度纬度和Label的位置。可以根据label中描述的位置信息,提供给用户对应的服务。也可根据用户的经度纬度信息,提供你最近的产品或是有地域性的产品。 首先根据微信的地理位置信息,定义WeChatLocationMessage类,并能把Xml转换为WeChatLocationMessage对象 public class WeChatLocationMessage { private String toUserName; private String fromUserName; private String createTime; private String msgType; private String locationx; private String localtiony; private String scale; private String label; private String msgId; public static WeChatLocationMessage getWeChatLocationMessage(String xml){ XStream xstream = new XStream(new DomDriver()); WeChatLocationMessage message = null; xstream.alias("xml", WeChatLocationMessage.class); xstream.aliasField("ToUserName", WeChatLocationMessage.class, "toUserName"); xstream.aliasField("FromUserName", WeChatLocationMessage.class, "fromUserName"); xstream.aliasField("CreateTime", WeChatLocationMessage.class, "createTime"); xstream.aliasField("MsgType", WeChatLocationMessage.class, "msgType"); xstream.aliasField("Location_X", WeChatLocationMessage.class, "locationx"); xstream.aliasField("Location_Y", WeChatLocationMessage.class, "localtiony"); xstream.aliasField("Scale", WeChatLocationMessage.class, "scale"); xstream.aliasField("Label", WeChatLocationMessage.class, "label"); xstream.aliasField("MsgId", WeChatLocationMessage.class, "msgId"); message = (WeChatLocationMessage)xstream.fromXML(xml); return message; } //getter and setter } 本文利用百度的地图API,查找最近的银行做为示例。 public String getPalace(String query,String lat,String lng) throws ClientProtocolException, IOException{ HttpClient httpClient = new DefaultHttpClient(); String url = palceRequestUrl(query,lat,lng); logger.log(Level.INFO, url); HttpGet httpget = new HttpGet(url); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String responseBody = httpClient.execute(httpget, responseHandler); logger.log(Level.INFO,"baidu response:"+responseBody); return responseBody; } public String palceRequestUrl(String query,String lat,String lng) throws UnsupportedEncodingException { String url = WeChatConstant.BASEURL + "place/search?query=" + URLEncoder.encode(query,"UTF-8") + "&key=" + WeChatConstant.MAPKEY +"&location="+lat+","+lng +"&radius=2000"+"&output=" + WeChatConstant.OUTPUTFORMAT; return url; } 输出的结果 <PlaceSearchResponse> <status>OK</status> <results> <result> <name>中国工商银行东长安街支行</name> <location> <lat>39.915891</lat> <lng>116.41867</lng> </location> <address>东城区东长安街1号东方广场西三办公楼1楼</address> <uid>a025683c73033c35a21de987</uid> <detail_url>http://api.map.baidu.com/place/detail?uid=a025683c73033c35a21de987&amp;output=html&amp;source=placeapi </detail_url> <tag>银行,王府井/东单</tag>
</result> </results> </PlaceSearchResponse> 接下来,把百度地图反映出来的最近位置信息,以图文消息的格式展示给微信用户 public static String getWeChatReplyNewsMessageByBaiduPlace(List<BaiduPlaceResponse> placeList, double lat, double lng,String userName, int size){ WeChatReplyNewsMessage newsMessage = new WeChatReplyNewsMessage(); List<Item> items = new ArrayList<Item>(); StringBuffer strBuf = new StringBuffer(); logger.log(Level.INFO,"placeList count="+placeList.size()); newsMessage.setItems(items); if(placeList.size()>size){ newsMessage.setArticleCount(size); } else{ newsMessage.setArticleCount(placeList.size()); } logger.log(Level.INFO,"article count="+newsMessage.getArticleCount()); newsMessage.setCreateTime(new Date().getTime()+""); newsMessage.setMsgType("news"); newsMessage.setFuncFlag("0"); newsMessage.setToUserName(userName); newsMessage.setFromUserName(WeChatConstant.FROMUSERNAME); for(int i = 0;i <newsMessage.getArticleCount();i++){ BaiduPlaceResponse place = placeList.get(i); Double distance = GeoUtil.DistanceOfTwoPoints(Double.valueOf(place.getLng()), Double.valueOf(place.getLat()), lng, lat, GaussSphere.Beijing54); Item item = new Item(); item.setTitle(place.getName()+"["+distance+"米]"+"\n"+place.getAddress()+"\n"+place.getTelephone()); item.setPicUrl(""); item.setUrl(place.getDetailUrl()); item.setDescription(""); items.add(item); } logger.log(Level.INFO,"newMessage="+newsMessage.toString()); strBuf = strBuf.append(getWeChatNewsMessage(newsMessage)); return strBuf.toString(); } public static String getWeChatNewsMessage(WeChatReplyNewsMessage newsMessage){ XStream xstream = new XStream(new DomDriver()); xstream.alias("xml", WeChatReplyNewsMessage.class); xstream.aliasField("ToUserName", WeChatReplyNewsMessage.class, "toUserName"); xstream.aliasField("FromUserName", WeChatReplyNewsMessage.class, "fromUserName"); xstream.aliasField("CreateTime", WeChatReplyNewsMessage.class, "createTime"); xstream.aliasField("MsgType", WeChatReplyNewsMessage.class, "msgType"); xstream.aliasField("ArticleCount", WeChatReplyNewsMessage.class, "articleCount"); xstream.aliasField("Content", WeChatReplyNewsMessage.class, "content"); xstream.aliasField("FuncFlag", WeChatReplyNewsMessage.class, "funcFlag"); xstream.aliasField("Articles", WeChatReplyNewsMessage.class, "items"); xstream.alias("item", Item.class); xstream.aliasField("Title", Item.class, "title"); xstream.aliasField("Description", Item.class, "description"); xstream.aliasField("PicUrl", Item.class, "picUrl"); xstream.aliasField("Url", Item.class, "url"); return xstream.toXML(newsMessage); } 2.路名、标志性建筑或是商场名称对路名、标志性建筑等信息,方法还是通过第三方地图信息,确定输入的位置信息的经度纬度。 本文使用百度地图API,确定所查找的位置的经度和纬度。 public String getGeoCode(String query) throws ClientProtocolException, IOException{ HttpClient httpClient = new DefaultHttpClient(); String url = geoCodeRequestUrl(query); logger.log(Level.INFO, url); HttpGet httpget = new HttpGet(url); ResponseHandler<String> responseHandler = new BasicResponseHandler(); String responseBody = httpClient.execute(httpget, responseHandler); logger.log(Level.INFO,"baidu response:"+responseBody); return responseBody; } public String geoCodeRequestUrl(String query) throws UnsupportedEncodingException{ String url = WeChatConstant.BASEURL + "geocoder?address=" + URLEncoder.encode(query,"UTF-8") + "&key=" + WeChatConstant.MAPKEY + "&output=" + WeChatConstant.OUTPUTFORMAT; return url; } 确定了经度和纬度,问题就变成和第1种消息类型一致了,根据经度纬度去做相应处理。 3.源代码本文的代码较长,提供源代码下载。 WeChatDemo下载
本文介绍,如果把Java Project转换为Java Web Project,应该在多数的Eclipse的版本都类似。 1.Java Project一个Java Projec,在Eclipse中显示的是一个“J”的蓝色文件夹。 2.Project Facets右键项目属性Properties,右侧选择菜单Project Facets,点击converted to faceted form... 勾选dynamic web module 选择下面的further configuration available 项目中的Web目录和设置保持一致。 3.Java Web Project这样你就转换到了Java Web Project。
1.新建Project 新建Java Project,并把mongo-java-driver驱动加入到项目bulid path中,如果你使用的是maven增加依赖。 <dependency> <groupId>org.mongodb</groupId> <artifactId>mongo-java-driver</artifactId> <version>2.10.1</version> </dependency> 2.连接上MongoDB //>2.10版本 MongoClient mongo = new MongoClient( "localhost" , 27017 );
//老版本 Mongo mongo = new Mongo("localhost", 27017); 如果需要验证,需要输入用户名和密码 MongoClient mongoClient = new MongoClient(); DB db = mongoClient.getDB("database name"); boolean auth = db.authenticate("username", "password".toCharArray()); 3.MongoDB数据库 得到MongoDB中的数据库,如果数据库名不存在,MongoDB会自动创建 DB db = mongo.getDB("database name"); 显示所有的数据库 List<String> dbs = mongo.getDatabaseNames(); for(String db : dbs){ System.out.println(db); } 4.MongoDB Collection(MongoDB表) 得到数据库中的表 DB db = mongo.getDB("testdb"); DBCollection table = db.getCollection("user"); 显示数据库中的所有表 DB db = mongo.getDB("testdb"); Set<String> tables = db.getCollectionNames(); for(String coll : tables){ System.out.println(coll); } 5.插入、查找、更新、删除操作 插入数据,向Collection(表)中插入一个Document DBCollection table = db.getCollection("user"); BasicDBObject document = new BasicDBObject(); document.put("name", "qiyadeng"); document.put("age", 30); document.put("createdDate", new Date()); table.insert(document); 更新Document中的name="qiyadeng.com" DBCollection table = db.getCollection("user");
BasicDBObject query = new BasicDBObject(); query.put("name", "qiyadeng");
BasicDBObject newDocument = new BasicDBObject(); newDocument.put("name", "qiyadeng.com");
BasicDBObject updateObj = new BasicDBObject(); updateObj.put("$set", newDocument); table.update(query, updateObj); 从Collection中查找name="qiyadeng.com"的Document DBCollection table = db.getCollection("user"); BasicDBObject searchQuery = new BasicDBObject(); searchQuery.put("name", "qiyadeng.com"); DBCursor cursor = table.find(searchQuery); while (cursor.hasNext()) { System.out.println(cursor.next()); } 删除name="qiyadeng"的Document DBCollection table = db.getCollection("user"); BasicDBObject searchQuery = new BasicDBObject(); searchQuery.put("name", "qiyadeng.com"); table.remove(searchQuery);
6.完整的例子 package com.qiyadeng.mongodb;
import java.util.Date;
import com.mongodb.BasicDBObject; import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.MongoClient;
public class MongoDBSample {
public static void main(String[] args) throws Exception{ /**** Connect to MongoDB ****/ //2.10.0后,使用MongoClient MongoClient mongo = new MongoClient("localhost", 27017); /**** Get database ****/ // if database doesn't exists, MongoDB will create it for you DB db = mongo.getDB("testdb"); /**** Get collection / table from 'testdb' ****/ // if collection doesn't exists, MongoDB will create it for you DBCollection table = db.getCollection("user"); /**** Insert ****/ // create a document to store key and value BasicDBObject document = new BasicDBObject(); document.put("name", "qiyadeng"); document.put("age", 30); document.put("createdDate", new Date()); table.insert(document); /**** Find and display ****/ BasicDBObject searchQuery = new BasicDBObject(); searchQuery.put("name", "qiyadeng"); DBCursor cursor = table.find(searchQuery); while (cursor.hasNext()) { System.out.println(cursor.next()); } /**** Update ****/ // search document where name="qiyadeng" and update it with new values BasicDBObject query = new BasicDBObject(); query.put("name", "qiyadeng"); BasicDBObject newDocument = new BasicDBObject(); newDocument.put("name", "qiyadeng.com"); BasicDBObject updateObj = new BasicDBObject(); updateObj.put("$set", newDocument); table.update(query, updateObj); /**** Find and display ****/ BasicDBObject searchQuery2 = new BasicDBObject().append("name", "qiyadeng.com"); DBCursor cursor2 = table.find(searchQuery2); while (cursor2.hasNext()) { System.out.println(cursor2.next()); } } }
输出 { "_id" : { "$oid" : "51444c88874c79654063356b"} , "name" : "qiyadeng" , "age" : 30 , "createdDate" : { "$date" : "2013-03-16T10:42:16.555Z"}} { "_id" : { "$oid" : "51444c88874c79654063356b"} , "age" : 30 , "createdDate" : { "$date" : "2013-03-16T10:42:16.555Z"} , "name" : "qiyadeng.com"}
使用mongo验证创建的数据库testdb,collection user是否存在。
本文介绍如何安装在windows 7中安装MongoDB。 注:MongoDB并不像Windows上安装其他软件,只需要下载Zip包并解压,然后配置数据存放目录并启动即可。 1.下载MongoDB从MongoDB官方网站,根据你的平台选择对应的windows的压缩包并解压,本文解压到D:\mongodb\。
注:如果需要在命令行中快速使用MongoDB bin目录下的命令,可以将D:\mongoDB\bin加入到Window环境变量。 2.配置数据文件在D:\mongodb\创建mongo.config文件,如下(并在d:\mongodb目录下新建data,log文件夹) ##数据存储的位置 dbpath=D:\mongodb\data ##所有的输出位置 logpath=D:\mongodb\log\mongo.log ##日志读写操作 diaglog=3 3.运行MongoDB Server在命令控制行,切换到d:\mongodb\bin目录下,使用命令mongod.exe --config d:\mongdb\mongo.config启动MongoDb Server。 D:\mongodb\bin>mongod.exe --config d:\mongodb\mongo.config all output going to: D:\mongodb\log\mongo.log 4.连接MongoDB新开启一个命令行控制窗口,使用mongo.exe连接MongoDB Server. 5.设置MongoDB为Windows服务在命令行控制窗口,加入--install选项可以把MongoDB安装为Windows服务。 D:\mongodb\bin>mongod.exe --config d:\mongodb\mongo.config 启动MongoDB的命令为:net start MongoDB 停车MongODB的命令为:net stop MongoDB 删除MongoDB的命令为:mongod --remove
1.设置成为开发者模式
登录微信工作平台,选择高级功能-进入开发模式,成为开发者。需要做如下图配置。URL配置的信息是指,微信的后台服务器把您的用户消息发送到该URL处理。Token是你和微信之间的一个密码,用来验证消息是否是从微信的服务发送而来,而不是其他来攻击你的系统。
现在你还不能设置,在设置时微信会GET请求你设置的URL,已检测接口是否可以使用。只有等你准备好GET方法之后才可以进行设置。
2.实现GET方法
从文档中知道,我们需要实现POST和GET方法,GET方法用于验证微信和你的通讯验证,POST用于消息处理。
新建Servlet HelloWeChat,先实现其中的GET方法
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO 为了简单起见,先不对消息来源进行校验
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
String echo = request.getParameter("echostr");
echo = new String(echo.getBytes("ISO-8859-1"),"UTF-8");
pw.println(echo);
}
可以在本地使用http://localhost:8080/QiyadengWeb/HelloWeChat?echostr=hello中文,先进行测试,如果没有问题,可以部署到服务器上,然后在微信公众平台进行设置了。
3.实现POST方法
POST方法首先接收到微信公众平台传送过来的XML,从中提取消息发送人和消息内容。更加消息发送内容,你可以增加自己的处理逻辑,最后拼装成回复消息XML,返回给微信公众平台。
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
String wxMsgXml = IOUtils.toString(request.getInputStream(),"utf-8");
WeChatTextMessage textMsg = null;
try {
textMsg = getWeChatTextMessage(wxMsgXml);
} catch (Exception e) {
e.printStackTrace();
}
StringBuffer replyMsg = new StringBuffer();
if(textMsg != null){
//增加你所需要的处理逻辑,这里只是简单重复消息
replyMsg.append("您给我的消息是:");
replyMsg.append(textMsg.getContent());
}
else{
replyMsg.append(":)不是文本的消息,我暂时看不懂");
}
String returnXml = getReplyTextMessage(replyMsg.toString(), textMsg.getFromUserName());
pw.println(returnXml);
}
关于调试,这里推荐一个工具Fiddler,你可以模拟微信的POST消息到你的本地,而不必每次部署到服务器上进行调试。关于Fiddler的POST数据使用方法,可以参考下图标注内容。
4.部署并测试
完成第一步,并和你的公众帐号好进行对话,回复消息没有问题的话,那就恭喜你了。
5.依赖库
使用maven的同学,添加以下依赖即可。非maven用户,找到这些库添加到buider path中即可。
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.3</version>
</dependency>
6.完整的代码
package com.qiyadeng.wechat;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
/**
* Servlet implementation class HelloWeChat
*/
public class HelloWeChat extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public HelloWeChat() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO 为了简单起见,先不对消息来源进行校验
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
String echo = request.getParameter("echostr");
echo = new String(echo.getBytes("ISO-8859-1"),"UTF-8");
pw.println(echo);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter pw = response.getWriter();
String wxMsgXml = IOUtils.toString(request.getInputStream(),"utf-8");
WeChatTextMessage textMsg = null;
try {
textMsg = getWeChatTextMessage(wxMsgXml);
} catch (Exception e) {
e.printStackTrace();
}
StringBuffer replyMsg = new StringBuffer();
if(textMsg != null){
//增加你所需要的处理逻辑,这里只是简单重复消息
replyMsg.append("您给我的消息是:");
replyMsg.append(textMsg.getContent());
}
else{
replyMsg.append(":)不是文本的消息,我暂时看不懂");
}
String returnXml = getReplyTextMessage(replyMsg.toString(), textMsg.getFromUserName());
pw.println(returnXml);
}
private WeChatTextMessage getWeChatTextMessage(String xml){
XStream xstream = new XStream(new DomDriver());
xstream.alias("xml", WeChatTextMessage.class);
xstream.aliasField("ToUserName", WeChatTextMessage.class, "toUserName");
xstream.aliasField("FromUserName", WeChatTextMessage.class, "fromUserName");
xstream.aliasField("CreateTime", WeChatTextMessage.class, "createTime");
xstream.aliasField("MsgType", WeChatTextMessage.class, "messageType");
xstream.aliasField("Content", WeChatTextMessage.class, "content");
xstream.aliasField("MsgId", WeChatTextMessage.class, "msgId");
WeChatTextMessage wechatTextMessage = (WeChatTextMessage)xstream.fromXML(xml);
return wechatTextMessage;
}
private String getReplyTextMessage(String content, String weChatUser){
WeChatReplyTextMessage we = new WeChatReplyTextMessage();
we.setMessageType("text");
we.setFuncFlag("0");
we.setCreateTime(new Long(new Date().getTime()).toString());
we.setContent(content);
we.setToUserName(weChatUser);
we.setFromUserName("shanghaiweather");//TODO 你的公众帐号微信号
XStream xstream = new XStream(new DomDriver());
xstream.alias("xml", WeChatReplyTextMessage.class);
xstream.aliasField("ToUserName", WeChatReplyTextMessage.class, "toUserName");
xstream.aliasField("FromUserName", WeChatReplyTextMessage.class, "fromUserName");
xstream.aliasField("CreateTime", WeChatReplyTextMessage.class, "createTime");
xstream.aliasField("MsgType", WeChatReplyTextMessage.class, "messageType");
xstream.aliasField("Content", WeChatReplyTextMessage.class, "content");
xstream.aliasField("FuncFlag", WeChatReplyTextMessage.class, "funcFlag");
String xml =xstream.toXML(we);
return xml;
}
}
开始微信公众平台的开发,我们首先要了解微信平台可以帮助我们做哪些事情? 使用您的公众账号登陆http://mp.weixin.qq.com/,选择菜单--高级功能-开发模式--查看文档,即能看到微信公众平台目前所能开发的功能。 一、通讯机制 公众平台的主要内容是 - 接受用户发送给您公众账号的消息
- 给您的用户回复消息
需要特别说明的是,发送消息和回复消失是一个连贯的过程,只能在一个对话中完成。也就是说您的用户不找您说话,您是不能主动发送消息给你的客户(群发是另外一种情况,有次数限制。你也可以申请付费使用微信CRM平台)。所有的发送消息和接受消息,都需要微信平台进行中转。 二、消息类型 下面介绍用户能给您发送的消息类型,也就是目前接受到的消息类型。 1.接受消息类型 1.1文本消息: 这也是我们平时碰到最多的,可以根据文本中提到的一些关键字,进行判断,判断用户的含义,并进行回复。 1.2图片消息: 目前通过图片理解用户想表达的意思,还是有较大难度,因此多数的公众账号,会选择忽略图片信息或选择由人工来处理。只能说一句:图片很美,但是我看不懂。 1.3地理位置消息: 用户把他的位置发给您,这对大多数公众账号来说,是一个重要的信息。可以提供一些基于位置信息的服务,比如酒店预订公众账号,可以给你推荐你周边的酒店。 另外一个补充是,可以在文本消息中分析出位置信息,并加以利用。比如用户输入“南京路步行街”,可以提供用户南京路步行街的相关商户。 1.4链接消息: 目前还没有看到开发模式中特别有效的使用方法。使用比较多的可能会是购物时或是咨询时,对所谈论的对象进行明确。 1.5事件推送消息: 当用户进入到和你对话的过程中,可以先和用户打招呼等。这个消息目前只支持4.5版本,且暂时还没有开发。后续可想想的空间很大,比如用户进入到会话之后,摇一摇会发生什么呢? 2.回复消息类型 2.1文本消息 这是我们平时发送最多的一类消息,当只需要简单的文字即可回答用户的消息时,可用文本消息。文本消息中可以带有链接地址。 2.2图文消息 图文消息,这是我们在推送消息中经常看到的消息格式。每项内容可以点击查看更详细信息(当然你也可以把链接设置为空,使其不能跳转) 2.3音乐消息 在你的答复中给用户一个语音消息或是音乐,可以获得不少用户的亲睐。 了解了公众平台的通讯机制和消息类型,接下来,我们开始准备开发环境了。
在类似PHP的项目中,经常会碰到项目开发目录和运行目录不是一个目录的情况。 在window7下面有一个非常好的工具,可以做到自动同步。 如: mklink /J D:\wamp\www\shanghaisales C:\Users\Admin\git\ShanghaiSales\ShanghaiSales
本文中演示如何通过URLConnection获取Http响应Header信息 1.从响应中获得Header信息 URL obj = new URL("http://www.qiyadeng.com"); URLConnection conn = obj.openConnection(); Map<String, List<String>> map = conn.getHeaderFields(); 2.从响应Header中获取Server信息 Map<String, List<String>> map = conn.getHeaderFields(); List<String> server = map.get("Server"); 完整的示例 package com.qiyadeng.http;
import java.net.URL; import java.net.URLConnection; import java.util.List; import java.util.Map; public class GetHttpResponseHeader { public static void main(String[] args) { try { URL obj = new URL("http://www.qiyadeng.com"); URLConnection conn = obj.openConnection(); Map<String, List<String>> map = conn.getHeaderFields(); System.out.println("显示响应Header信息\n"); for (Map.Entry<String, List<String>> entry : map.entrySet()) { System.out.println("Key : " + entry.getKey() + " ,Value : " + entry.getValue()); } System.out.println("\n使用key获得响应Header信息 \n"); List<String> server = map.get("Server"); if (server == null) { System.out.println("Key 'Server' is not found!"); } else { for (String values : server) { System.out.println(values); } } } catch (Exception e) { e.printStackTrace(); } } } 输出 显示响应Header信息...
Key : null ,Value : [HTTP/1.1 200 OK] Key : X-Pingback ,Value : [http://www.qiyadeng.com/xmlrpc.php] Key : Date ,Value : [Sun, 10 Mar 2013 12:16:26 GMT] Key : Transfer-Encoding ,Value : [chunked] Key : Connection ,Value : [close] Key : Content-Type ,Value : [text/html; charset=UTF-8] Key : Server ,Value : [Apache/2.2.3 (CentOS)] Key : X-Powered-By ,Value : [PHP/5.2.17]
使用key获得响应Header信息 ...
Apache/2.2.3 (CentOS)
在.net中也有非常多的日志工具,今天介绍下NLog。NLog特别好的地方就是和Vs(Visual Studio)开发环境的集成。 只需下载(下载地址)安装包,安装之后NLog就会在VS的新建项中增加很多选项,并且在编辑NLog配置文件时也会提供智能提示和校验。 NLog工作主要依赖的是两个文件一个是NLog.dll,另外一个是NLog.config,解下来演示下如何引入和进行配置 1.在你的项目中加入NLog。右击项目,选择添加新项目,选择Empty NLog Configuration,并选择添加(如图)。 (说明:有可能不像官网上说的在NLog的目录下面,在ASP.net Web项目中,会在VB的目录中。) 在非Asp.net项目中,记得把NLog.config文件复制到输出目录(右击NLog.config文件属性)。 2.编辑配置文件NLog.config. 关于配置文件如何编辑有大量的篇幅(https://github.com/nlog/nlog/wiki/Configuration-file),我们这里介绍两种常用的场景。 A)在Vs的输出窗口输出日志,关于这些变量的说明${},请参看文档Configuration Reference。(https://github.com/nlog/nlog/wiki) <target name="debugger" xsi:type="Debugger" layout="${logger}::${message}" />
B)以文件形式输出。 <target name="file" xsi:type="File" maxArchiveFiles="30"
layout="${longdate} ${logger} ${message}"
fileName="${basedir}/logs/log${shortdate}.txt"
keepFileOpen="false" />
完整的配置文件例子: <?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" throwExceptions="true" internalLogFile="d:\internal_log_file.txt" internalLogLevel="Trace" internalLogToConsole="true">
<targets>
<target name="debugger" xsi:type="Debugger" layout="${logger}::${message}" />
<target name="file" xsi:type="File" maxArchiveFiles="30"
layout="${longdate} ${logger} ${message}"
fileName="${basedir}/logs/log${shortdate}.txt"
keepFileOpen="false" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="debugger" />
<logger name="*" minlevel="Trace" writeTo="file" />
</rules>
</nlog>
3.在程序中使用NLog 在程序中使用就特别简单了,和大多数日志工具类似。 using NLog; namespace MyNamespace { public class MyClass { private static Logger logger = LogManager.GetCurrentClassLogger(); } }
本文演示如何使用Collections.frequency和Map来计算重复项出现的次数。(Collections.frequency在JDK 1.5版本以后支持)
package com.qiyadeng.core;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class CountDuplicatedList {
public static void main(String[] args) {
List list = new ArrayList();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("b");
list.add("c");
list.add("a");
list.add("a");
list.add("a");
System.out.println("\n例子1 - 计算'a'出现的次数");
System.out.println("a : " + Collections.frequency(list, "a"));
System.out.println("\n例子2 - 计算所有对象出现的次数");
Set uniqueSet = new HashSet(list);
for (String temp : uniqueSet) {
System.out.println(temp + ": " + Collections.frequency(list, temp));
}
System.out.println("\n例子3 -用Map来计算对象出现的次数");
Map map = new HashMap();
for (String temp : list) {
Integer count = map.get(temp);
map.put(temp, (count == null) ? 1 : count + 1);
}
printMap(map);
System.out.println("\nMap排序-以key排序");
Map treeMap = new TreeMap(map);
printMap(treeMap);
}
public static void printMap(Map map) {
for (Map.Entry entry : map.entrySet()) {
System.out.println("Key-value : " + entry.getKey() + "- "
+ entry.getValue());
}
}
}
输出结果
例子1 - 计算'a'出现的次数
a : 4
例子2 - 计算所有对象出现的次数
d: 1
b: 2
c: 2
a: 4
例子3 -用Map来计算对象出现的次数
Key-value : d- 1
Key-value : b- 2
Key-value : c- 2
Key-value : a- 4
Map排序-以key排序
Key-value : a- 4
Key-value : b- 2
Key-value : c- 2
Key-value : d- 1
抓紧年前的两天时间,自己开发了一个微信天气预报的自定义接口。做微信公众平台的开发已经不是第一次,这也算是自娱自乐,第一个版本不需要太注重外观,主要注重天气信息。 微信号(shanghaiweather):上海天气预报- 2692296679
这篇文章将使用两个例子计算两个日期的时间差。
1.使用Java SDK。
2.使用Joda库。
1.使用Java SDK
计算两个Date之间的时间差,基本思路为把Date转换为ms(微秒),然后计算两个微秒时间差。时间的兑换规则如下:
1s秒 = 1000ms毫秒 1min分种 = 60s秒 1hours小时 = 60min分钟 1day天 = 24hours小时
|
package com.qiyadeng.date;
import java.text.SimpleDateFormat; import java.util.Date;
public class DateDifferentExample { public static void main(String[] args) { String dateStart = "2013-02-19 09:29:58"; String dateStop = "2013-02-20 11:31:48";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d1 = null; Date d2 = null;
try { d1 = format.parse(dateStart); d2 = format.parse(dateStop);
//毫秒ms long diff = d2.getTime() - d1.getTime();
long diffSeconds = diff / 1000 % 60; long diffMinutes = diff / (60 * 1000) % 60; long diffHours = diff / (60 * 60 * 1000) % 24; long diffDays = diff / (24 * 60 * 60 * 1000);
System.out.print("两个时间相差:"); System.out.print(diffDays + " 天, "); System.out.print(diffHours + " 小时, "); System.out.print(diffMinutes + " 分钟, "); System.out.print(diffSeconds + " 秒.");
} catch (Exception e) { e.printStackTrace(); }
} }
运行结果:
两个时间相差:1 天, 2 小时, 1 分钟, 50 秒.
|
2.Joda时间库
package com.qiyadeng.date;
import java.text.SimpleDateFormat; import java.util.Date;
import org.joda.time.DateTime; import org.joda.time.Days; import org.joda.time.Hours; import org.joda.time.Minutes; import org.joda.time.Seconds;
public class JodaDateDifferentExample { public static void main(String[] args) { String dateStart = "2013-02-19 09:29:58"; String dateStop = "2013-02-20 11:31:48";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d1 = null; Date d2 = null;
try { d1 = format.parse(dateStart); d2 = format.parse(dateStop);
DateTime dt1 = new DateTime(d1); DateTime dt2 = new DateTime(d2);
System.out.print("两个时间相差:"); System.out.print(Days.daysBetween(dt1, dt2).getDays() + " 天, "); System.out.print(Hours.hoursBetween(dt1, dt2).getHours() % 24 + " 小时, "); System.out.print(Minutes.minutesBetween(dt1, dt2).getMinutes() % 60 + " 分钟, "); System.out.print(Seconds.secondsBetween(dt1, dt2).getSeconds() % 60 + " 秒.");
} catch (Exception e) { e.printStackTrace(); }
} }
运行结果:
两个时间相差:1 天, 2 小时, 1 分钟, 50 秒.
|
问题: 页面保证是保证的一部分。页面机制我这边采用的是操作结束前不能重做,服务端结束返回信息后,适当条件下(服务器忙)是允许重新操作的。 你说的唯一值保证页面不重复的方法,可以本页面再次提交呢? 解决办法: 哈哈,还是流程图吧,有的时候更容易说清楚问题。应该回答了你的问题。 关于页面(我们叫前端吧)和后台(服务端)在判断中和校验中的关系,我认为的原则应该是: 1.如果前端和后端校验只能选一个的话,选择后端校验。可以都选的话,两者都需要。 2.前端校验可以检查一些格式错误或是基本校验,可以减轻服务器校验负担,也可以让用户更快知道自己错在哪里。 3.后台校验更擅长做的是逻辑校验,数据的格式可能都正确,但是发生的关系不正确或是重复数据,只有通过后台校验才能校验出来。
今天看到关于一个整数表示为n个连续整数和的问题。搜索了下找到如下文章: 还搜索到程序的实现:http://blog.csdn.net/Solstice/archive/2006/09/13/1217700.aspx http://squall.cs.ntou.edu.tw/cprog/Assignments/99Fall/FindGivenSum.html http://blog.chinaunix.net/u2/76292/showart_1359876.html(这个是不正确的) 我也简单的实现了下:(g++编译,主要思路是利用等差数列求和公式n=(a+a+k)*(k+2)/2列举k,找到合适的a) #include <iostream>
#include <math.h>
using namespace std;
int main(){
int n;
cout<<"input n:"<<endl;
cin>>n;
int maxk=(int)sqrt((double)n*2);
int flag = 0;
//cout<<"maxk="<<maxk<<endl;
//for(int i=1;i<maxk+1;i++){
for(int i=maxk+1;i>0;i--){
double a = n/(double)(i+1)-i/(double)2;
//cout<<"a="<<a<<endl;
if(a>0&&a-(int)a==0){
//cout<<i<<endl;
for(int j=0;j<i+1;j++){
cout<<a+j<<" ";
}
cout<<endl;
flag=1;
}
}
if(flag==0){
cout<<"NONE"<<endl;
}
return 0;
}
事实上,媒体也非常认可这两份由专家为总统写作的简历,包括ABCNews,并将他们作为介绍总统候选人的核心提要。像其他的高端工作职位一样,只有更加有力地呈现自己作为候选人的资格,您才能成功获得工作职位。这就像美国的总统竞选一样。 这两份出色的简历,首先是非常简洁。人力资源经理,招聘经理或者猎头顾问没有时间去翻阅每个申请人的详细简历。因此要保持你的简历在1-2页之间,最多不超过2页。要把重点放在你最近15年的工作经历,对于工作经历丰富的高端候选人,你可能还需要对以往的一些工作做出简短的总结性说明。 其次,确保简历的整体布局清晰明白。简历要给人一个良好的视觉吸引力。简历的格式要编排有序。字体大小选择要正确,避免留下太多的空白,也要避免把一页纸塞得太满。整体效果上,简历应该突出高品位和专业化的特点。 这两个人的简历虽然没有得到官方的认可,但是我们认为这两份由专家写作的简历不仅呈现了他们卓越的任职资格,而且突出了他们最优秀的职业成就。这也是简历专家建议候选者首先要在简历中呈现的。因为卓越的任职资格和最优秀的职业成就是简历的核心内容。简历的大部分内容应当呈现你过去或现在的工作职责和“专业经验”标题下的职业成就。典型来讲,职业经验是以逆时序排列,从你最近的职位开始,列出公司名称,雇佣日期,担当的职务,也包括每一个职务你的重点工作内容和职责的简要概述。 最有效的简历,也应该包括每个工作所取得的工作成绩。工作成绩或者工作成就可以通过简要的,针对性的陈述来呈现,要证明你如何增加销售,降低费用,扩大市场份额等。
极光是南北极地区特有的一种大气发光现象。极光在东西方的神话传说中都留下了美丽的身影,现代科学的发展,使人类能够用理性的眼光看待极光,对它作出科学的解释。 长期以来,极光的成因机理未能得到满意的解释。在相当长一段时间内,人们一直认为极光可能是由以下三种原因形成的。一种看法认为极光是地球外面燃起的大火,因为北极区临近地球的边缘,所以能看到这种大火。另一种看法认为,极光是红日西沉以后,透射反照出来的辉光。还有一种看法认为,极地冰雪丰富,它们在白天吸收阳光,贮存起来,到夜晚释放出来,便成了极光。总之,众说纷纭,无一定论。直到20世纪60年代,将地面观测结果与卫星和火箭探测到的资料结合起来研究,才逐步形成了极光的物理性描述。 现在人们认识到,极光一方面与地球高空大气和地磁场的大规模相互作用有关,另一方面又与太阳喷发出来的高速带电粒子流有关,这种粒子流通常称为太阳风。由此可见,形成极光必不可少的条件是大气、磁场和太阳风,缺一不可。具备这三个条件的太阳系其他行星,如木星和水星,它们的周围,也会产生极光,这已被实际观察的事实所证明。 地磁场分布在地球周围,被太阳风包裹着,形成一个棒槌状的胶体,它的科学名称叫做磁层。为了更形象化,我们打这样一个比方。可以把磁层看成一个巨大无比的电视机显像管,它将进入高空大气的太阳风粒子流汇聚成束,聚焦到地磁的极区,极区大气就是显像管的荧光屏,极光则是电视屏幕上移动的图像。但是,这里的电视屏幕却不是 18英寸或 24英寸,而是直径为4000公里的极区高空大气。通常,地面上的观众,在某个地方只能见到画面的l/50。在电视显像管中,电子束击中电视屏幕,因为屏上涂有发光物质,会发射出光,显示成图像。同样,来自空间的电子束,打入极区高空大气层时,会激发大气中的分子和原子,导致发光,人们便见到了极光的图像显示。在电视显像管中,是一对电极和一个电磁铁作用于电子束,产生并形成一种活动的图像。在极光发生时,极光的显示和运动则是由于粒子束受到磁层中电场和磁场变化的调制造成的。 极光不仅是个光学现象,而且是个无线电现象,可以用雷达进行探测研究,它还会辐射出某些无线电波。有人还说,极光能发出各种各样的声音。极光不仅是科学研究的重要课题,它还直接影响到无线电通信,长电缆通信,以及长的管道和电力传送线等许多实用工程项目。极光还可以影响到气候,影响生物学过程。当然,极光也还有许许多多没有解开的谜。 极光被视为自然界中最漂亮的奇观之一。如果我们乘着宇宙飞船,越过地球的南北极上空,从遥远的太空向地球望去,会见到围绕地球磁极存在一个闪闪发亮的光环,这个环就叫做极光卵。由于它们向太阳的一边有点被压扁,而背太阳的一边却稍稍被拉伸,因而呈现出卵一样的形状。极光卵处在连续不断的变化之中,时明时暗,时而向赤道方向伸展,时而又向极点方向收缩。处在午夜部分的光环显得最宽最明亮。长期观测统计结果表明,极光最经常出现的地方是在南北磁纬度67度附近的两个环带状区域内,分别称作南极光区和北极光区。在极光区内差不多每天都会发生极光活动。在极光卵所包围的内部区域,通常叫做极盖区,在该区域内,极光出现的机会反而要比纬度较低的极光区来得少。在中低纬地区,尤其是近赤道区域,很少出现极光,但并不是说压根儿观测不到极光。1958年2月10日夜间的一次特大极光,在热带都能见到,而且显示出鲜艳的红色。这类极光往往与特大的太阳耀斑暴发和强烈的地磁暴有关。 在寒冷的极区,人们举目瞭望夜空,常常见到五光十色,千姿百态,各种各样形状的极光。毫不夸大地说,在世界上简直找不出两个一模一样的极光形体来,从科学研究的角度,人们将极光按其形态特征分成五种:一是底边整齐微微弯曲的圆弧状的极光孤;二是有弯扭折皱的飘带状的极光带;三是如云朵一般的片朵状的极光片;四是面纱一样均匀的帐幔状的极光幔;五是沿磁力线方向的射线状的极光芒。 极光形体的亮度变化也是很大的,从刚刚能看得见的银河星云般的亮度,一直亮到满月时的月亮亮度。在强极光出现时,地面上物体的轮廓都能被照见,甚至会照出物体的影子来。最为动人的当然是极光运动所造成的瞬息万变的奇妙景象。我们形容事物变得快时常说:“眼睛一眨,老母鸡变鸭。”极光可真是这样,翻手为云,覆手为雨,变化莫测,而这一切又往往发生在几秒钟或数分钟之内。极光的运动变化,是自然界这个魔术大师,以天空为舞台上演的一出光的活剧,上下纵横成百上千公里,甚至还存在近万公里长的极光带。这种宏伟壮观的自然景象,好像沾了一点仙气似的,颇具神秘色彩。令人叹为观止的则是极光的色彩,早已不能用五颜六色去描绘。说到底,其本色不外乎是红、绿、紫、蓝、白、黄,可是大自然这一超级画家用出神入化的手法,将深浅浓淡、隐显明暗一搭配、一组合,好家伙,一下子变成了万花筒啦。
看清楚了,这可是154公斤。 这可是我们国家老鼠的强项 “刘翔”
每个Java程序员都要处理异常,异常处理在应用程序中起着重要的作用。在Java世界中,处理异常看上去不是那么简单,不仅仅是在异常发生的地方,优雅的报告异常。 使用ThreadGroup处理异常 ThreadGroup类中在这个时候大部分方法已经作废了,但是该类还是很有用的。ThreadGroup类有一个方法uncaughtException(Thread,Throwable),意思是:当线程组中的线程因为异常而终止时,Java虚拟机调用该方法。这样你可以考虑继承ThreadGroup类来处理你应用中没有捕获的异常。 public class AppSpecificThreadGroup extends ThreadGroup { public void uncaughtException(Thread, Throwable) { // app specific error handling here // ex: if fatal, release resources // show user dialog } } 这样在你的应用启动的时候,把你的线程加入到线程组(Thread group)中。 public class ApplicationMain { public static void main(String[] args) { Runnable r = new ApplicationStarter(args); ThreadGroup g = new AppSpecificThreadGroup(); Thread t = new Thread(g, r); t.start(); } private static void doStart(String[] args) { // start application here... } private static class ApplicationStarter { private String[] args; ApplicationStarter(String[] args) { this.args = args; } public void run() { doStart(args); } } }
使用Java5的Thread.UncaughtExceptionHandler
当认识到ThreadGroup.uncaughtException(Thread,Throwable)方法的巨大作用时,Sun团队在Java5版本站中增强了未捕获的异常处理的功能,引入了Thread.UncaughtExceptionHandler接口,另外这个接口引入了两个新的”Check points”用于处理异常。Thread类有两个属性-uncaughtExceptionHandler和defaultUncaughtExceptionHandler,它们有相应的get/set方法。但是这两个方法是不同的。让我们看看处理异常的过程。
n 使用uncaughtExceptionHandler为当前线程处理异常。
n 如果uncaughtExceptionHandler为空(null),使用线程组(如果用户没有显示设置,线程组是uncaughtExceptionHandler的默认实现)。
n 如果线程组是默认的实现,但是不是根线程组,将把错误处理委派给父线程组。
n 如果线程组是默认的实现,并且是根线程组,将使用线程(Thread)类上设置的defaultUncaughtExceptionHandler方法。
n 如果在线程类没有设置defaultUncaughtExceptionHandler,将调用Java5之前的ThreadGroup类中的未捕获异常方法。
实现Thread.UncaughtExceptionHandler接口看起来也像上面实现ThreadGroup接口。 public class AppSpecificExceptionHandler implements Thread.UncaughtExceptionHandler { public void uncaughtException(Thread, Throwable) { // app specific error handling here // ex: if fatal, release resources // show user dialog } }
在应用程序中插入如下: public class ApplicationMain { public static void main(String[] args) { Thread.setDefaultUncaughtExceptionHandler(new AppSpecificExceptionHandler()); } }
Swing异常处理实现
许多人没有注意到现有的JOptionPanel类,该类中有许多的优美的弹出对话框的静态方法(showOptionDialog,showConfirmDialog,showInputDialog,和showMessageDialog),这些方法接受的参数不仅仅是字符串。这些静态方法(也包括JOptionPanel构造函数)都是接受一个Object作为参数的的。下面是一个实现该类的核心代码:
public class ErrorDialog {
public ErrorDialog() {
super();
}
public static void showQuickErrorDialog(JFrame parent, Exception e) {
final JTextArea textArea = new JTextArea();
textArea.setFont(new Font("Sans-Serif", Font.PLAIN, 10));
textArea.setEditable(false);
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
textArea.setText(writer.toString());
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setPreferredSize(new Dimension(350, 150));
JOptionPane.showMessageDialog(parent, scrollPane,
"An Error Has Occurred", JOptionPane.ERROR_MESSAGE);
}
}
在程序中的一个例图如下:
使用InputVerifier设计Swing校验包 尽管有许多针对Swing的校验框架,并且Swing中也有API InputVerifier用于数据校验,InputVerifier易于使用并且容易定制。本文将在InputVerifier的基础上构建一个可用的校验包,可以在其它的Swing应用之中。 InputVerifier类在javax.swing包中。它有一个抽象方法,我们需要实现一个叫verify()的方法,这个方法接受一个JComponent作为参数,返回值为true或false。一旦我们实现了verify()方法,我们可以在JTextField中使用如下: JTextField text = new JTextField(); Text.setInputVerifier(new MyVerifier()); 然而,InputVerifier本身并没有什么意思。它的作用是防止用户输入错误的信息在组件中,阻止用户把输入光标移动到其他组件上。这种方式令人感到厌烦并且用户不知道错误所在。InputVerifier并没有提供一些更好的功能,所以我们需要构建一个如下校验包: 1. 如果数据不正确时,可以改变输入组件的背景颜色。 2. 如果数据不正确时,可以弹出消息提示用户错误出在什么地方,应该怎么处理。 3. 我们需要通知Form窗体每个组件的校验结果,这样的话Form窗体可以执行相应的动作。(例如启动”OK”按钮) 4. 如果用户改正了组件的错误数据,则错误提示信息应消失。 我们的包中包含一个抽象类,这个抽象类处理了大部分的工作,我们只需要实现一个方法,决定使用什么规则来进行数据校验;同时也提供一个接口,如果需要校验的Form窗体需要校验结果通知Form窗体,则可以实现该接口。好了,我们现在可以设计这个接口。 WantsValidationStatus接口 下面的接口可以提供一种方式用于出发Form窗体的制定事件。 package ica.swing.validation; public interface WantsValidationStatus { void validateFailed(); // Called when a component has failed validation. void validatePassed(); // Called when a component has passed validation. } 实现这个接口可以通知Form窗体校验状态,但是实现该接口不是必须的。我们也可以不是实现这个接口,如果校验失败不做任何事情。但是,我认为这样的设计是比较优美的。接下来看看我们的抽象类。 AbstractValidator类 AbstarctValidator处理了显示出错提示,改变出错背景,通知Form窗体等许多工作。只留下一个抽象的方法待实现,protected abstract Boolean validateCriteria(JComponent c),这个方法我们需要提供自己的校验规则用于校验组件。以下是抽象类: protected abstract boolean validationCriteria(JComponent c); public boolean verify(JComponent c) { if (!validationCriteria(c)) { c.setBackground(Color.PINK); //messageLabel.setSize(0,0); //messageLabel.setBackground(color); point = c.getLocation(); double x =point.getX(); double y = point.getY(); Point p = new Point(); p.setLocation(x,y+c.getHeight()); tooltip= new HalfOpaqueToolTip( message, new Color( 250 , 250 , 200 ), Color.RED, Color.BLACK, 1,dialog,p ); c.addMouseListener(tooltip); return false; } Verify()方法 当需要校验是调用方法boolean verify(JComponent c),下面用一例子示之。 public class ToolTipsEmptyValidator extends ToolTipsAbstractValidator { public ToolTipsEmptyValidator(JDialog dialog, JTextField c, String message) { super(dialog, c, message); } public ToolTipsEmptyValidator(JFrame dialog, JTextField c, String message) { super(dialog, c, message); } public ToolTipsEmptyValidator(JDialog dialog, JTextArea c, String message) { super(dialog, c, message); } public ToolTipsEmptyValidator(JFrame dialog, JTextArea c, String message) { super(dialog, c, message); } @Override protected boolean validationCriteria(JComponent c) { if (c instanceof JTextField){ if (((JTextField) c).getText().equals("")) return false; } else if (c instanceof JTextArea){ if (((JTextArea) c).getText().equals("")) return false; } return true; } } 调用方法如下: ToolTipsEmptyValidator validator1 = new ToolTipsEmptyValidator(jDialog,localdbPanel.getIpAddressField(),"Error,IP Address can't be empty"); boolean flag1 = validator1.verify(localdbPanel.getIpAddressField()); 下图是使用该校验包的示例。
向导在今天的桌面应用中非常常用。向导应该是个什么样子呢?相信你应该很清楚,因为你使用过很多的向导。也许你使用过一些安装程序向导或是一些程序的配置向导。这篇文章,我们会创建一个简单的向导框架。 一个向导包括很多Panel,每个Panel里面包含用户的配置组件或是文本域或是选择框等。用户点击”Next”或是”Back”按钮,在各个Panel之间切换,输入需要的信息。注意的是,当最后一个Panel是”Next”按钮要变成”Finish”按钮,并且再次按下的时候向导关闭。在向导关闭的时,发起向导的类要得到向导Panel中所有的数据。在任何情况下,用户可以点击”Cancel”按钮关闭向导并丢弃前面所填的所有数据。 看上去很简单是吗?对的,但是有些设计细节我们需要考虑。 第一,向导中的每个Panel不是都需要访问的,换句话说,如果向导包含1到5个Panel,点击”Next”按钮可以从第一个Panel依次到第五个Panel,但是有时候可能由于用户的选项直接从第一个Panel跳到第五个Panel. 而且还有的情况是,假设向导中需要连接到远程服务器或是远程数据库,如果连接不上的,那么就不能到达下一个Panel。这样向导中的Panel就像是树形,你从树的根开始,通过不同的分支到达叶子节点,这时”Next”按钮变成”Finish”按钮。 第二,有些时候Next按钮和Back按钮需要禁用。比如,第一个Panel出现的时候,back按钮应该禁用,因为没有上一个Panel。另外,当有些值必须输入的时候,没有输入的情况下Panel中的Next按钮应该为禁用。 第三,输入的数据需要一直保持到用户完成向导或是取消。因为当用户点击Back按钮会到上一个Panel时,上一个Panel填写的数据应该能够保持,并且再次使用Next按钮时,本Panel中的数据也应该保持。 有了这些设计细节,我们可以考虑设计自己的向导了。我们先规划下我们将要完成的一些类。 Wizard-这个类包含模型(model)和控制器(controller),其主要是一个对话框(JDialog),并且包含有Next,Back和Cancel按钮。还有一个使用CardLayout布局管理的大组件,可以把各个Panel显示在上面。想下图的样子: Java.awt.Componet的子类,这个类一般是继承了java.awt.Componet,通常是一个javax.swing.JPanel.这个类是用于显示在wizard类中的大组件位置。下图是其中一个Panel。 WizardPanelDescriptor-这第三个类用于关联wizard和panel。这个需要类用户继承,并用于标识Panel.这个类指定了访问下一个和前一个Panel的规则,并且在Panel的显示前,现实中,和显示后执行相应的动作。 Wizard 首先我们需要创建一个用于显示向导对话框本身,它包含有三个按钮Back,Next,Cancel.一般这些按钮是按照从左到右的顺序分布的,另外Cancel按钮要离其他的两个按钮远一点,这样防止用户不小心点击到Cancel按钮。接下来,就用需要一个布局,可以在同一个区域显示各个Panel,在AWT中有CardLayout布局。 在这个设计中,我们使用一个简单的方法来检测我们的数据。下面我们看看Wizard类: Public Class Wizard{ private WizardModel wizardModel; private WizardController wizardController; private JDialog wizardDialog; private JPanel cardPanel; private CardLayout cardLayout; private JButton backButton; private JButton nextButton; private JButton cancelButton; private MainFrame mainFrame; private int returnCode; public Wizard(MainFrame owner) { this.mainFrame = owner; wizardModel = new WizardModel(); wizardDialog = new JDialog(owner); Point np = owner.getLocation(); wizardDialog.setLocation(np); initComponents(); } } 注意到initComponents()方法,这个方法是用于布置界面中的组件和按钮的,并且把按钮事件关联到控制器中。 private void initComponents() { JPanel buttonPanel = new JPanel(); Box buttonBox = new Box(BoxLayout.X_AXIS); cardPanel = new JPanel(); cardPanel.setBorder(new EmptyBorder(new Insets(5, 10, 5, 10))); cardLayout = new CardLayout(); cardPanel.setLayout(cardLayout); backButton = new JButton(); nextButton = new JButton(); cancelButton = new JButton(); backButton.setActionCommand(BACK_BUTTON_ACTION_COMMAND); nextButton.setActionCommand(NEXT_BUTTON_ACTION_COMMAND); cancelButton.setActionCommand(CANCEL_BUTTON_ACTION_COMMAND); buttonPanel.setLayout(new BorderLayout()); buttonPanel.add(separator, BorderLayout.NORTH); buttonBox.setBorder(new EmptyBorder(new Insets(5, 10, 5, 10))); buttonBox.add(backButton); buttonBox.add(Box.createHorizontalStrut(10)); buttonBox.add(nextButton); buttonBox.add(Box.createHorizontalStrut(30)); buttonBox.add(cancelButton); buttonPanel.add(buttonBox, java.awt.BorderLayout.EAST); wizardDialog.getContentPane().add(buttonPanel, java.awt.BorderLayout.SOUTH); wizardDialog.getContentPane().add(cardPanel, java.awt.BorderLayout.CENTER); } 接下来,我们要把Componet Panel注册到Wizard中。Wzard中使用registerWizardPanel()方法。我们知道CardLayout布局中包含有next(),previous()这样的方法来回翻动Panel,然而我们需要的是树形结构,而不是线性的结构,因此我们需要使用标识符来标识各个Panel对象。 public void registerWizardPanel(Object id, WizardPanelDescriptor panel) { cardPanel.add(panel.getPanelComponent(), id); wizardMdel.registerPanel(id, panel); } 最后wizard中是用setCurrentPanel(Object id)来设置,Wizard初始化时显示的第一个Panel。剩下的就是一些事件处理,比较简单。Wizard中大量的使用Wizard来保存数据,并使用WizardController来处理对话框本身的事件。 WizardPanelDescriptor 注册到Wizard的每个方法都需要继承WizardPanelDescriptor类,这个类包含一些方法可以把组件集成到Wizard向导中。以下的四个方法:是访问组件和组件对象标识符的方法。 public final void setPanelComponent(Component panel) { targetPanel = panel; } public final Component getPanelComponent() { return targetPanel; } public final Object getPanelDescriptorIdentifier() { return panelIdentifier; } public final void setPanelDescriptorIdentifier(Object id) { panelIdentifier = id; } 下面是比较重要的一部分,就是每个继承类都需要改写的一些方法。包括控制Next,Back之后显示的Panel。在每次Panel初始化的时候都会执行Next这个方法,当Next和Back方法中返回的是null之的时候Next和Back按钮被禁用。因此,Next方法不能用于数据的校验,需要有另外的方法,在这里使用了一个Validator方法,当然如果数据需要校验,也需要在WizardPanelDescriptor子类中进行覆盖。具体如下: public Object getNextPanelDescriptor() { return null; } public Object getBackPanelDescriptor() { return null; } public boolean validator(){ return true; } 另外提供三个方法,用于控制Panel显示前,显示中和显示后的事件。 public void aboutToDisplayPanel() { } public void displayingPanel() { } public void aboutToHidePanel() { } WizardPanelDescriptor 最后,用上图来表示Wizard类和其他几个类之间的关系图,并展示了两个实例。
ci-bayes实现了两种贝叶斯分类方法:a Navie implementation 和 a Fisher implementation,是Toby segaran's 的书"Programming Collective Intelligence"的一部分的Java实现版本。 简单的一个例子如下: FisherClassifier fc=new FisherClassifierImpl();
fc.train("The quick brown fox jumps over the lazy dog's tail","good");
fc.train("Make money fast!", "bad");
String classification=fc.getClassification("money"); // should be "bad"
水星(Mercury) 金星(Venus) 地球(Earth) 火星(Mars) 木星(Jupiter) 土星(Saturn) 天王星(Uranus) 海王星(Neptune) 冥王星(Pluto) Twelve Years of Animals( 12生肖) 1.Year of the Rat 鼠年 2.Year of the Ox 牛年 3.Year of the Tiger 虎年 4.Year of the Rabbit 兔年 5.Year of the Dragon 龙年 6.Year of the Snake 蛇年 7.Year of the Horse 马年 8.Year of the Goat 羊年 9.Year of the Monkey 猴年 10.Year of the Rooster 鸡年 11.Year of the Dog 狗年 12.Year of the Boar 猪年 Twelve Constellations (12星座): 1.Aquarius(the Water Carrier)水瓶座 2.Pisces(the Fishes)双鱼座 3.Aries(the Ram)白羊座 4.Taurus(the Bull)金牛座 5.Gemini(the Twins)双子座 6.Cancer(the Crab)巨蟹座 7.Leo(the Lion)狮子座 8.Virgo(the Virgin)处女座 9.Libra(the Scales)天秤座 10.Scorpio(the Scorpion)天蝎座 11.Sagittarius(the Archer)射手座 12.Capricorn(the Goat)山羊座
一下是一些收集的关于动物的习语。有些没有翻译的分两种情况,一是从字面意思很容易看出来,而写出来不一定准确的;另外一类是我还不确定其真正的含义。 Bear 熊 - bear market 熊市-空头市场
- You can't have your cake and eat it too.鱼和熊掌不可兼得
Bird 鸟 - A bird in the hand is worth two in the bush.
- A little bird told me.
- a bird's-eys view鸟瞰
- Birds of a feather flock together.物以类聚
- eat like a bird吃的很少,食量小
- Fine feathers make fine birds.人要衣装,佛要金装。
- give a person the bird喝倒彩
- Kill two birds with one stone.一石二鸟
- the birds and the bees有关性的基本常识
- The early birds catches the worm.早起的鸟儿有虫吃
- newbie菜鸟
Bull,calf,cattle,cow,ox牛 - bull market 牛市-多头市场
- bull session 闲谈,男性之间的谈话
- like a bull in china shop笨手笨脚
- to milk the bull缘木求鱼
- You can't sell the cow and drink the milk.与和熊掌不可兼得
- why buy the cow when the milk is free.
- Take the bull by the horns.勇敢的客服困难,挺身而出
- as dumb as an ox像公牛一般沉默
- as strong as an ox像牛般强壮
- dirnk lisk a fish牛饮
- as stubborn as mule牛脾气
- talk horse吹牛
- to eat like a horse食量大如牛
- to lead a dog's life 牛马不如的生活
- to cast the pearls before swine对牛弹琴
Cat, Kitten猫 - A cat has nine lives.
- cat-and-mouse玩弄,折磨
- cat burglar蜘蛛盗
- cat house妓院
- cat nap/sleep小睡
- Cat got you tongue?舌头打结
- bell the cat做危险的事
- cats and dogs形同水火
- Curiousity killed the cat.勿多管闲事
- Let the cat out of the bag.泄漏机密
- live a cat-and-dog life争吵度日
- It rains cats and dogs.倾盆大雨
- There is not even enough room for swing a cat.无转身之处
- She is a scary cat.很容易受到惊吓
- While the cat is away, the mice will play.猫去鼠嬉
Chicken ,cock,hen,rooster鸡 - a hen party女性聚会
- chicken pox水痘
- chicken out临阵脱逃
- cock-eyed斜眼的
- henpeck对丈夫作威作福
- Which comes first,the chicken or the eggs?
- better to be the head of duck than the ass of a horse宁为鸡头不为凤尾
Dog, doggie, puppy狗 - a dog in the manager 鸠占鹊巢
- an old dog can't learn new tricks
- Barking dogs seldom bite.会叫的狗不咬人
- doggy bag剩菜袋
- dog's life生活艰难
- dog-eat-dog world
- dog tired
- a dog chance极有限的一点希望
- dog's day很热的日子
- dog-eared书页的折角
- dog eat dog无情而野蛮的竞争
- dog sleep小寐
- Every dog has its day.狗也有出头的日子
- Let sleeping dog lie.少惹麻烦
- Love me ,love my dog.爱屋及乌
- put on the dog装腔作势
- red dog beer私家酿酒
- top dog人上人
- paparazzi狗仔队
- bite the hands the feed one
- You can't make a silk purse out of sow's ear.够最终吐不出象牙
Fish鱼 - cold fish冷淡的人
- drink like a fish酗酒
- There are other fish in the sea天涯何处无芳草
Goat,lamb,sheep,kid羊 - as gentle as a lamb像绵羊一样温和
- a black sheep害群之马
- follow like sheep盲目顺从
- separate the sheep from and goats辨别好人与坏人
- Lock the stable door after the horse is stolen.亡羊补牢
Goose鹅 - as silly as goose像鹅般蠢
- Fair for the goose is fair for gander
- goose bump鸡皮疙瘩
- goose egg考零分
- gooseflesh鸡皮疙瘩
- Kill the goose that lays the golden eggs.杀鸡取卵
Hog,pig,swine,sow猪 - road hog挡道的自私驾驶
- bleed like a stuck pig大量出血
- eat like a pig吃的多而且吃相不好
- make a pig of oneself狼吞虎咽
- Pig may fly.Or to milk the bull.
Horse,pony马 - Don't put the cart before the horse.不要本末倒置
- Don't ride the high horse.不要妄自尊大
- Every horse thinks its own pack heaviest.
- It is useless to flog a dead horse.鞭策死马,徒劳无功
- Lock the stable after the horse has gone.亡羊补牢
- Don't look a gift horse in the mouth.受赠之马,勿探其齿
- Zeal without knowledge is runaway horse.
- Don't change horses in mid-stream.
- a horse of a different color
- a dark horse黑马
- from the horse's mouth第一手消息
- horses sense常识
- I can eat a horse.
- one-horse town小镇
- You can lead a horse to water,but you can't make him drink.师傅领进门,修行在个人
- Cruise chicks泡马子
- All lay loads on a willing horse马善被人骑
- leapfrog
- piggyback fight骑马打仗
Lion狮子 - as brave as a lion像狮子般勇敢
- That software designer still the lion's share of the market.那位软件设计师人人握有大部分的市场
- Put one's head into the lion's mouth自愿冒险
Monkey猴子 - monkey/horse/idle/fool around鬼混
- monkey business恶作剧
- monkey on the back某事已成了负担
- Monkey see,monkey de.有样学样
- monkey uncle吃惊
Mouse,rat鼠 - as poor as church mouse
- rat fink告密者
- a rug rat指不会走路的小婴儿
- I can smell the rat.我觉得事有蹊跷。
- Rats!胡说八道,该死,可恶
Snake蛇 - a snake in the grass口蜜腹剑的人
- Once bitten twice shy.一朝被蛇咬,十年怕井绳。
- to cherish a snake in one's bosom养护为患
- crawl蛇行
- overdo it画蛇添足
Tiger,cub虎 - ride the tiger骑虎难下
- the ass in the lions skin狐假虎威
- How can one get the tiger cubs if one does not enter tiger den?不入虎穴,焉得虎子
Other Animal其他 - as big as a whale
- as blind as a bat
- as busy as a bee
- as pround as peacock
- as slow as a turtle
- as slow as a snail
- as sly as a fox
- as stupid as a donkey
- as stubborn as a mule
- as wise as an owl
- butterfly effect连锁反应
- to have or get butterfly in one's stomach非常紧张
- The last straw breaks camel's back.忍无可忍
- crocodile tears不真诚的同情
- eat crow不得已认错
- A deer caught in the headlight.手足无措
- eagle-eyed眼力好
- mad as a March hare
- A leopard can't change his spots.江山易改本性难移。
- pet peeve不能忍受的事
- stag party男人的聚会
- stool pigeon奸细
- One swallow does not make a summer
- swan song最后作品
- white elephant 昂贵而无用的东西。
在刚刚结束的的WWW2008的会议中,有篇超短的paper 《Size Matters: Word Count as a Measure of Quality on Wikipedia》,这篇文章针对Wikipedia的质量的评估,提出了一种方法来评估一个Wikipedia文章的质量,这种方法很简单,就是数文章字数(Word Count)。 作者认为这种方法的好处有如下几点: 1.文章的长度很容易测量; 2.许多其他的方法需要一些其他的信息,而这些信息不是很容易得到; 3.许多其他的方式对用户来说不容易理解; 4.文章的长度评估的方法比其他的复杂的方法更有意义。 (说的好像是一点简单^_^)这确实是一种简单的方法,但是效率怎么样?根据作者的实验,这个方法在二个分类(featured and random)的情况下能得到96.3%的准确率。 当然,作者也比较实事求是,也不会夸大这种方法的作用,作者将寻找新的方法来评估Wikipedia文章质量。 除了这篇文章,其实还有其他几种方法,可以参看下面这些论文: 1.Information Quality Discussion in Wikipedia 2.Assessing Information Quality of a Community-Base Encyclopedia 3.Measuring Wikipedia 4.Wikipedia Article Quality Assessment and Ranking Tips for Users and Search Engine Engineers 这些文章虽然都是关于wiki质量讨论的,但是研究的意义很重大。特别是针对目前网络的信息泛滥的情况下,需要用一种方式来评估信息的有用性,这样可以让我们更轻松找到我们需要的有用信息,同样搜索引擎如果能判断提供给我们的文章的质量好坏,这也就免去了我们很多时间去挑选了。 对于推荐系统(如新闻、文章推荐系统),用户很少评分的情况下,可以采用这种方法自动给项目评分。
UltraEdit 是每天必用的工具之一.但想想看,实际使用到的功能只是这个工具强大功能的极小的一部分.灵活使用这个工具有的时候能起到事半功倍的效果.记录并收集一些小技巧对自己对别人都有帮助.因为我在使用"列编辑"这样的编辑方式还令我的一个同事看到之后很惊讶,之前他从来不知道有这个用法.这也是写这个备忘文档的目的。 Tip 1: 如何去掉所编辑文本中包含特定字符串的行? 这则技巧是在UltraEdit的帮助文件里提到.CTRL+R 调出来替换(Replace)窗口,选中"使用正则表达式";然后用查找 %*你的字符串*^p 替换成空内容即可.如,我当前有个文本文件,需要去掉所有包含 http://www.dbanotes.net/ 这个字符串的行,查找%*http://www.dbanotes.net/*^p 替换成空即可.注意,^p 是 DOS 文件类型的换行符.如果是 Unix 类型文件,则用 ^n. Tip 2: 如何在行末添加特定字符,比如逗号? 有了上面的经验(其实我第一次是从同事那里学到的),CTRL+R 调出来替换(Replace)窗口,选中"使用正则表达式".然后可以查找 ^p(或者^n,如果是Unix 文件),用 ,^p(或者,^n)进行"全部替换"即可.补充一点,如果是 MAC(Apple) 类型文件,则换行符号为 ^r . Tip 3: 如何删除空行? 参考上面两个例子,查找 ^p$ 然后替换为空即可. Tip 4: 编辑文件如何加入时间戳 ? F7 快捷键即可.你试试看? Tip 5: 为何 拷贝(Copy)/粘贴(Paste)功能不能用了? 不怕大家笑话,我有几次使用 UltraEdit 的过程中发现拷贝与粘贴的内容是不匹配的.不知所以然,干脆重新启动了笔记本.今天翻看手册才恍然大悟:UltraEdit有10个剪切板 (clipboard),分别用Ctrl+0 - Ctrl+9 切换. Ctrl+0 是 Windows 的,其他则为用户自定义的.我在使用的过程中错调用了 CTRL+n, 结果内容就有问题了.你遇到过没? Tip 6: 即使是打开小文件也有迟延? 这是我遇到过的问题.每次打开文件的时候总有几秒钟的耽搁.我的机器性能可不算差.怎么回事? 网络打印机搞得鬼! 打开"高级"->"设置"->"编辑器"->"高级",看看是不是选中了"载入/恢复打印机设置"?如果是的话,去掉(不同的版本/汉化与否可能该位置所在有差别). Tip 7:打开*.ec,*.hea文件时代码自动高亮度显示。 菜单路径:Advanced-Configuration-Syntax Highlighting页下部,选择“Open”按钮打开文件C:\Program Files\UltraEdit\wordfile.txt,修改该文件第一行,在后边的”File Extensions = C CPP CC CXX H HPP AWK”后加上”EC HEA”保存即可。 Tip 8: 把常用的菜单功能做成快捷按钮放在面板上。 菜单路径:Advanced-Configuration-Toolbar,选中左边喜欢的功能菜单,选中右边的位置,点击Insert即可。比如”Dos to Unix”用于把dos下的回车键去掉,“Compare files”用来比对文件,”Display Ruler”,”Display Line Numbers”,最让人惊喜的居然还有一个”ASCII table”的功能,不用每次查ASCII码的时候都去翻箱倒柜了,呵呵。不过可显示字符的ASCII值可以通过直接查看16进制模式看到,不用这么麻烦。 Tip 9: 列模式(快捷键:Alt+C) 当你需要批量修改数据或者造数据的时候,这个功能显示了强大威力。试试打开一个文件,按下Alt+C,Ctrl+A,开始写字,你会发现文件中所有的行都在执行相同的动作,一排相同的数据就出现了。动动脑筋你会发现各种奇妙的用法。 Tip 10: 做行标记 移到需要标记的行按下Ctrl+F2,标记好了;走到文件的任意其他行,按F2,回到标记处。可以做多个标记,这时F2在各个标记中循环走动。F2:Next Bookmark,Alt+F2:Previous Bookmark。 Tip 11: 列标志 写后台程序的时候不希望一行写的太长,一般要小于80个字节,但即使显示了标尺也看不大清楚屏幕中下部的行是否已经到了80字节,除非看下部状态条的列号。这时这个东西就起作用了。菜单路径:View-Set Column Markers,可以设置两个列标志,我们先设第一个,在第一个编辑框中填入80,再选择View-Show Column Marker 1,看到了?第80字节的地方出现了一条竖线。 Tip 12: 注释 有时调试程序时需要把连续的多行用“//”注释,然后还要放开注释,一行一行写太土了吧,有现成的东西用。选定要注释的行,Edit-Comment Add增加注释,Edit-Comment Remove。 Tip 13:编辑 选定整个单词当然可以用鼠标双击,用键盘Ctrl+J。删除整行Ctrl+E,删除到行首Ctrl+F11,删除到行尾Ctrl+F12。 Tip 14:别让它老问你是否把unix文件转换尾dos文件 到了9.0版本打开unix文件的时候它总是会问是否转换为dos文件,实在很烦。不过可以屏蔽,Advanced-Configuration-General,把右中部的Auto Convert Unix Files点上就好了。 Tip 15: 打开文件内容中的文件 如果文件的内容里面有 "c:\test.txt" 或者"http://www.test.com/js/test.js" 这样的内容你可以把鼠标定位到上面,点右键。弹出的菜单最上边会多个选项 "c:\test.txt" 或者"http://www.test.com/.../test.js" 点击它,打开相应文件。 ctrl+b 写程序的时候,括号一般要一一对应的,但是如果嵌套太多,看花眼了,怎么办?你把光标放在括号开始的地方,按ctrl+b,UE 会帮你找到相对应的括号结尾的地方。你还可以试试连着多按几次ctrl+b。 [Stick Out Tongue] F3 默认情况下,当你按F3的时候UE可以查找现在选中的内容,F3是下一个符合的内容,ctrl+f3是上一个符合的内容.(请查 看advanced/configuration/Find标签) ctrl+f2 程序会有很多行你当然可以记得你要到的行数,然后用ctrl+g,然后输入行号,到所在的行。但是用ctrl+f2我觉得更方便。比如说你要频繁在多个function中切换。可以在function开始的地方,按一下ctrl+f2,给这一行加一个书签。然后再另外的function开始的地方,也来一下ctrl+f2,有书签的地方,字的背景色会不同。当你想换到下一个书签的时候,就按f2,但是想到上一个标签怎么办?ctrl+f2?不对,嘿嘿, 再按就是加书签或者取消当前行的书签了。应该是alt +f2. 简单的用正则表达式的查找替换 有时候会有一些简单文本处理的工作。比如你手头有一个文本,需要给所有行后边添加一个";"。用查找替换来完成ctrl +r,查找 '^p' 替换为'^p;',(记得选中regular Expressions,这样才能用正则表达式的功能。)然后你可以选replace all(alt+a),或者点开始,一个一个的查找,替换,这样的好处是知道都替换了那些,有些时候你可能不想全部替换 把类似'{$abc}'替换为'var abc=abc;',abc有可能是其他字符ctrl+r,查找 '{^$^(*^)}' 替换为'var ^1=^1;'(记得选中regular Expressions,这样才能用正则表达式的功能。)然后你可以选replace all(alt+a),或者点开始,一个一个的查找,替换。 自定义快捷键 UE很多功能都有快捷键,但不是所有的都有。11.00有一个Text2html的功能。我工作中遇到了要对大量代码进行这种操 作的情况。我就想自己定一个快捷键。 advanced/configuration/key mapping 在commands 里面找到你要用的command.我这里是HTMLConvertSpecialChars,然后点Pres s new key下面的输入框,设置一个自己觉得爽,不冲突的快捷键。比如ctrl+alt+s.然后点ok.这样用常用的功能,可以成倍 提高效率。 计算选中区域数字的和 比如如下文本 2 23a1 4 1.1 5 6 先选中,然后Column/(sum column/Selection),UE会以空格,字母分割数字,告诉你一个总数 UE的列编辑功能 首先要alt+c,进入列编辑模式。进入后,你可以用鼠标选择一个方形的区域。删除,复制全看你喜欢了。 如果你想在每一行第二个字符开始加入一个'test',在列编辑模式下,定位光标到第一行,第二列。 Column/(Insert/Fill column) ,你还可以用这个功能插入行数。在列编辑状态下的复制粘贴都很有意思,某些情况下可以取得意想不到的效果。 UE的比较 UE内置一个比较功能,可以帮你比较2个文件的不同 file/compare files... 如果你打开了2个要比较的文件,UE会把这2个文件自动填入2个要比较的文件位置,不然,你要用browse功能去找到那2个文件,设置一下text还是bin,是否要ignore一些你不关心的东西。然后点击compare就到比较界面。你可以设置只显示不同或者相同或者都显示。日常的应用是可以了。(有点像BC,不知道谁抄谁的 ) 加入当前时间 有时候写代码要注释,比如那天改的,按一下F7试试。 打开的文件中切换 如果打开多个文件,要在多个文件中切换,用鼠标点,麻烦,试一下ctrl+tab。好多多窗口的软件都支持这个功能。 恢复到上次存盘状态 一个文件改动多了,想undo到最初状态,file/revert to saved. 文件备份,重命名 一个文件要备份,你如果选save as了,那当前打开的就是你save as之后的文件了。 用fle/(make copy/backup),你还可以直接重命名当前编辑的文件 file/Rename file UE的project功能 11.00以后有了一个自动打开上次关闭时打开着的文件这个功能了,以前好像没有。这就可以用到Project功能。其实就是定 义一组相关的文件。project/(new project/workspace) UE会要求你存一个*.prj的文件。下次你可以打开UE的时候,project/(o pen project/workspace),继续上次的那个project的session工作。也可以file/(recent project/workspace) UE的function列表功能 打开一个程序文件,比如*.js,确保view/view as(*)/Javascript。选择view/(view/lists)/function list.也可以用F8 大块代码缩进的调整 选中要调整的代码块 按tab,进行缩进,你再按一下shift+tab。效果咋样? UE的右键功能 1)去处行末的空格 选中要去空格的区域。点右键/format/menu../trim trailing spaces 2)删除整行 定位光标要删除的行。点右键/delete/delete line,(也可以用ctrl+e) 3)给代码加注释 首先要确定选中了正确的语法加亮显示 view/view as(*)/Javascript 然后选中要注释的部分 点右键/delete/comment add 或者comment remove 4)格式化代码 首先要确定选中了正确的语法加亮显示 view/view as(*)/Javascript 然后选中要格式化的部分,就是让代码的缩进好看点,点右键/format menu/reIndent selection 不过,如果你的代码是一行,没有按照句子分号。好像没啥效果。 5)复制当前编辑文件的路径如果你要把当前文件作其他处理,需要这个文件的路径,这个 功能可以不用再去敲路径点右键 copy file path/name 6)复制当前编辑文件的路径::点右键copy file path/name 7)打开文件内容中的文件: 如果文件的内容里面有"c:test.txt" 或者"http://www.test.com/js/test.js" 这样的内容。你可以把鼠标定位到上面,点右键。弹出的菜单最上边会多个选项 "c:test.txt" 或者"http://www.test.com/.../test.js",点击它,打开相应文件。 调整,添加语法高亮显示 advanced/configuration/syntax Highlighting 点击下边的full path name for word list后边的open 打开的文件如:d:Program FilesUltraEditWORDFILE.TXT,就是UE语法高亮显示的配置文件 /L1"C/C++" 就是第一种语言,/L2就是第二种。目前这些word files 可以从下面连接下载到 http://www.ultraedit.com/index.php?...id=40#wordfiles 用的时候,下载相应的word file,复制出来,粘贴到d:Program Files\UltraEdit\WORDFILE.TXT,注意修改刚开始的/L1和你现有系统匹配。好像对xml.xsl的显示不是很好,不够准确 运行dos命令,直接得到结果 F9,会跳出来一个窗口,让你输入命令和工作目录。比如 dir c: 会列出来c盘的目录。如果你要给朋友发目录列表,除了从dos窗口复制过来,还可以用这个简单的方法。加上一下简单的列编辑。结 果就更好看了。 内置的ascii table view/ascii table 有时候需要知道某个字母的ascii值,从这里就能查出来 !是33 A是65 。。。 内置的多个剪贴板 你点右键可以看到 clipboards,里面内置了10个剪贴板。按说windows的copy只能复制一个内容。如果你要复制多个内容,跟据不 同的情况进行粘贴,这10个剪贴板,应该够你用了。ctrl+0-9的数字键,是在剪贴板之间切换。比如 ctrl+1,然后copy了内容"a";ctrl+2,然后copy了内容"b" 你如果想paste a,就要先按1下ctrl+1再ctrl+v,要paste b,就按一下ctrl+2再 ctrl+v。我有时候不小心更换了剪贴板,就奇怪从别的地方复制的东西粘贴不过来。这种情况要注意。
数据挖掘中的任务大致包括以下几个方面: 1、分类:通过一个带有类标记的训练数据集,建立一个分类模型,通过对一系列属性的考察,可以对对象的类型进行预测,这是有监督的学习; 2、估计,例如:分析消费模型,估计个人收入和孩子数目; 3、预言,例如:根据个人教育、当前工作、行业趋势、预言2009年的工资; 4、密切性发掘,例如:关联规则发掘和相关性分析 5、聚集:主要针对没有类标记的数据,建立一个归类模型,让同一类的对象有尽量大的相似性,不同类的对象有尽量大的差异,这是无监督的学习; 6、偏差分析; 7、异常检测:发现不同于正常模式的数据,多用于风险规避、入侵检测。 (关于监督学习和非监督学习,请查看Machine Learning, Part I: Supervised and Unsupervised Learning或是译文) 数据挖掘中的步骤为: 1、数据规范化(消除错误和不一致的数据)和集成(从不同数据源提取数据); 2、数据选择和变换(提取任务相关数据,根据需要转换成统一的、适合挖掘的形式); 3、数据挖掘(使用合适的算法,在有效的时间内完成); 4、模式评估(根据某种兴趣度量,识别表示知识的真正有趣的模式); 5、数据挖掘结论的表示(使用可视化和知识表示技术,向用户提供挖掘的知识)。
<?xml version="1.0" encoding="ISO-8859-1" ?>
<gui-definition>
<colors>
<background>#808080</background>
<text>#000000</text>
<header>#008000</header>
<link normal="#000080" visited="#800080"/>
<default>${colors.header}</default>
</colors>
<rowsPerPage>15</rowsPerPage>
<buttons>
<name>OK,Cancel,Help</name>
</buttons>
<numberFormat pattern="###\,###.##"/>
</gui-definition> 以上是个很简单的XML,首先肯定是把这个文件载入(就把上面这个文件命名为table.xml吧) try
{
XMLConfiguration config = new XMLConfiguration("tables.xml");
// do something with config
}
catch(ConfigurationException cex)
{
// something went wrong, e.g. the file was not found
} 如果导入的时候没有异常的话,config对象就可以使用了,读取的方法如下: String backColor = config.getString("colors.background");
String textColor = config.getString("colors.text");
String linkNormal = config.getString("colors.link[@normal]");
String defColor = config.getString("colors.default");
int rowsPerPage = config.getInt("rowsPerPage");
List buttons = config.getList("buttons.name"); 下面说明下需要注意的几个地方: 1.首先根元素是被忽略的,在上面的例子中不是写gui-definition.colors.text,而是写成colors.text。 2.使用“.”符号访问子元素。在上面的例子中访问<colors>元素中的<text>,对应的key是colors.text。 3.访问元素属性的方法类似XPath的方法。 4.返回的是list的元素,可以用getList()方法。就像上面例子中的buttons.name的内容是"OK,Cancel,Help",用getList() 方法返回的是3个元素组成的List。这里的分隔符是“,”,可以通过setDefaultDelimiter ()方法进行指定。 5.可以操作propertiesConfiguration,就像是例子中的<default>元素。 上面是个最简单的XML格式,但是还有些相对复杂的XML,如下:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<database>
<tables>
<table tableType="system">
<name>users</name>
<fields>
<field>
<name>uid</name>
<type>long</type>
</field>
<field>
<name>uname</name>
<type>java.lang.String</type>
</field>
<field>
<name>firstName</name>
<type>java.lang.String</type>
</field>
<field>
<name>lastName</name>
<type>java.lang.String</type>
</field>
<field>
<name>email</name>
<type>java.lang.String</type>
</field>
</fields>
</table>
<table tableType="application">
<name>documents</name>
<fields>
<field>
<name>docid</name>
<type>long</type>
</field>
<field>
<name>name</name>
<type>java.lang.String</type>
</field>
<field>
<name>creationDate</name>
<type>java.util.Date</type>
</field>
<field>
<name>authorID</name>
<type>long</type>
</field>
<field>
<name>version</name>
<type>int</type>
</field>
</fields>
</table>
</tables>
</database> 像上面例子中用tables.table.name key去取得表格的名字,返回的是什么类型呢?因为tables.table.name中的table和name 有两个地方都出现了。其实返回的是个Collection, Object prop = config.getProperty("tables.table.name");
if(prop instanceof Collection)
{
System.out.println("Number of tables: " + ((Collection) prop).size());
} 其实也可以用getList()方法来操作,这里如果你只想得到第一个tables.table.name,需要使用getString()方法就可以了。 还有另外一个问题我们可以用getList()方法取到tables.table.fields.field.name,但是其实这些filed.name并不是属于同 一个table,这样的话,就需要使用如下的方法: List fields = config.configurationsAt("tables.table(0).fields.field");
for(Iterator it = fields.iterator(); it.hasNext();)
{
HierarchicalConfiguration sub = (HierarchicalConfiguration) it.next();
// sub contains now all data about a single field
String fieldName = sub.getString("name");
String fieldType = sub.getString("type");
...
第一行中是使用configurationsAt的方法是为了,省略在下面子元素前全路径。在实际应用中可能不一定会直接指定table(0),这可以结合上面的Object prop = config.getProperty("tables.table.name")进行迭代。
ica推荐系统团队,正式在blogjava启动,欢迎新加入的成员。
今天看了一段关于谦虚的精彩描述,看完这段之后相信我应该明白怎么认识自己了,怎么更理性和人相处。 “我们应该谦虚,因为你我都没什么了不起。我们都会去世,百年之后就被人忘得一干二净了。生命是如此短暂,请不要在别人面前大谈我们的成就,使别人不耐烦,我们要鼓励他们谈谈他们自己才对。回想起来,你反正也没有什么好谈的。你知道什么东西使你没有变成白痴吗?没有什么了不起的东西,只不过是你甲状腺中的碘罢了,价值才5 分钱。如果医生割开你颈部的甲状腺,取出一点点的碘,你就变成一个白痴了。5 分钱就可在街角药房中买到的一点点的碘,是使你没有住在疯人院的东西。价值5 分钱的碘!没有什么好谈的。”
Pearson相关系数 今天用做实验时发现一个问题:计算两组数据的相关系数userRate1(方程中的x),userRate2(方程中的y) 计算得到x,y的交集为1个元素,故∑中的n=1,x- 是一个非零的数,y- =0(userRate2的平均值和userRate2、userRate1中的交集的那个唯一的元素的值相等),这样分母就为零了。这种情况,Pearson相关系数应该为0?从userRate1和userRate2是有交集来看它们的相关系数,从感觉上来说应该不是0,也不是计算出来的无穷大。不知道有没有人碰到同样的问题?
STEP 1:下载和安装 首先在Subversion的官方网站去下载windows安装包,最新版是1.3.1,可惜在项目树上只更新到了1.3.0的二进制包。 下载后安装在本地机器上,这里注意的是最好将安装目录指定为纯英文名目录,安装在中文目录下天知道哪天会冒出一个让你想破头也想不出的错误来。 下载TortoiseSVN进行本地安装,我安装的是最新的1.3.2 for svn 1.3.0,这是一个将SVN集成到windows shell中的GUI管理工具,推荐使用。 STEP 2:创建储存库 安装完TortoiseSVN后提示要重启机器,其实启不启都可以正常使用了,首先创建SVN储存库(repository),可以选择命令行方式或者通过TortoiseSVN插件进行GUI操作,命令行运行如下: svnadmin create E:\svn\repository e:\svn\repository就是我指定的储存库目录,如果用GUI方式,可以在这个目录下点击右键选择[TotoiseSVN]->[Create Repository href...]进行创建,版本库模式指定为默认的即可。 repository创建完毕后会在目录下生成若干个文件和文件夹,dav目录是提供给Apache与mod_dav_svn使用的目录,让它们存储内部数据;db目录就是所有版本控制的数据文件;hooks目录放置hook脚本文件的目录;locks用来放置Subversion文件库锁定数据的目录,用来追踪存取文件库的客户端;format文件是一个文本文件,里面只放了一个整数,表示当前文件库配置的版本号; STEP 3:配置 打开/conf/目录,打开svnserve.conf找到一下两句: # [general] # password-db = passwd 去之每行开头的#,其中第二行是指定身份验证的文件名,即passwd文件 同样打开passwd文件,将 # [users] # harry = harryssecret # sally = sallyssecret 这几行的开头#字符去掉,这是设置用户,一行一个,存储格式为“用户名 = 密码”,如可插入一行:admin = admin888,即为系统添加一个用户名为admin,密码为admin888的用户 STEP 4:运行SVN服务 在命令行执行 svnserve --daemon --root E:\svn\repository 服务启动,--daemon可简写为-d,--root可简写为-r,可以建立一个批处理文件并放在windows启动组中便于开机就运行SVN服务,或者在这个地址http://clanlib.org/~mbn/svnservice/下载那个svnservice.exe文件,拷贝到E:\svn\bin目录下,再从命令行下执行: svnservice -install --daemon --root "E:\svn\Repository" sc config svnservice start= auto net start svnservice 此文件会将SVN变成windows系统的一个服务,并默认为自启动,注意:执行第三句时确保前面以命令行方式运行的SVN服务已经停止,如果没停止可在其窗口中按Ctrl+C中止运行。 STEP 5:创建项目版本树 确定SVN服务(命令行或windows服务)运行后,在你需要导入储存库的目录下单击右键选择[TortoiseSVN]-> [Import...],在弹开的窗口的URL框中输入 "svn://localhost/myproject" 点击 "OK" 执行导入,如果没有报错,数据就全部加入SVN储存库目录树上了。用命令行也可以完成这些操作,这需要你在系统变量中新建一个“SVN_EDITOR”的系统变量,变量值为本地的一个文本编辑器执行文件路径,一般指到windows的记事本上就行了 "c:\windows\notepad.exe" ,然后新开一个CMD窗口,执行 svn mkdir svn://localhost/myproject 随即关闭记事本打开的log文件窗口后按"c"键继续后生成项目树。一般情况,我们在创建文件根路径后应该在创建三个目录:branches、tags、 trunk,这三个目录是Subversion需要的三个目录。对于check out、commit、update等操作可以通过svn命令行方式执行,也可以用TortoiseSVN的windows菜单完成,非常简单咯。 --------------------------------------------------------------------------------------------------------------- 我安装的时候基本上是参考这篇文章,但是有个地方好像有问题,svnservice -install --daemon --root "E:\svn\Repository"需要修改为svnservice -install --daemon,否则使用svn://localhost/svnrepos浏览是会出错的,具体原因不知道为什麽,也是从http://svn.haxx.se/users/archive-2005-03/1343.shtml 地方看到的,不过搞了好长时间。 如果需要在eclipse中使用的话,需要安装插件,参考这篇文章http://www.ibm.com/developerworks/cn/opensource/os-ecl-subversion/ 另外1.4版本的subversion自带了SVNServer,参考文章http://www.subversion.org.cn/index.php?option=com_content&task=view&id=83&Itemid=9
在Google的工具条中制作Baidu的搜索按钮 安装了Google工具条是很方便的,但是有的时候需要使用Baidu搜索一些中文方面的资料的时候(我认为百度在搜索中文方面比Google强,而且我还是爱国青年^_^),这样还得到百度网站,把工具条的内容复制下来,这样就显得很麻烦。就想在Google按钮的旁边加个百度的按钮,这样就可以方便的进行搜索了。 其实Google工具条可以方便的实现自定义按钮的,详细的可以看API使用入门(http://toolbar.google.com/buttons/intl/zh-CN/apis/howto_guide.html)。 给出我的Baidu按钮实现方法: 1.在“C:\Documents and Settings\qiya\Local Settings\Application Data\Google\Custom Buttons”(这个是隐藏文件)路径下面的文件夹下面创建一个XML文件,如www.baidu.com_qiyadeng.xml。 把下面的内容复制进去: <?xml version="1.0" encoding="utf-8"?> <custombuttons xmlns="http://toolbar.google.com/custombuttons/"> <button> <search>http://www.baidu.com/s?ie=utf-8&wd={query}</search> <site>http://www.baidu.com</site> <title>Baidu</title> <description>Baidu</description> <icon> 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 vM/83M8KEAQAOw== </icon> </button> </custombuttons> 这样重启开启浏览器,按钮就应该自动增加好了。这些是最基本的功能,不过对我来说已经足够了,需要增加的话可以具体查看API使用入门。 如果觉得按钮的图标不够漂亮的话,也可以更改<icon></icon>中的值。使用你自己的图片到http://www.motobit.com/util/base64-decoder-encoder.asp转换成Base64代码替换<icon></icon>中的值即可。
1 .J2EE设计开发编程指南
简单评论:
这本书从实际项目出发,作者介绍了大量的J2ee技术,并讨论各种技术在什么情况下适用,及使用中应避免的问题。从中可以看出作者是个疯狂的实用主义者,有很丰富的实际项目经验,并且作者的知识广度和深度真的是让人佩服,对提到的一些问题,作者似乎都有较深入的研究。
好像是从第6章开始(记不太清楚了),作者给出了一个实际项目,然后并用实际中使用的方法对项目进行分析,很实用。接下来的章节就是按开发的顺序介绍各个技术的,比如数据存取,详细介绍了JDBC的模板方法,如果读者自己认真读完再好好整理整理一个完整的小框架就形成了(当然作者提供了源码的下载)。如果使用过Spring的读者可能觉得这本书有点像是Spring的源码分析,如果有志要分析Spring的源码的话,这本书也是不错的。
在书的各章中都渗透了设计模式(有章节重点介绍)和编码的规则。最后本书还就一个完成的项目进行各方面的测试讨论。
有没有试图写这样的代码 Java:
package novelty.function.test;
public class ChinesePrograme {
public static void main(String[] args){ String 中文变量 = "english"; System.out.println(中文变量); } }
C#:
using System; using System.Collections.Generic; using System.Text;
namespace ConsoleApplication1 { class Program { static void Main(string[] args) { String 中文变量 = "english"; Console.WriteLine(中文变量); } } }
是的,这样的代码是可以正确运行的。 在你的程序中有没有考虑使用中文的变量名呢?
1,现在全国上下,流行骂人骂得最凶的一句话: 甲:听说你哥哥在国家队踢球? 乙:你哥哥才在国家队踢球呢!!!你全家都在国家队踢球!!!
2,国内猪肉涨价的真实原因 儿子问:爸爸,猪肉为什么会涨价? 爸爸和蔼的告诉他:儿子,中国的猪都被一个叫猪广户的带去踢足球了。国内没货
开始
1. 用DBTestCase的子类建立数据库
2. 用你自己的TestCase子类建立数据库
3. 数据库数据校验
4. DbUnit的Ant任务和Canoo web测试(此处省略,另详)
用DBTestCase的子类建立数据库
第一步:创建你的dataset文件
你的测试需要一些数据。这就意味着你必须创建dataset。许多情况下你都是处理xml的dataset。你可以人工凑一些一般的xml dataset或是从你的数据库中导出一个xml dataset.
第二步:继承DBTestCase类
现在你要创建一个测试类。最简单的方式是用通过继承DbUnit的DBTestCase来创建你自己的测试类。DBTestCase继承了Junit的TestCase类。一个模板方法你需要实现,getDataSet()返回你在第一步创建的dataset。DBTestCase依靠IdatabaseTester来工作,默认的配置是使用PropertiesBaseJdbcDatabaseTester,它是用系统属性来指出DriverManager的配置。最简单的方式是在你测试类的构造函数中配置它。你可以通过覆盖getDatabaseTester()方法来修改它的行为。
使用下面提供的三种IDatabaseTester之一实现,你也可以使用下面表中描述的DBTestCase的其它子类。
JdbcBaseDBTestCase
|
使用DriverManager来创建连接(在JdbcDatabaseTester的帮助下)
|
DataSourceBasedDBTestCase
|
使用javax.sql.DataSource来创建连接(在DataSourceDatabaseTester的帮助下)
|
JndiBasedDBTestCase
|
使用javax.sql.DataSourece通过JNDI定位(在jndiDatabaseTester的帮助下)
|
下面是一个简单的实现,连接到一个Hypersonic数据库并返回xml dataset:
public class SampleTest extends DBTestCase
{
public SampleTest(String name)
{
super( name );
System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_DRIVER_CLASS, "org.hsqldb.jdbcDriver" );
System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_CONNECTION_URL, "jdbc:hsqldb:sample" );
System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_USERNAME, "sa" );
System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_PASSWORD, "" );
// System.setProperty( PropertiesBasedJdbcDatabaseTester.DBUNIT_SCHEMA, "" );
}
protected IDataSet getDataSet() throws Exception
{
return new FlatXmlDataSet(new FileInputStream("dataset.xml"));
}
}
第三步:(可选)实现getSetUpOperation()和getTearDownOperation()方法
默认的情况,DbUnit在每次执行test之前执行一个CLEAN_INSERT操作并且不之后不执行清除操作。你可以通过覆盖getSetUpOperation()和getTearDownOperation()来改变这个行为。
下面这个例子演示你可以通过简单的覆盖方法改变执行测试前和后。
public class SampleTest extends DBTestCase
{
protected DatabaseOperation getSetUpOperation() throws Exception
{
return DatabaseOperation.REFRESH;
}
protected DatabaseOperation getTearDownOperation() throws Exception
{
return DatabaseOperation.NONE;
}
}
第四步:实现你的testXXX()方法
就像你使用JUit一样实现test方法。你的数据库在测试方法之前初始化并且在测试之后清除,这取决于你在前几步是怎么做的。
用你自己的TestCase子类建立数据库
为了使用DbUnit你不是必须要继承DBTestCase类。你可以覆盖标准的Junit的SetUp()方法,执行你所需要的数据库操作。如果你要执行清除,同样覆盖teardown()方法。
例如:
public class SampleTest extends TestCase
{
public SampleTest(String name)
{
super(name);
}
protected void setUp() throws Exception
{
super.setUp();
// initialize your database connection here
IDatabaseConnection connection = null;
//
// initialize your dataset here
IDataSet dataSet = null;
//
try1
{
DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
}
finally
{
connection.close();
}
}
}
自从2.2版本你可以使用IdatabaseTester来完成同样的功能。就像前面所提到过的,DBTestCase内部是使用IdatabaseTester来实现的。你的测试类可以使用这个功能操作数据集(DataSets)。目前有4个方便的实现。
JdbcDatabaseTester
|
使用DriverManager来创建连接。
|
PropertiesBasedJdbcDatabaseTester
|
也是使用DriverManager来创建连接,但是配置信息是从系统属性中读取的。这是DBTestCase的默认实现方式。
|
DataSourceDatabaseTester
|
使用javax.sql.DataSource创建连接。
|
JndiDatabaseTester
|
使用javax.sql.DataSource通过JNDI寻找
|
你也可以提供你自己的IdatabaseTester实现,推荐使用AbstractDatabaseTester作为一个开始点。
例:
public class SampleTest extends TestCase
{
private IDatabaseTester databaseTester;
public SampleTest(String name)
{
super(name);
}
protected void setUp() throws Exception
{
databaseTester = new JdbcDatabaseTester("org.hsqldb.jdbcDriver",
"jdbc:hsqldb:sample", "sa", "");
// initialize your dataset here
IDataSet dataSet = null;
//
databaseTester.setDataSet( dataSet );
// will call default setUpOperation
databaseTester.onSetUp();
}
protected void tearDown() throws Exception
{
// will call default tearDownOperation
databaseTester.onTearDown();
}
}
数据库数据校验
DbUnit提供校验两个表或是数据集是否包含相同的数据的方法。下面的两个方法是在执行测试类的时候可以校验你的数据库中是否包含预期的数据。
public class Assertion
{
public static void assertEquals(ITable expected, ITable actual)
public static void assertEquals(IDataSet expected, IDataSet actual)
}
例子
以下例子,展示怎么比较一个数据库表的快照和一个XML表。
public class SampleTest extends DBTestCase
{
public SampleTest(String name)
{
super(name);
}
// Implements required setup methods here
public void testMe() throws Exception
{
// Execute the tested code that modify the database here
// Fetch database data after executing your code
IDataSet databaseDataSet = getConnection().createDataSet();
ITable actualTable = databaseDataSet.getTable("TABLE_NAME");
// Load expected data from an XML dataset
IDataSet expectedDataSet = new FlatXmlDataSet(new File("expectedDataSet.xml"));
ITable expectedTable = expectedDataSet.getTable("TABLE_NAME");
// Assert actual database table match expected table
Assertion.assertEquals(expectedTable, actualTable);
}
}
actual数据集是一个数据库的快照可以和你想要比较的expected数据集进行比较。就象他的名字一样,expected数据集中包含预期的值。
expected数据集一定要和你建立数据库时的对象不一样。因为你需要两个数据集,一个是在测试之前建立数据库,一个是提供匹配测试时的expected数据。
使用查询来获取数据库的快照
你也可以校验查询的结果是不是和期望的数据集匹配。这个查询可以使查询一个表中的一部分也可以是多表的联合查询。
Itable actualJoinData = getConnection().createQueryTable("RESULT_NAME",
"SELECT * FROM TABLE1, TABLE2 WHERE ");
在比较的时候忽略一些列
有些时候希望忽视一些列来进行比较,特别是对主键,日期或是时间列,这些列的值是在测试的时候又代码产生的。一种方式是在你的expected表中省略你不想比较的列的声明。这样你可以过滤真实的数据库表只暴露出expected表中的列。
下面这些代码片段向你展示怎么过滤真实数据库中的表。首先,真实数据库中必须包含expected表中的所有列。另外,真实表中有这些列而expected表中没有这些列,这种情况是允许的。
ITable filteredTable = DefaultColumnFilter.includedColumnsTable(actual,
expected.getTableMetaData().getColumns());
Assertion.assertEquals(expected, filteredTable);
这个技术的主要限制是你不能在你的expected数据集XML中使用DTD。使用DTD的话你需要过滤expected表和真实表中的列。查看FAQ中关于在运行时排除一些表的列(excluding some table columns at runtime)。
行顺序
默认的情况下,用DbUnit得到的数据库快照表是按主键排序的。如果一个表没有主键或是主键是由数据库自动产生的,行的顺序是不确定的,那么assertEquals将会失败。
你一定要排序你的数据库快照通过在IdatabaseConnection.createQueryTable时手工加入”ORDER BY”语句。或者你可以这样使用SortedTable:
Assertion.assertEquals(new SortedTable(expected),
new SortedTable(actual, expected.getTableMetaData()));
两分钟教程
这是一个关于XStream快速的介绍。快速浏览一下你会马上知道把一个对象转换到XML或是转换回来是多么的简单。你肯定会碰到下面的问题。
创建能够序列化的类
这是一组简单的类。XStream能把这些类的实例转换到XML或是转换回来。
public class Person {
private String firstName; private String lastName; private PhoneNumber phonex; private PhoneNumber fax; //构造函数或是其他方法 }
public class PhoneNumber {
private int code; private int number;
//构造函数或是其他方法 }
注:注意到这些都是私有变量。XStream不关心变量的作用域。不需要getter或是setter方法。并且,XStream不限定需要默认的构造函数。
实例化XStream
使用XStream,简单实例化XStream类:
XStream xStream = new XStream();
你需要xstream-[version].jar and xpp3-[version].jar在classpath中。XPP3是一个非常快的XML拉式转换器工具。如果你不想包含这个依赖,你可以使用标准的JAXP DOM转换器来代替。
XStream xStream = new XStream(new DomDriver());//不需要XPP3库
注:这个类的简单设计是为了实现通用操作的。为了实现更复杂的操作你可以选择自己创建出不同方式。
现在,为了使用XStream来更精简的输出XML,你可以为自定义的类创建别名到XML的元素名的映射。这是使用XStream唯一需要的映射的,甚至这个都是可选的。
xStream.alians("person",Person.class);
注:这是可选的一步。没有这步XStream也可以很好的起作用,但是XML元素的名字就会包含每个类的全称(包括包名),这将会使生成XML稍大。
序列号一个对象到XML
让我们创建一个Person的实例并且填充它的变量域:
Person joe = new Person("Joe","Walnes");
joe.setPhone(new PhoneNumber(123,"1234-456"));
joe.setFax(new PhoneNumber(123,"9999-999"));
现在转换到XML,你要做的是简单的调用XStream:
String xml = xstream.toXML(joe);
生成的XML看上去像这样:
<person> <firstname>Joe</firstname> <lastname>Walnes</lastname> <phone> <code>123</code> <number>1234-456</number> </phone> <fax> <code>123</code> <number>9999-999</number> </fax> </person>
非常简单,像创建XML一样。
从XML反序列化一个对象
从XML重新构造一个对象:
Person newJoe = (Person)xStream.fromXML(xml);
XStream是多么的简单啊!
总结:
使用xStream.alias(String elementName, Class cls)为任何一个自定义类创建到类到元素的别名;
使用xStream.toXML(Object obj)转换对象到XML;
使用xStream.fromXML(String xml)转换XML到对象;
(附原文地址: http://xstream.codehaus.org/tutorial.html)
今天在调试程序中发现类似下面这种错误
- SQL Error: 1064, SQLState: 42000- You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'REQUIRE, PLAN_DATE, COMPILE, AUDITOR, AGREE_PERSON, AGREE_DATE, ADD_BY, MOD_BY, ' at line 1- Could not synchronize database state with sessionorg.hibernate.exception.SQLGrammarException: Could not execute JDBC batch update
找了半天也没发现是什么错误,搜索多数认为是 hbm.xml中catalog中引起的问题,但我检查了发现不是配置文件引起的问题。后来联想到是不是MySQL的保留字的问题,找到http://dev.mysql.com/doc/refman/5.1/zh/language-structure.html#reserved-words,果然是。 MySQL的保留字有以下这些,使用中需要注意。
ADD |
ALL |
ALTER |
ANALYZE |
AND |
AS |
ASC |
ASENSITIVE |
BEFORE |
BETWEEN |
BIGINT |
BINARY |
BLOB |
BOTH |
BY |
CALL |
CASCADE |
CASE |
CHANGE |
CHAR |
CHARACTER |
CHECK |
COLLATE |
COLUMN |
CONDITION |
CONNECTION |
CONSTRAINT |
CONTINUE |
CONVERT |
CREATE |
CROSS |
CURRENT_DATE |
CURRENT_TIME |
CURRENT_TIMESTAMP |
CURRENT_USER |
CURSOR |
DATABASE |
DATABASES |
DAY_HOUR |
DAY_MICROSECOND |
DAY_MINUTE |
DAY_SECOND |
DEC |
DECIMAL |
DECLARE |
DEFAULT |
DELAYED |
DELETE |
DESC |
DESCRIBE |
DETERMINISTIC |
DISTINCT |
DISTINCTROW |
DIV |
DOUBLE |
DROP |
DUAL |
EACH |
ELSE |
ELSEIF |
ENCLOSED |
ESCAPED |
EXISTS |
EXIT |
EXPLAIN |
FALSE |
FETCH |
FLOAT |
FLOAT4 |
FLOAT8 |
FOR |
FORCE |
FOREIGN |
FROM |
FULLTEXT |
GOTO |
GRANT |
GROUP |
HAVING |
HIGH_PRIORITY |
HOUR_MICROSECOND |
HOUR_MINUTE |
HOUR_SECOND |
IF |
IGNORE |
IN |
INDEX |
INFILE |
INNER |
INOUT |
INSENSITIVE |
INSERT |
INT |
INT1 |
INT2 |
INT3 |
INT4 |
INT8 |
INTEGER |
INTERVAL |
INTO |
IS |
ITERATE |
JOIN |
KEY |
KEYS |
KILL |
LABEL |
LEADING |
LEAVE |
LEFT |
LIKE |
LIMIT |
LINEAR |
LINES |
LOAD |
LOCALTIME |
LOCALTIMESTAMP |
LOCK |
LONG |
LONGBLOB |
LONGTEXT |
LOOP |
LOW_PRIORITY |
MATCH |
MEDIUMBLOB |
MEDIUMINT |
MEDIUMTEXT |
MIDDLEINT |
MINUTE_MICROSECOND |
MINUTE_SECOND |
MOD |
MODIFIES |
NATURAL |
NOT |
NO_WRITE_TO_BINLOG |
NULL |
NUMERIC |
ON |
OPTIMIZE |
OPTION |
OPTIONALLY |
OR |
ORDER |
OUT |
OUTER |
OUTFILE |
PRECISION |
PRIMARY |
PROCEDURE |
PURGE |
RAID0 |
RANGE |
READ |
READS |
REAL |
REFERENCES |
REGEXP |
RELEASE |
RENAME |
REPEAT |
REPLACE |
REQUIRE |
RESTRICT |
RETURN |
REVOKE |
RIGHT |
RLIKE |
SCHEMA |
SCHEMAS |
SECOND_MICROSECOND |
SELECT |
SENSITIVE |
SEPARATOR |
SET |
SHOW |
SMALLINT |
SPATIAL |
SPECIFIC |
SQL |
SQLEXCEPTION |
SQLSTATE |
SQLWARNING |
SQL_BIG_RESULT |
SQL_CALC_FOUND_ROWS |
SQL_SMALL_RESULT |
SSL |
STARTING |
STRAIGHT_JOIN |
TABLE |
TERMINATED |
THEN |
TINYBLOB |
TINYINT |
TINYTEXT |
TO |
TRAILING |
TRIGGER |
TRUE |
UNDO |
UNION |
UNIQUE |
UNLOCK |
UNSIGNED |
UPDATE |
USAGE |
USE |
USING |
UTC_DATE |
UTC_TIME |
UTC_TIMESTAMP |
VALUES |
VARBINARY |
VARCHAR |
VARCHARACTER |
VARYING |
WHEN |
WHERE |
WHILE |
WITH |
WRITE |
X509 |
XOR |
YEAR_MONTH |
ZEROFILL |
Linux控制台的用户自动登录
在/sbin目录下新建文件autologin,并往文件写入下面内容:
exec 0</dev/$1 1>/dev/$1 2>&1
cat /etc/issue
shift
exec $*
改变autologin文件的权限执行下面语句:
chmod 777 /sbin/autologin
最后编辑/etc/inittab文件
把类似以下部分
2:2345:respawn:/sbin/getty tty2
修改为
2:2345:respawn:/sbin/autologin tty2 login –f sybase
以上为系统启动时,sybase用户自动登录.
关于
Servlet
中的事件监听
在
Servlet2.3
规范中添加了一些监听
web
应用中重要事件的能力。这项功能可以让我们根据事件的状态更有效的对资源进行管理和自动化进行。这部分描述了
servlet
的事件监听,包含以下部分:
1.
事件的分类和
Listener
接口
2.
典型的事件监听过程
3.
事件监听的声明和调用
4.
事件监听编码和发布向导
5.
事件监听的方法和相关的类
事件的分类和
Listener
接口
Servlet
事件有两个级别:
1.
Application
级别事件
包含着运行应用程序的虚拟机级别的相关资源和状态,即和
servlet
的
Context
对象相关。
2.
Session
级别的事件
包含着一个单一用户的
session
的一系列请求的相关资源和状态,即
Http
的
Session
对象。
在上面两个级别的事件,又可分别分为两种:
1.
生命周期的改变
2.
属性的改变
你可以为上面四种事件创建一个或多个监听类。一个单一的监听类可以监视多种事件。
创建一个事件类可以从
javax.servlet
包或
javax.servlet.http
包中实现合适的接口。下表中列出了四种事件相关的接口。
事件种类
|
事件描述
|
接口
|
Context
生命周期的改变
|
context
的建立和即将关闭
context
|
Javax.servlet.ServletContextListener
|
Context
属性值的改变
|
添加,删除,修改
context
的属性值
|
Javax..servlet.ServletContextAttributeListener
|
Session
生命周期的改变
|
Session
的创建,注销,超时
|
Javax.servlet.http.HttpSessionListener
|
Session
属性值的改变
|
添加,删除,修改
session
的属性值
|
Javax.servlet.htpp.HttpSessionAttributeListener
|
典型的事件监听过程
考虑一个
web
应用是由一组访问数据库的
servlet
组成的。一个典型的事件监听机制是这样的,创建一个
context
生命周期的事件来管理数据库连接,这个监听器可以有如下的功能:
1.
这个监听器监视着应用程序的启动
2.
这个应用程序写入日志到数据库中并且把连接对象存储在
context
中
3.
Servelt
使用连接对象来执行
SQL
4.
监听器监听应用程序的即将关闭
5.
在关闭应用程序之前,先关闭连接对象
事件监听的声明和调用
事件监听的声明在应用程序的
web.xml
里,用
<listener>
元素,该元素是
<web-app>
的子元素。每个监听器都对应一个
<listener>
,有一个
<listener-class>
子元素用来指定对应的类名。在每种事件中,你需要指定你想调用的顺序。
在应用程序启动之后,并且在第一次请求之前,
servlet
容器会创建并注册每个监听类的实例。每种事件,监听器是按照他们声明的顺序来注册的。然后,当应用程序开始运行,每种事件监听器安装他们的顺序调用。在最后一次请求之前,所有的监听器都保持活动状态。
一旦应用程序关闭,
session
事件首先发生,以他们声明的顺序相反。然后
context
事件发生也是以声明的顺序相反。
下面是一个例子:
<web-app>
<display-name>MyListeningApplication</display-name>
<listener>
<listener-class>com.acme.MyConnectionManager</listenerclass>
</listener>
<listener>
<listener-class>com.acme.MyLoggingModule</listener-class>
</listener>
<servlet>
<display-name>RegistrationServlet</display-name>
...
</servlet>
</web-app>
假设
MyConnectionMnanager
和
MyLoggingModule
都是实现
ServletContextListener
接口,
MyLoggingModule
也是实现了
HttpSessionListener
接口。
当应用程序运行,两个监听器都会监听
context
生命周期事件,
MyLoggingModule
监听器还会监听
session
生命周期。在
context
生命周期中,
MyConnectionMananger
会首先开始监听,因为它声明在前面。
事件监听器的编码和发布指南
请注意事件监听器类的以下规则和指南:
l
在多线程的应用程序中,属性可能同时改变。这是不需要
Servlet
容器来同步结果――在这种情况下监听类本身负责保持数据的完整性。
l
每个监听类都必须有一个
public
的零参数的构造函数。
l
每个监听类文件必须打包到
WAR
文件,也可以是在
/WEB-INF/classes
或是包含在
/WEB-INF/lib
下的
JAR
文件中。
注意:在分布式的环境中,事件监听类的作用域是包含这个部署描述文件的虚拟机。不需要分布式的
Web
容器来传递
servlet
的
context
事件或是
session
事件到其他的虚拟机。这个在
Sun Microsystem
的
Java Servlet
规范,
2.3
版本。
事件监听器的方法和相关的类
这部分列出了事件监听器的方法,当
servlet
的
context
事件或是
servlet
的
session
事件发生时,容器将会调用他们。这些方法的输入的事件对象的类型不一样,因此一下讨论事件类和他们的方法。
ServletContextListener
方法,
ServletContextEvent
类
ServletContextListener
接口规范以下的方法:
void contextInitialized(ServletContextEvent sce)
servlet
容器调用这个方法来通知监听器,
servlet
的
context
已经建立并且应用程序准备处理请求。
void contextDestory(ServletContextEvent sce)
servlet
容器调用这个方法来通知监听器应用程序即将关闭。
Servlet
容器创建一个
java.servlet.ServletContextEvent
对象作为调用
ServletContextListener
方法的输入。
ServletContextEvent
类包含以下方法,你的监听器可以调用
ServletContext getServletContext()
用这个方法返回已创建的或是将要销毁的
servlet context
对象,从中你可以得到你想要的信息。(未完待续)
为了加快发传真的速度,弄了个串口卡(4口),加上原来的主板上的串口,一共现在可以接五个外置modem了。 在linux安装串口卡其实很简单: 1、把串口卡插入PCI插槽中 2、开启计算机,以root用户登陆 命令#more /proc/pci 找到这串口卡的相关信息 Bus 1, device 9, function 0: Serial controller: PCI device 9710:9845 (NetMos Technology) (rev 1). IRQ 9. Master Capable. Latency=32. I/O at 0x9000 [0x9007]. I/O at 0x9400 [0x9407]. I/O at 0x9800 [0x9807]. I/O at 0x9c00 [0x9c07]. I/O at 0xa000 [0xa007]. I/O at 0xa400 [0xa40f]. 可以知道各个串口的地址和IRQ。 3、添加串口 命令 #setserial /dev/ttyS2 port 0x9000 UART 16550A irq 9 Baud_base 115200 #setserial /dev/ttyS3 port 0x9400 UART 16550A irq 9 Baud_base 115200 #setserial /dev/ttyS4 port 0x9800 UART 16550A irq 9 Baud_base 115200 #setserial /dev/ttyS5 port 0x9c00 UART 16550A irq 9 Baud_base 115200 4、用minicom测试下 OK之后证明安装成功了。
1) 好好规划自己的路,不要跟着感觉走
根据个人的理想决策安排,绝大部分人并不指望成为什么院士或教授,而是希望活得滋润一些,爽一些。那么,就需要慎重安排自己的轨迹。从哪个行业入手,逐渐对该行业深入了解,不要频繁跳槽,特别是不要为了一点工资而转移阵地,从长远看,这点钱根本不算什么,当你对一个行业有那么几年的体会,以后钱根本不是问题。频繁地动荡不是上策,最后你对哪个行业都没有摸透,永远是新手!
2) 可以做技术,切不可沉湎于技术
千万不可一门心思钻研技术!给自己很大压力,如果你的心思全部放在这上面,那么注定你将成为孔乙己一类的人物!适可而止为之,因为技术只不过是你今后前途的支柱之一,而且还不是最大的支柱,除非你只愿意到老还是个工程师!
3) 不要去做技术高手,只去做综合素质高手
在企业里混,我们时常瞧不起某人,说他什么都不懂,凭啥拿那么多钱,凭啥升官这是普遍的典型的工程师的迂腐之言。很牛吗?人家能上去必然有他的本事,而且是你没有的本事。你想想,老板搞经营那么多年,难道见识不如你这个新兵?人家或许善于管理,善于领会老板意图,善于部门协调等等。因此务必培养自己多方面的能力,包括管理,亲和力,察言观色能力,攻关能力等,要成为综合素质的高手,则前途无量,否则只能躲在角落看示波器!技术以外的技能才是更重要的本事!!从古到今,美国日本,一律如此!
4) 多交社会三教九流的朋友
不要只和工程师交往,认为有共同语言,其实更重要的是和其他类人物交往,如果你希望有朝一日当老板或高层管理,那么你整日面对的就是这些人。了解他们的经历,思维习惯,爱好,学习他们处理问题的模式,了解社会各个角落的现象和问题,这是以后发展的巨大的本钱,没有这些以后就会笨手笨脚,跌跌撞撞,遇到重重困难,交不少学费,成功的概率大大降低!
5) 知识涉猎不一定专,但一定要广
多看看其他方面的书,金融,财会,进出口,税务,法律等等,为以后做一些积累,以后的用处会更大!会少交许多学费!!
6) 抓住时机向技术管理或市场销售方面的转变
要想有前途就不能一直搞开发,适当时候要转变为管理或销售,前途会更大,以前搞技术也没有白搞,以后还用得着。搞管理可以培养自己的领导能力,搞销售可以培养自己的市场概念和思维,同时为自己以后发展积累庞大的人脉!应该说这才是前途的真正支柱!
7) 逐渐克服自己的心里弱点和性格缺陷
多疑,敏感,天真(贬义,并不可爱),犹豫不决,胆怯,多虑,脸皮太薄,心不够黑,教条式思维。。。这些工程师普遍存在的性格弱点必须改变!很难吗?只在床上想一想当然不可能,去帮朋友守一个月地摊,包准有效果,去实践,而不要只想!不克服这些缺点,一切不可能,甚至连项目经理都当不好——尽管你可能技术不错!
8) 工作的同时要为以后做准备
建立自己的工作环境!及早为自己配置一个工作环境,装备电脑,示波器(可以买个二手的),仿真器,编程器等,业余可以接点活,一方面接触市场,培养市场感觉,同时也积累资金,更重要的是准备自己的产品,咱搞技术的没有钱,只有技术,技术的代表不是学历和证书,而是产品,拿出象样的产品,就可技术转让或与人合作搞企业!先把东西准备好,等待机会,否则,有了机会也抓不住!
9) 要学会善于推销自己
不仅要能干,还要能说,能写,善于利用一切机会推销自己,树立自己的品牌形象,很必要!要创造条件让别人了解自己,不然老板怎么知道你能干?外面的投资人怎么相信你?提早把自己推销出去,机会自然会来找你!搞个个人主页是个好注意!!特别是培养自己在行业的名气,有了名气,高薪机会自不在话下,更重要的是有合作的机会...
10) 该出手时便出手
永远不可能有100%把握!!!条件差不多就要大胆去干,去闯出自己的事业,不要犹豫,不要彷徨,干了不一定成功,但至少为下一次冲击积累了经验,不干永远没出息,而且要干成必然要经历失败。不经历风雨,怎么见彩虹,没有人能随随便便成功!
在传真量很大的部门,用传真机发送传真有时候不仅浪费资源(很多东西都是先需要打印出来然后再去传真),而且效率低下,也不便于归档管理。大家都知道,
Windows xp
提供了传真服务组件,需要使用一个
modem
,就可以供传真服务。只要是能够打印的东西,都能够传真,这样确实方便不少。但是,实际使用中会有一个比较严重的问题,就是
fax
并不像打印机一样可以共享使用,这样网络中的其他机器就不能使用该
modem
进行传真。很自然的想到使用第三方的软件,思路应该是基于传真服务器或是传真客户端的模式。遗憾的是我没有找到满意的软件,而且都是价格不菲。
关键的时候想到了开源的东西,经过搜索,锁定在
Hylaxfax
上面,这是一个
linux
下面的
fax
服务器,选择它的理由是,网站的文档非常详细,并且有很多成熟的开源的客户端。
Hylafax软件的安装和配置
Hylafax
(
http://www.hylafax.org/
)上有很详细的介绍怎么进行安装。
http://www.hylafax.org/content/Handbook:Binary_Package_Install
安装步骤很简单,但是我安装的时候好像需要一个
sharutils-4.2.1-9.i386.rpm
包,但是文档中没有提出,如果安装提示的时候可以另外下载。
另外,在天极上面也有一片不错的文章
http://www.yesky.com/20030318/1657865.shtml
。
客户端的配置
天极那篇文章上作者推荐了
whfc
,我觉得这个软件不太适合我。我觉得比较方便的是
HylaFSP
,这个软件使用起来就像是
windows
自带的传真服务,然后把传真请求发送到
Hylafax
的传真队列中,但是这个软件不是免费的,而且没有提供管理
Hylafax
所有传真文件的功能。
先说说客户端的配置,默认情况下
Hylafax
是不允许任何的网络用户使用传真服务,需要编辑
hots.hylafax
文件。或是通过
faxadduser
来添加用户和主机,另外一点就是不要忘了防火墙的设置,还有
Hlafax
默认开放的端口是
4579
。
给出一篇参考的文档:
http://fanqiang.chinaunix.net/a1/b1/20020111/08100047.html
另外就是说说,我们怎么进行开发,用
java
。
先看看一个小软件
JHylaFAX
(
http://jhylafax.sourceforge.net/
),
它是一个
Hylafax
的
java
客户端
,
而且是开源的,我们可以研究它的代码来实现自己的传真程序。其实
JHylaFAX
使用到另外一个开源项目
gnu.hylafax
(
http://gnu-hylafax.sourceforge.net/
),使用起来很简单的,看看例子文档应该就不成问题了。现在剩下的就是用你的想像去实现传真了。
甚至你可以有一个网站来提供传真服务,和邮件服务来互相绑定。
网上主要流传有两种方式实现系统托盘: 1.Windows Tray Icon (http://jeans.studentenweb.org/java/trayicon/trayicon.html)
2.SysTray for Java (http://systray.sourceforge.net/)
这两个都是开源的...可以根据上面的下载.
相对来说,我更喜欢SysTray for Java,原因很简单,SysTray for Java实现了我所要的功能而且相对来说比Windows Tray Icon 要简单.
使用SysTray是很简单的.下载下来的文件有个例子Example.java,照着这个实现你所需要的功能应该不算困难.
主要是菜单和按钮的操作,和操作一般的JFrame一样.
下面是一个例子程序:
package qiya.systray;import java.awt.Dimension; import java.awt.Font; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import snoozesoft.systray4j.SysTrayMenu; import snoozesoft.systray4j.SysTrayMenuEvent; import snoozesoft.systray4j.SysTrayMenuIcon; import snoozesoft.systray4j.SysTrayMenuItem; import snoozesoft.systray4j.SysTrayMenuListener; public class MainFrame extends JFrame implements ActionListener, SysTrayMenuListener { static final int INIT_WIDTH = 400;// 默认窗口宽度 static final int INIT_HEIGHT = 244;// 默认窗口高度 private static final String toolTip = "宽带计费接口";// 提示文字 static final SysTrayMenuIcon icon = new SysTrayMenuIcon("rocket.gif");// 图片信息 SysTrayMenu menu;// 菜单 private JButton launchButton = new JButton();// 启动按钮 private JButton exitButton = new JButton();// 退出按钮 private JLabel statusLabel = new JLabel();// 运行状态 public static void main(String[] args) { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (UnsupportedLookAndFeelException e) { e.printStackTrace(); } new MainFrame(); } public MainFrame() { super("宽带计费接口");// 标题 setIconImage(new ImageIcon(getClass().getResource("rocket.gif")) .getImage());// 图标 this.setLayout(null); this.setSize(new Dimension(INIT_WIDTH, INIT_HEIGHT)); Dimension dimScreen = Toolkit.getDefaultToolkit().getScreenSize(); int xPos = (dimScreen.width - INIT_WIDTH) / 2; int yPos = (dimScreen.height - INIT_HEIGHT) / 2; statusLabel.setText("系统状态监视"); statusLabel.setBounds(new Rectangle(45, 35, 280, 40)); statusLabel.setToolTipText("当前系统的运行状态"); statusLabel.setFont(new Font("宋体", 0, 14)); launchButton.setText("启动"); launchButton.setBounds(new Rectangle(80, 180, 80, 23)); exitButton.setText("退出"); exitButton.setBounds(new Rectangle(230, 180, 80, 23)); this.getContentPane().add(statusLabel, null); this.getContentPane().add(launchButton, null); this.getContentPane().add(exitButton, null); launchButton.addActionListener(this); exitButton.addActionListener(this); this.setBounds(xPos, yPos, INIT_WIDTH, INIT_HEIGHT); icon.addSysTrayMenuListener(this); this.createMenu(); this.setVisible(true); } /** * 创建菜单 * */ private void createMenu() { SysTrayMenuItem subItem1 = new SysTrayMenuItem("退出", "退出"); subItem1.addSysTrayMenuListener(this); SysTrayMenuItem subItem2 = new SysTrayMenuItem("关于", "关于"); subItem2.addSysTrayMenuListener(this); SysTrayMenuItem subItem3 = new SysTrayMenuItem("帮助", "帮助"); subItem3.addSysTrayMenuListener(this); menu = new SysTrayMenu(icon, toolTip);// 生成菜单 menu.addItem(subItem1); menu.addSeparator(); menu.addItem(subItem2); menu.addItem(subItem3); } /** * 点击按钮事件 */ public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equalsIgnoreCase("退出")) { System.exit(0); } else if (e.getActionCommand().equalsIgnoreCase("启动")) { // 启动计费程序 } } /** * 菜单选择事件 */ public void menuItemSelected(SysTrayMenuEvent e) { if (e.getActionCommand().equalsIgnoreCase("退出")) { System.exit(0); } else if (e.getActionCommand().equalsIgnoreCase("关于")) { JOptionPane.showMessageDialog(this, "宽带计费接口" + "完成于2005-3-27"); } else if (e.getActionCommand().equalsIgnoreCase("帮助")) { JOptionPane.showMessageDialog(this, "宽带计费接口" + "帮助文件待写..."); } } /** * 左键单击事件 */ public void iconLeftClicked(SysTrayMenuEvent e) { if (this.isVisible()) {// 如果可见,最小化 this.setVisible(false); } else {// 如果不可见显示出来 this.setVisible(true); } } /** * 左键双击事件 */ public void iconLeftDoubleClicked(SysTrayMenuEvent e) { if (this.isVisible()) {// 如果可见,最小化 this.setVisible(false); } else {// 如果不可见显示出来 this.setVisible(true); } } }
( Generic algorithms for Java)jga is a functors library: the intent is to explore and exploit functors as a design and implementation tool to reduce boilerplate coding. A functor is an object that encapsulates a function or expression: it can take arguments and produce results, as can any method, expression, or function (in other languages that support functions). Unlike an expression, as an object it can be passed as an argument without being executed; it can be persisted to a database or file; it can be serialized and passed from client to server (and back); and it can be instantiated at runtime based on information unavailable at compile-time.
美国联邦法律规定: 1)不得与豪猪发生性关系。(靠,谁敢呀) 2)每周四晚6:00以后不得放P。(以后还真要小心了,别一不留神坐牢了还不知为啥) 3)任何人不得销售其子女。(好象中国也不许吧?)
阿拉巴马州: 无论任何时候,将冰激淋卷放在口袋里是违法的。(有病啊~~)
阿肯色州: 男性可以合法殴打其配偶,但每月最多一次。 (估计很多暴力倾向的兄弟知道了一定想移民阿肯色了, 可也有例外呀,克林顿就是阿肯色的前州长,咋老被希拉里扁呀)
亚利桑纳州: 任何房间中不得有两根以上的假阳具。 (估计那州的最高法官是个变态狂!)
夏威夷州: 不得将谷物放在耳朵里。(神经病,以为偷太空种子呀)
印弟安纳州: 1)任何年满18岁的男性,若与17岁以下的女性发生性关系,而且当时她又没穿鞋袜,那将课重罪. (兄弟们千万注意了呀!别全了) 2)圆周率在该州法定为4。(活活气死咱祖聪之前辈呀!)
爱荷华州: 1)任何只有一只上臂的钢琴演奏者必须免费演奏。(严重歧视残疾艺术表演家) 2)任何有胃病的男性不得在公共场所与女性接吻。(接吻和胃有关系吗?男性胃癌晚期患者的福音
纽约州: 1)不得仅为娱乐而将球砸向他人脑袋。(谋杀可以不?真的脑子进水了) 2)10:00以后不得穿拖鞋。(光脚吧)
新泽西州: 凡谋杀时不得穿防弹背心。(管得着吗,警察这么没自信!)
北卡州: 任何一位未婚男性与一为未婚女性,如果在任何旅馆或汽车旅馆登记为已婚,那么他们即算合法夫妻了。(想带小蜜开房的兄弟们千万别去那州呀!)
宾西法尼亚州: 不得在浴室唱歌。(难怪在宾大商学院的同胞都不会K歌)
南卡州: 仅在每周六,男性被允许在法院的门前台阶上合法殴打其配偶。(这是啥规定,郁闷ING)
犹他州: 1)不喝牛奶违法。(喝不完援助非洲难民呀,干么为难自己! 难怪俺一只要喝牛奶就拉肚子的朋友从犹大转到纽约了,保命要紧呀。) 2)不得在正在执行急救任务的救护车后座上做爱。(这好理解,怕病人看见血管爆裂么!哈哈)
一、穷 我想这已经是共识,没钱?那就别想谈女朋友了,别再幻想着什么纯洁无暇爱情了,都是扯淡。当你憋着小嘴愤愤不平的拿这个诅咒分手女友的无情的时候,那只能说明你无能懦夫懒惰加上小人,离开你是明智选择。给你个富婆说不定你绝情的嘴脸比谁都更丑陋。所以,男人,想讨老婆就赚钱吧不管你混B社会杀人放火贩毒contraband只要你能弄到票子女人就多得让你眼花了。 二、丑 经典语句,长得丑不是你的错,出来吓人就是你不对了,如果你敢说一句外表不是主要的,心灵美才是重要的,打住,那我就给你一头猪一样的恐龙做老婆我看你什么反映,己所不欲勿施与人,当你做着癞蛤蟆想吃天鹅肉的美梦时,女孩还在走着灰姑娘的神呢。 三、薄 这个薄是脸皮薄的意思,如果你文质彬彬,甚至会害羞,傻傻的,你如果不是内心真有点城府的话,那看来你跟异性也是绝缘了,别想着八十年代电影里女孩说傻的可爱了,时代不同了,并且进步太快了。如果你能达到不要脸的流氓地步,也是泡妞的最高境界了。 四、专 这里指的是在一棵树上吊死,如果你是个痴情专一的汉字,对一个MM渴望用坚持和痴情来感动,我想你离沉重打击不远了,现在的MM才不喜欢你用情专一呢,如果你能到处沾花惹草正说明你的本事,MM虽然会幽怨,其实底子里才更看得起你呢。保证你召之即来,挥之即去。 五、笨 这里的笨是指嘴笨,不需要你多厉害的口才,但是甜言蜜语是要会说的,哪怕你心里想着另一个MM,口里把对这个MM的感情换个名字说给那个听也行,爱的肉麻。 六、真 其实和五差不多,就是太真诚了,MM是要来骗和哄的,你傻傻的都说实话掏老底,嘿嘿,那你也等着MM和你拜拜吧! 七、小 此小非彼小,嘿嘿,是胆小,好像和三类似,但也不是,只要有机会,能拉手早拉手,拉了手了要赶快亲,亲完了赶快找地上床,不要觉得太快了,现在是个效率的社会,你不快有人比你快,也别担心MM会觉得你轻浮,她比你还急呢。不信?等上了床你就知道了。 八、跑 专指看贴不回贴就跑了的人,这类也是找不到女朋友的,自己掂量吧!
目录
前言 一. Java 文档和 javadoc 二. 文档注释的格式 1. 文档注释的格式化 2. 文档注释的三部分 三. 使用 javadoc 标记 1. @see 的使用 2. 使用 @author、@version 说明类 3. 使用 @param、@return 和 @exception 说明方法 四. javadoc 命令
前言
Java 的语法与 C++ 及为相似,那么,你知道 Java 的注释有几种吗?是两种?
// 注释一行 /* ...... */ 注释若干行
不完全对,除了以上两种之外,还有第三种,文档注释:
/** ...... */ 注释若干行,并写入 javadoc 文档
通常这种注释的多行写法如下:
/** * ......... * ......... */
暂停,暂停!这第三种注释有什么用?javadoc 又是什么东西?
好,那就让我告诉你——
一. Java 文档和 javadoc
Java 程序员都应该知道使用 JDK 开发,最好的帮助信息就来自 SUN 发布的 Java 文档。它分包、分类详细的提供了各方法、属性的帮助信息,具有详细的类树信息、索引信息等,并提供了许多相关类之间的关系,如继承、实现接口、引用等。
Java 文档全是由一些 html 文件组织起来的,在 SUM 的站点上可以下载它们的压缩包。但是你肯定想不到,这些文档我们可以自己生成。——就此打住,再吊一次胃口。
安装了 JDK 之后,安装目录下有一个 src.jar 文件或者 src.zip 文件,它们都是以 ZIP 格式压缩的,可以使用 WinZip 解压。解压之后,我们就可以看到分目录放的全是 .java 文件。是了,这些就是 Java 运行类的源码了,非常完整,连注释都写得一清二楚……不过,怎么看这些注释都有点似曾相识的感觉?
这就不奇怪了,我们的迷底也快要揭开了。如果你仔细对比一下 .java 源文件中的文档注释 (/** ... */) 和 Java 文档的内容,你会发现它们就是一样的。Java 文档只是还在格式和排版上下了些功夫。再仔细一点,你会发现 .java 源文件中的注释还带有 HTML 标识,如 <B>、<BR>、<Code> 等,在 Java 文档中,该出现这些标识的地方,已经按标识的的定义进行了排版。
终于真像大白了,原来 Java 文档是来自这些注释。难怪这些注释叫做文档注释呢!不过,是什么工具把这些注释变成文档的呢?
是该请出 javadoc 的时候了。在 JDK 的 bin 目录下你可以找到 javadoc,如果是 Windows 下的 JDK,它的文件名为 javadoc.exe。使用 javdoc 编译 .java 源文件时,它会读出 .java 源文件中的文档注释,并按照一定的规则与 Java 源程序一起进行编译,生成文档。
介绍 javadoc 的编译命令之前,还是先了解一下文档注释的格式吧。不过为了能够编译下面提到的若干例子,这里先介绍一条 javadoc 命令:
javadoc -d 文档存放目录 -author -version 源文件名.java
这条命令编译一个名为 “源文件名.java”的 java 源文件,并将生成的文档存放在“文档存放目录”指定的目录下,生成的文档中 index.html 就是文档的首页。-author 和 -version 两个选项可以省略。
二. 文档注释的格式
文档注释可以用于对类、属性、方法等进行说明。写文档注释时除了需要使用 /** .... */ 限定之外,还需要注意注释内部的一些细节问题。
1. 文档和文档注释的格式化
生成的文档是 HTML 格式,而这些 HTML 格式的标识符并不是 javadoc 加的,而是我们在写注释的时候写上去的。比如,需要换行时,不是敲入一个回车符,而是写入 <br>,如果要分段,就应该在段前写入 <p>。
因此,格式化文档,就是在文档注释中添加相应的 HTML 标识。
文档注释的正文并不是直接复制到输出文件 (文档的 HTML 文件),而是读取每一行后,删掉前导的 * 号及 * 号以前的空格,再输入到文档的。如
|
/** * This is first line. <br> ***** This is second line. <br> This is third line. */ |
编译输出后的 HTML 源码则是
|
This is first line. <br> This is second line. <br> This is third line. |
前导的 * 号允许连续使用多个,其效果和使用一个 * 号一样,但多个 * 号前不能有其它字符分隔,否则分隔符及后面的 * 号都将作为文档的内容。* 号在这里是作为左边界使用,如上例的第一行和第二行;如果没有前导的 * 号,则边界从第一个有效字符开始,而不包括前面的空格,如上例第三行。
还有一点需要说明,文档注释只说明紧接其后的类、属性或者方法。如下例:
|
/** comment for class */
public class Test {
/** comment for a attribute */
int number;
/** comment for a method */
public void myMethod() { ...... }
......
} |
上例中的三处注释就是分别对类、属性和方法的文档注释。它们生成的文档分别是说明紧接其后的类、属性、方法的。“紧接”二字尤其重要,如果忽略了这一点,就很可能造成生成的文档错误。如
|
import java.lang.*;
/** commnet for class */
public class Test { ...... }
// 此例为正确的例子 |
这个文档注释将生成正确的文档。但只需要改变其中两行的位置,变成下例,就会出错:
|
/** commnet for class */
import java.lang.*;
public class Test { ...... }
// 此例为错误的例子 |
这个例子只把上例的 import 语句和文档注释部分交换了位置,结果却大不相同——生成的文档中根本就找不到上述注释的内容了。原因何在?
“/** commnet for class */”是对 class Test 的说明,把它放在“public class Test { ...... }”之前时,其后紧接着 class Test,符合规则,所以生成的文档正确。但是把它和“import java.lang.*;”调换了位置后,其后紧接的就是不 class Test 了,而是一个 import 语句。由于文档注释只能说明类、属性和方法,import 语句不在此列,所以这个文档注释就被当作错误说明省略掉了。
2. 文档注释的三部分
根据在文档中显示的效果,文档注释分为三部分。先举例如下,以便说明。
|
/**
* show 方法的简述.
* <p>show 方法的详细说明第一行<br>
* show 方法的详细说明第二行
* @param b true 表示显示,false 表示隐藏
* @return 没有返回值
*/
public void show(boolean b) {
frame.show(b);
} |
第一部分是简述。文档中,对于属性和方法都是先有一个列表,然后才在后面一个一个的详细的说明。列表中属性名或者方法名后面那段说明就是简述。如下图中被红框框选的部分:
简述部分写在一段文档注释的最前面,第一个点号 (.) 之前 (包括点号)。换句话说,就是用第一个点号分隔文档注释,之前是简述,之后是第二部分和第三部分。如上例中的 “* show 方法的简述.”。
有时,即使正确地以一个点号作为分隔,javadoc 仍然会出错,把点号后面的部分也做为了第一部分。为了解决这个问题,我们可以使用一个 <p> 标志将第二分部分开为下一段,如上例的“* <p>show 方法的详细说明第一行 ....”。除此之外,我们也可以使用 <br> 来分隔。
第二部分是详细说明部分。该部分对属性或者方法进行详细的说明,在格式上没有什么特殊的要求,可以包含若干个点号。它在文档中的位置如下图所示:
这部分文档在上例中相应的代码是:
* show 方法的简述. * <p>show 方法的详细说明第一行<br> * show 方法的详细说明第二行
发现什么了?对了,简述也在其中。这一点要记住了,不要画蛇添足——在详细说明部分中再写一次简述哦!
第三部分是特殊说明部分。这部分包括版本说明、参数说明、返回值说明等。它在文档中的位置:
第三部分在上例中相应的代码是
* @param b true 表示显示,false 表示隐藏 * @return 没有返回值
除了 @param 和 @return 之外,还有其它的一些特殊标记,分别用于对类、属性和方法的说明……不要推我,我马上就说。
三. 使用 javadoc 标记
javadoc 标记是插入文档注释中的特殊标记,它们用于标识代码中的特殊引用。javadoc 标记由“@”及其后所跟的标记类型和专用注释引用组成。记住了,三个部分——@、标记类型、专用注释引用。不过我宁愿把它分成两部分:@ 和标记类型、专用注释引用。虽然 @ 和 标记类型之间有时可以用空格符分隔,但是我宁愿始终将它们紧挨着写,以减少出错机会。
javadoc 标记有如下一些:
标记 |
用于 |
作用 |
@author |
对类的说明 |
标明开发该类模块的作者 |
@version |
对类的说明 |
标明该类模块的版本 |
@see |
对类、属性、方法的说明 |
参考转向,也就是相关主题 |
@param |
对方法的说明 |
对方法中某参数的说明 |
@return |
对方法的说明 |
对方法返回值的说明 |
@exception |
对方法的说明 |
对方法可能抛出的异常进行说明 | |
下面详细说明各标记。
1. @see 的使用
@see 的句法有三种:
@see 类名 @see #方法名或属性名 @see 类名#方法名或属性名
类名,可以根据需要只写出类名 (如 String) 或者写出类全名 (如 java.lang.String)。那么什么时候只需要写出类名,什么时候需要写出类全名呢?
如果 java 源文件中的 import 语句包含了的类,可以只写出类名,如果没有包含,则需要写出类全名。java.lang 也已经默认被包含了。这和 javac 编译 java 源文件时的规定一样,所以可以简单的用 javac 编译来判断,源程序中 javac 能找到的类,javadoc 也一定能找到;javac 找不到的类,javadoc 也找不到,这就需要使用类全名了。
方法名或者属性名,如果是属性名,则只需要写出属性名即可;如果是方法名,则需要写出方法名以及参数类型,没有参数的方法,需要写出一对括号。如
成员类型 |
成员名称及参数 |
@see 句法 |
属性 |
number |
@see number |
属性 |
count |
@see count |
方法 |
count() |
@see count() |
方法 |
show(boolean b) |
@see show(boolean) |
方法 |
main(String[] args) |
@see main(String[]) |
有时也可以偷懒:假如上例中,没有 count 这一属性,那么参考方法 count() 就可以简写成 @see count。不过,为了安全起见,还是写全 @see count() 比较好。
@see 的第二个句法和第三个句法都是转向方法或者属性的参考,它们有什么区别呢?
第二个句法中没有指出类名,则默认为当前类。所以它定义的参考,都转向本类中的属性或者方法。而第三个句法中指出了类名,则还可以转向其它类的属性或者方法。
关于 @see 标记,我们举个例说明。由于 @see 在对类说明、对属性说明、对方法说明时用法都一样,所以这里只以对类说明为例。
|
/**
* @see String
* @see java.lang.StringBuffer
* @see #str
* @see #str()
* @see #main(String[])
* @see Object#toString()
*/
public class TestJavaDoc {
} |
生成的文档的相关部分如下图:
String 和 StringBuffer 都是在 java.lang 包中,由于这个包是默认导入了的,所以这两个类可以直接写类名,也可以写类全名。str、str() 为同名属性和方法,所以方法名需要用 () 区分。main 是带参数的方法,所以在 () 中指明了参数类型。toString() 虽然在本类中也有 (从 Object 继承的),但我们是想参考 Object 类的 toString() 方法,所以使用了 Object#toString()。
奇怪的是,为什么其中只有 str、str() 和 main(String[]) 变成了链接呢?那是因为编译时没有把 java.lang 包或者 Stirng、StringBuffer、Object 三个类的源文件一起加入编译,所以,生成的文档没有关于那三个类的信息,也就不可以建立链接了。后面讲解 javadoc 编译命令的时候还会详细说明。
上例中如果去把类中的 str 属性去掉,那么生成的文档又会有什么变化呢?你会发现,原来是 str, str(),而现在变成了 str(), str(),因为 str 属性已经没有了,所以 str 也表示方法 str()。
2. 使用 @author、@version 说明类
这两个标记分别用于指明类的作者和版本。缺省情况下 javadoc 将其忽略,但命令行开关 -author 和 -version 可以修改这个功能,使其包含的信息被输出。这两个标记的句法如下:
@author 作者名 @version 版本号
其中,@author 可以多次使用,以指明多个作者,生成的文档中每个作者之间使用逗号 (,) 隔开。@version 也可以使用多次,只有第一次有效,生成的文档中只会显示第一次使用 @version 指明的版本号。如下例
|
/**
* @author Fancy
* @author Bird
* @version Version 1.00
* @version Version 2.00
*/
public class TestJavaDoc {
} |
生成文档的相关部分如图:
从生成文档的图示中可以看出,两个 @author 语句都被编译,在文档中生成了作者列表。而两个 @version 语句中只有第一句被编译了,只生成了一个版本号。
从图上看,作者列表是以逗号分隔的,如果我想分行显示怎么办?另外,如果我想显示两个以上的版本号又该怎么办?
——我们可以将上述两条 @author 语句合为一句,把两个 @version 语句也合为一句:
@author Fancy<br>Bird @version Version 1.00<br>Version 2.00
结果如图:
我们这样做即达到了目的,又没有破坏规则。@author 之后的作者名和 @version 之后的版本号都可以是用户自己定义的任何 HTML 格式,所以我们可以使用 <br> 标记将其分行显示。同时,在一个 @version 中指明两个用 <br> 分隔的版本号,也没有破坏只显示第一个 @version 内容的规则。
3. 使用 @param、@return 和 @exception 说明方法
这三个标记都是只用于方法的。@param 描述方法的参数,@return 描述方法的返回值,@exception 描述方法可能抛出的异常。它们的句法如下:
@param 参数名 参数说明 @return 返回值说明 @exception 异常类名 说明
每一个 @param 只能描述方法的一个参数,所以,如果方法需要多个参数,就需要多次使用 @param 来描述。
一个方法中只能用一个 @return,如果文档说明中列了多个 @return,则 javadoc 编译时会发出警告,且只有第一个 @return 在生成的文档中有效。
方法可能抛出的异常应当用 @exception 描述。由于一个方法可能抛出多个异常,所以可以有多个 @exception。每个 @exception 后面应有简述的异常类名,说明中应指出抛出异常的原因。需要注意的是,异常类名应该根据源文件的 import 语句确定是写出类名还是类全名。 示例如下:
|
public class TestJavaDoc {
/**
* @param n a switch
* @param b excrescent parameter
* @return true or false
* @return excrescent return
* @exception java.lang.Exception throw when switch is 1
* @exception NullPointerException throw when parameter n is null
*/
public boolean fun(Integer n) throws Exception {
switch (n.intValue()) {
case 0:
break;
case 1:
throw new Exception("Test Only");
default:
return false;
}
return true;
}
} |
使用 javadoc 编译生成的文档相关部分如下图:
可以看到,上例中 @param b excrescent parameter 一句是多余的,因为参数只是一个 n,并没有一个 b但是 javadoc 编译时并没有检查。因此,写文档注释时一定要正确匹配参数表与方法中正式参数表的项目。如果方法参数表中的参数是 a,文档中却给出对参数 x 的解释,或者再多出一个参数 i,就会让人摸不着头脑了。@exceptin 也是一样。
上例程序中并没有抛出一个 NullPointerException,但是文档注释中为什么要写上这么一句呢,难道又是为了演示?这不是为了演示描述多余的异常也能通过编译,而是为了说明写异常说明时应考运行时 (RunTime) 异常的可能性。上例程序中,如果参数 n 是给的一个空值 (null),那么程序会在运行的时候抛出一个 NullPointerException,因此,在文档注释中添加了对 NullPointerException 的说明。
上例中的 @return 语句有两个,但是根据规则,同一个方法中,只有第一个 @return 有效,其余的会被 javadoc 忽略。所以生成的文档中没有出现第二个 @return 的描述。
讲到这里,该怎么写文档注释你应该已经清楚了,下面就开始讲解 javadoc 的常用命令。
四. javadoc 命令
运行 javadoc -help 可以看到 javadoc 的用法,这里列举常用参数如下:
用法: javadoc [options] [packagenames] [sourcefiles]
选项:
|
-public |
仅显示 public 类和成员 |
|
-protected |
显示 protected/public 类和成员 (缺省) |
|
-package |
显示 package/protected/public 类和成员 |
|
-private |
显示所有类和成员 |
|
-d <directory> |
输出文件的目标目录 |
|
-version |
包含 @version 段 |
|
-author |
包含 @author 段 |
|
-splitindex |
将索引分为每个字母对应一个文件 |
|
-windowtitle <text> |
文档的浏览器窗口标题 |
javadoc 编译文档时可以给定包列表,也可以给出源程序文件列表。例如在 CLASSPATH 下有两个包若干类如下:
fancy.Editor fancy.Test fancy.editor.ECommand fancy.editor.EDocument fancy.editor.EView
这里有两个包 (fancy 和 fancy.editor) 和 5 个类。那么编译时 (Windows 环境) 可以使用如下 javadoc 命令:
javadoc fancy\Test.java fancy\Editor.java fancy\editor\ECommand.java fancy\editor\EDocument.java fancy\editor\EView.java
这是给出 java 源文件作为编译参数的方法,注意命令中指出的是文件路径,应该根据实际情况改变。也可以是给出包名作为编译参数,如:
javadoc fancy fancy.editor
用浏览器打开生成文档的 index.html 文件即可发现两种方式编译结果的不同,如下图:
用第二条命令生成的文档被框架分成了三部分:包列表、类列表和类说明。在包列表中选择了某个包之后,类列表中就会列出该包中的所有类;在类列表中选择了某个类之后,类说明部分就会显示出该类的详细文档。而用第一条命令生成的文档只有两部分,类列表和类说明,没有包列表。这就是两种方式生成文档的最大区别了。
下面再来细说选项。
-public、-protected、-package、-private 四个选项,只需要任选其一即可。它们指定的显示类成员的程度。它们显示的成员多少是一个包含的关系,如下表:
-private (显示所有类和成员) |
-package (显示 package/protected/public 类和成员) |
-protected (显示 protected/public 类和成员) |
-public (仅显示 public 类和成员) | | | |
-d 选项允许你定义输出目录。如果不用 -d 定义输出目录,生成的文档文件会放在当前目录下。-d 选项的用法是
-d 目录名
目录名为必填项,也就是说,如果你使用了 -d 参数,就一定要为它指定一个目录。这个目录必须已经存在了,如果还不存在,请在运行 javadoc 之前创建该目录。
-version 和 -author 用于控制生成文档时是否生成 @version 和 @author 指定的内容。不加这两个参数的情况下,生成的文档中不包含版本和作者信息。
-splitindex 选项将索引分为每个字母对应一个文件。默认情况下,索引文件只有一个,且该文件中包含所有索引内容。当然生成文档内容不多的时候,这样做非常合适,但是,如果文档内容非常多的时候,这个索引文件将包含非常多的内容,显得过于庞大。使用 -splitindex 会把索引文件按各索引项的第一个字母进行分类,每个字母对应一个文件。这样,就减轻了一个索引文件的负担。
-windowtitle 选项为文档指定一个标题,该标题会显示在窗口的标题栏上。如果不指定该标题,而默认的文档标题为“生成的文档(无标题)”。该选项的用法是:
-windowtitle 标题
标题是一串没有包含空格的文本,因为空格符是用于分隔各参数的,所以不能包含空格。同 -d 类似,如果指定了 -windowtitle 选项,则必须指定标题文本。
到此为止,Java 文档和 javadoc 就介绍完了。javadoc 真的能让我们在 Java 注释上做文章——生成开发文档。
ISBN号是国际标准书号的简称,它是国际标准化组织于1972年公布的一项国际通用的出版物统一编号方法。所有正规出版的普通图书版权页都有ISBN
号,ISBN是 international standard of book number
几个英文字母的缩写,即国际标准书号。它由10位数字组成,这10位数字由组号、出版者号、书名号、校验号这四部分组成,其间用“--”相连。
ISBN号是由10位数字组成,共分四段:
1.组号: 代表出版者的国家,地理区域,语种等.我国的组号为"7"。
2.出版者号: 代表组内所属的一个具体出版者(出版社,出版公司等)。由国家或地区的ISBN中心设置和分配,可取1-7位数字。
3.书名号:书名号是由出版者给予每种出版物的编号。
4. 校验号:
校验号是ISBN号的最后一位数值,它能够校验出ISBN号是否正确,即:将ISBN1-9位数字顺序乘以10-2这9个数字,将这些乘积之和再加上校验
号,假如能被11整除,则这个ISBN号是正确的,算式为7*10+3*9+0*8 +5*7+0*6+1*5+5*4+6*3+8*2+7=
198,198/11=18,能被11整除。校验号只能是1位数,当为10时,记为罗马数字X。
该文章在<<EJB编程指南>>的实例的基础上建立的,主要是给新手一个比较直观的例子和作为自己的日志,并不打算介绍EJB的原理性的东西。另外,由于本人水平有限,请不吝赐教。 笔者使用的IDE为:Eclipse3.0+MyEclipse4.01GA J2EE容器为:JBoss4.0 本文描述一个帮助存款和取款的无状态会话Bean的完整开发及部署的过程。步骤如下: 1、编写无状态会话Bean的实现类。 2、编写无状态会话Bean的主接口和组件接口。 3、将Bean汇编成应用程序,编写部署描述项。 4、在EJB服务器上部署应用程序。 5、用Java应用程序进行测试。 上面是主要的过程,有些步骤可以不用手工完成,通过IDE可以简化开发过程,如果你对IDE的该功能不太清楚可以参考产品文档(http: //myeclipseide.com/enterpriseworkbench/help/index.jsp?topic=/com.genuitec.myeclipse.doc/html/quickstarts/firstejb/index.html)。 一、新建一个EJB Project,工程名字为FundEJB,其他默认就好。 二、创建Session Bean: 1、在src目录下新建包:qiya.deng.fund.ejb,请注意包名最后一定要以ejb为后缀,因为后面我们需要使用的XDoclet工具。 2、新建SessionBean class,命名为StatelessFundManagerEJB,要需要以EJB为后缀,原因同上,而且根据规范最好是以EJB或是Bean为后缀。 3、配置XDoclet : 右击项目选择Properties,选择MyEclipse-XDoclet,点击Add Stander...,选择Standard EJB。 选中Standard EJB,在ejbxdoclet上点击右键添加Add,在其中选择jboss,因为该例子中使用jboss作为应用服务器。选中jboss,修改下列属性 Version = 4.0 destDir = src/META-INF 修改完毕,点击OK按钮回到主窗口。 4、运行Xdoclet: 右击项目选择MyEclipse->run Xdoclet。运行是console窗口会产生提示信息,运行完毕可以看到目录结构发生变化。 5、编辑实现类StatelessFundManagerEJB: 在编辑StatelessFundManagerEJB类之前选观察下StatelessFundManager接口,一定可以发现这个远程组件接口的接口方法和StatelessFundManager的方法是有对应关系的。 在StatelessFundManager.java文件最后添加: /** * * @param balance * @param amount * @return * * @ejb.interface-method */ public double addFunds(double balance,double amount){ balance += amount; return balance; } /** * * @param balance * @param amount * @return * @throws InsufficientBalanceException * * @ejb.interface-method */ public double withdrawFunds(double balance,double amount)throws InsufficientBalanceException { if (balance < amount) { throw (new InsufficientBalanceException()); } balance -= amount; return balance; }
重复第4步运行Xdoclet,之后观察StatelessFundManager接口。 6、部署该应用到EJB服务器: 部署描述项在IDE自动生成了,该文件的位置在/META-INF/ejb-jar.xml。打开ejb-jar.xml,jboss.xml文件描述进行查看。 利用MyEclipse提供的部署工具进行部署: 然后运行JBoss容器,可以看到有如下信息提示: 关于MyEclipse中Application Server的使用请查看文档(http://www.myeclipseide.com/images/tutorials/quickstarts/appservers/)。 到现在为止,你已经发布了一个简单的无状态的会话Bean。下面写个简单的应用程序进行测试. 三、编写进行测试的Java客户端程序。 客户端程序可以是Web程序也可以是Application应用程序。这里以Application应用程序为例。 同样使用Eclipse,新建Java Project,这里命名为FundClient。右击该项目选择properties->Java Build path,在Projects中加入上面的Project:FundEJB。在Libraries中点击Add External JARs...,把$JBoss_Home/client的目录下的所有jar文件添加到Libraries中。 最后,就是编写客户端代码: package qiya.deng.client; //import省去 public class StatelessFundManagerTestClient extends JFrame implements ActionListener {
double balance = 0; JTextField amount = new JTextField(10); JButton addFunds = new JButton("Add Funds"); JButton withdrawFunds = new JButton("Withdraw Funds"); String msg = "Current account balance"; String strBal = "0"; JLabel status; StatelessFundManager manager; NumberFormat currencyFormatter; public StatelessFundManagerTestClient(){ super("Fund Manager"); } public static void main(String[] args){ new StatelessFundManagerTestClient().init(); } private void init() { buildGUI(); addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent event){ System.exit(0); } }); addFunds.addActionListener(this); withdrawFunds.addActionListener(this); createFundManager(); currencyFormatter = NumberFormat.getCurrencyInstance(); String currencyOut = currencyFormatter.format(0); status.setText(msg + currencyOut); pack(); show(); }
private void buildGUI() { GridBagLayout gl = new GridBagLayout(); GridBagConstraints gc = new GridBagConstraints(); Container container = getContentPane(); container.setLayout(gl); gc.fill = GridBagConstraints.BOTH; JLabel label = new JLabel("Enter Amount"); gl.setConstraints(label,gc); container.add(label); gc.gridwidth = GridBagConstraints.REMAINDER; gl.setConstraints(amount,gc); container.add(amount); gl.setConstraints(addFunds,gc); container.add(addFunds); gl.setConstraints(withdrawFunds,gc); container.add(withdrawFunds); status = new JLabel(msg); gl.setConstraints(status,gc); container.add(status); }
public void createFundManager(){ try { Properties prop = new Properties(); prop.put(Context.INITIAL_CONTEXT_FACTORY,"org.jnp.interfaces.NamingContextFactory"); prop.put(Context.PROVIDER_URL,"localhost:1099"); Context initial = new InitialContext(prop); Object objref = initial.lookup("ejb/StatelessFundManager");//JINI-Name StatelessFundManagerHome home = (StatelessFundManagerHome) PortableRemoteObject.narrow(objref,StatelessFundManagerHome.class); manager = home.create(); } catch (ClassCastException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } catch (NamingException e) { e.printStackTrace(); } catch (CreateException e) { e.printStackTrace(); } }
public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equalsIgnoreCase("Withdraw Funds")) { System.out.println("Withdraw Funds"); } if (e.getActionCommand().equalsIgnoreCase("Add Funds")) { System.out.println("Add Funds"); } if (e.getSource().equals(addFunds)){ System.out.println("addFunds"); try { status.setText(msg + currencyFormatter.format(manager.addFunds(0,Double.parseDouble(amount.getText())))); } catch (NumberFormatException e1) { e1.printStackTrace(); } catch (RemoteException e1) { e1.printStackTrace(); } } if (e.getSource().equals(withdrawFunds)){ System.out.println("withdrawFund"); try { status.setText(msg + currencyFormatter.format(manager.withdrawFunds(100,Double.parseDouble(amount.getText())))); } catch (NumberFormatException e1) { e1.printStackTrace(); } catch (RemoteException e1) { e1.printStackTrace(); } catch (InsufficientBalanceException e1) { e1.printStackTrace(); } } }
}
然后,你可以运行该程序进行测试了: 至此,恭喜你,你已经大功告成,基本上对EJB建立了感性的认识,可以参考资料进行深入的学习了。
今天是大年的初一,新年的第一天!!!祝所有的朋友春节快乐,狗年旺旺(好像关于狗年的贺词不多)!也希望自己新的一年来进步多多,有所作为。
因为需要对网络环境进行监控,做了个Java程序在linux服务器上运行。但是每次重新启动的时候都要手动的运行,这样就不太现实。所以想到把Java程序做成像Windows的系统服务那样,开机就会自动在后台运行。以前使用过一个工具http://javaservice.objectweb.org/,可以把Java程序注册成为Windows的系统服务,而且使用起来很简单,但是遗憾的是该工具不能注册linux的后台服务。所以不得不另外寻找工具。
最后找到了JavaService Wrapper(http://wrapper.tanukisoftware.org/doc/english/introduction.html),该工具能在很多中平台下面注册为系统服务(查看支持平台)。该工具使用方法有三种模式,我用的是第一种WrapperSimpleApp帮助类,另外的两种方式都需要在原来的程序上进行适当的编码。下面就用我的程序NetWatchDog为例子说明下大概的配置步骤。(其实文档中已经用Jboss进行了演示)
1.建立一个目录名为NetWatchDog,并在该目录下建立bin,lib,conf,logs目录。以下把NetWatchDog目录称为$DOG_HOME。
2.把下载来的文件解压,把$WRAPPER_HOME/bin/wrapper,$WRAPPER_HOME/src/bin/sh.script.in文件copy到$DOG_HOME/bin目录中。
3.把sh.script.in文件该名为你的服务名称,这里改为NetWatchDog。
4.编辑NetWatchDog,把Application的相关信息改为如下:
APP_NAME="NetWatchDog" APP_LONG_NAME="NetWatchDog Application"
5.把你的程序的打包成jar文件,拷贝到$DOG_HOME/lib目录下,并把$WRAPPER_HOME/lib/libwrapper.so,$WRAPPER_HOME/lib/wrapper.jar文件也拷贝到$DOG_HOME/lib目录下面。
6.这步比较重要是成败的关键,把$WRAPPER_HOME/conf/wrapper.conf文件拷贝到$DOG_HOME/conf目录下(文档上面说的是wrapper.conf.in文件,但是我下载来的文件就是wrapper.conf,所以步需要改名的)。
7.编辑wrapper.conf文件:
#java命令的位置
wrapper.java.command=%JAVA_HOME%/bin/java
#wrapper的主类
wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp
#java classpath
wrapper.java.classpath.1=../lib/wrapper.jar wrapper.java.classpath.2=%JAVA_HOME%/jre/lib/ext/log4j-1.2.13.jar wrapper.java.classpath.3=%JAVA_HOME%/jre/lib/ext/activation.jar wrapper.java.classpath.4=%JAVA_HOME%/jre/lib/ext/commons-email-1.0.jar wrapper.java.classpath.5=%JAVA_HOME%/jre/lib/ext/mail.jar wrapper.java.classpath.6=../lib/NetWatchDog.jar #你的程序的主类,将作为wrapper的参数
wrapper.app.parameter.1=qiya.deng.main.Main
8.这样基本就大功告成了,现在可以运行命令./NetWatchDog start,如果提示权限不够就用chmod 755 NetWatchDog命令改变权限。如果没出现错误提示,这样基本上就正确了。 另外Windwos版本可以参考:http://blog.chinaunix.com/u/1677/?u=http://blog.chinaunix.com/u/1677/showart.php?id=67084
Denny 丹尼 1.Denny这个名字让人联想到课堂上的笑蛋-爱玩友善极度幽默的年轻男孩,脑袋却不太灵光。 2.邓是我本姓
这也是个开源的标签库^_^,因为老外总是很专业些写了很多好东西,可是我基本上是和他们相反,即没有时间也比较懒得。Struts-layout是一个用来扩充Struts的html标签的作用的,我以前写过(blog里)了怎么安装使用的,这里就不说了。
1.这次我们先看JSP的结构: <head> <script src="/WebSample/config/javascript.js"></script> </head> <body> <html:form action="/country.do"> <layout:select key="Country" property="countryId"> <layout:option value=""/> <layout:options collection="countries" property="countryId" labelProperty="name" sourceOf="cityId"/> </layout:select> <layout:select key="City" property="cityId"> <layout:optionsDependent collection="cities" property="cityId" labelProperty="cityName" dependsFrom="countryId"/> </layout:select> <html:submit /><html:reset /> </html:form> </body>
两个select,其中第二个是<layout:optionsDependent/>它的dependsFrom的属性需要和上面的那个select的property一致。countries是在request域的一个collection,而cities是countries的一个属性,但是类型也是collection。 2.Action:你需要Action来初始化countries, public ActionForward execute(ActionMapping mapping,ActionForm form,HttpServletRequest request,HttpServletResponse response) { List countries = new ArrayList(); for(int i = 0 ;i< 10 ;i++){ Country c = new Country(); c.setCountryId(new Integer(i)); c.setName("country"+i); for(int j = 0;j< 5 ;j++){ c.addCity("city"+i+j,new Integer(i*10+j)); } countries.add(c); } request.setAttribute("countries",countries); return mapping.findForward("success"); }
这样你基本上就好了,够简单吧!下面还有两个类的结构就是Country类和CityBean类: Country类:3个如下属性,外加Setter/Getter方法。 private String name; private List cities = new ArrayList(); private Integer countryId; CityBean类:2个如下属性,外加Setter/Getter方法。 private Integer cityId; private String cityName; 这些东西你当然还可以和数据库结合起来使用,或是和XML文件结合起来使用。基本上一个应用最要需要考虑好 类似Country类这个结构就可以了。
这个标签的方法是把所有的数据都会写入到html文件中,并写入相应的JavaScript,你可以查看源码。 <script>var countries = new Array(); countries[0] = new Object(); countries[0].value = "0"; countries[0].cities = new Array(); countries[0].cities[0] = new Object(); countries[0].cities[0].value = "0"; countries[0].cities[0].label = "city00"; countries[0].cities[1] = new Object(); countries[0].cities[1].value = "1"; countries[0].cities[1].label = "city01"; countries[0].cities[2] = new Object(); countries[0].cities[2].value = "2"; countries[0].cities[2].label = "city02"; countries[0].cities[3] = new Object(); countries[0].cities[3].value = "3"; countries[0].cities[3].label = "city03"; countries[0].cities[4] = new Object(); countries[0].cities[4].value = "4"; countries[0].cities[4].label = "city04" ..... </script>
个人总结,水平有限。主要是最近在论坛里看到不少关于这方面的问题,然后有没有最后的答案,所以借助开源标签可以做到通用性,希望对您有所帮助。
看了一个开源的标签库AjaxTag(http://ajaxtags.sourceforge.net/index.html),提供几个比较简单的应用。我测试下了DropDownSelect应用: 1、首先当然是看安装文档(http://ajaxtags.sourceforge.net/install.html),应该没什么负责的,我要说的是第3步,也是比较重要的一步,创建服务端的控制程序。这部分也就是AjaxTag的原理,AjaxTag是通过服务器端的servelt生成XML文件,当然也可以是其他的服务器端技术只要是能生成格式良好的XML文件就可以了,文件格式如下图:
2、我们的例子就是通过选择制造商可以动态的产生该制造商的车型。下面是需要的一些类文件: Car类:包含两个String类型属性make,model分别指制造商和车型. CarService类如下: public class CarService {
static final List cars = new ArrayList();
static { cars.add(new Car("Ford", "Escape")); cars.add(new Car("Ford", "Expedition")); cars.add(new Car("Ford", "Explorer")); cars.add(new Car("Ford", "Focus")); cars.add(new Car("Ford", "Mustang")); cars.add(new Car("Ford", "Thunderbird"));
cars.add(new Car("Honda", "Accord")); cars.add(new Car("Honda", "Civic")); cars.add(new Car("Honda", "Element")); cars.add(new Car("Honda", "Ridgeline"));
cars.add(new Car("Mazda", "Mazda 3")); cars.add(new Car("Mazda", "Mazda 6")); cars.add(new Car("Mazda", "RX-8")); }
public CarService() { super(); } /*该方法在servlet被调用*/ public List getModelsByMake(String make) { List l = new ArrayList(); for (Iterator iter = cars.iterator(); iter.hasNext();) { Car car = (Car) iter.next(); if (car.getMake().equalsIgnoreCase(make)) { l.add(car); } } return l; }
public List getAllCars() { return cars; } }
DropdownServlet类: String make = request.getParameter("make"); CarService service = new CarService(); //得到该make的所有model List list = service.getModelsByMake(make); //这就是Helper类,能生成像上面的xml文件 return new AjaxXmlBuilder().addItems(list, "model", "make").toString();
4、不要忘记了在web.xml中加上映射DropdownServlet <servlet> <servlet-name>dropdown</servlet-name> <servlet-class>qiya.deng.ajaxtag.DropdownServlet</servlet-class> <load-on-startup>2</load-on-startup> </servlet>
<servlet-mapping> <servlet-name>dropdown</servlet-name> <url-pattern>/dropdown.view</url-pattern> </servlet-mapping>
5.下面就是JSP部分(需要JSTL和EL): <c:set var="contextPath" scope="request">${pageContext.request.contextPath}</c:set> <form id="carForm" name="carForm"> <fieldset> <legend>Choose Your Car</legend>
<div> <img id="makerEmblem" src="<%=request.getContextPath()%>/img/placeholder.gif" width="76" height="29" /> </div>
<label for="make">Make:</label> <select id="make"> <option value="">Select make</option> <option value="Ford">Ford</option> <option value="Honda">Honda</option> <option value="Mazda">Mazda</option> <option value="dummy">Dummy cars</option> </select>
<label for="model">Model:</label> <select id="model" disabled="disabled"> <option value="">Select model</option> </select> </fieldset> </form> <script type="text/javascript"> function showMakerEmblem() { var index = document.forms["carForm"].make.selectedIndex; var automaker = document.forms["carForm"].make.options[index].text; var imgTag = document.getElementById("makerEmblem"); if (index > 0) { imgTag.src = "<%=request.getContextPath()%>/img/" + automaker.toLowerCase() + "_logo.gif"; } } function handleEmpty() { document.getElementById("model").options.length = 0; document.getElementById("model").options[0] = new Option("None", ""); document.getElementById("model").disabled = true; } </script> <ajax:select baseUrl="${contextPath}/dropdown.view" source="make" target="model" parameters="make={make}" postFunction="showMakerEmblem"
emptyFunction="handleEmpty" /> 注意到form里面其实没什么不一样的,就是最后那段的<ajax:select/>,baseUrl就是服务器端处理的Handler,source是关联的源,model是被关联的;parameter是传个servlet的参数,可以有多个用逗号隔开;postFunction,emptyFunctuion就是上面的两个JavaScript.详细的可以看 http://ajaxtags.sourceforge.net/usage.html。
经过简单的修改,我们也可以把这个应用到Struts中。那就后续吧^_^...
|