|
2007年5月8日
现在做互联网产品的团队都比较小,也可能没有特别多运维人员。因此特别需要用一些系统或是工具来监控服务器或者是服务是否正常。之前比较直接的做法是自己搭建一套开源的监控系统,现在随着云服务器的流行,也有越来越多的人用户会使用云端的监控平台。
从我的经验来看,云服务器监控是有些特别的好处的:
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)
|