春天里,百花香...
2008年6月17日
#
我习惯连代码和文章一起贴,现在空间用完了,重新注册了一个( http://www.blogjava.net/heyang ),欢迎大家访问。
摘要: 我们可以使用DOM来解析来自服务器端的XML反馈,但返回结果比较复杂时我们必须在XML文档中一个节点一个节点的向下钻探,而使用XPath(专门用于定位匹配模式的一个或多个节点的小语言)只要一行代码就能遍历多个节点。与使用DOM相比,使用XSLT和XPath编码所投入的精力要小得多,随着应用规模的增长,后者的优势会越来越显著。
阅读全文
摘要: 侧边栏静态树状菜单在WebApp中很常见,本文涉及了静态树状菜单的制作和显示控制。比较简单。
阅读全文
摘要: 如果WebApp的侧面菜单项较多时我们可以采用SlideBar的方式将部分菜单显示,大部隐藏,类似Visio中做得那样。本文讨论了Slidebar的做法和显示控制,比较简单。
阅读全文
摘要: 工字型布局中都有一个侧边菜单栏目用以导航,它们存在的一个普遍问题是:用户无法迅速的找到自己所处页面在整个网站中的位置。
当菜单项较多时这会演变成一个大问题,当用户需要刻意寻找网页标志来确定自己所处位置时,这已经说明网站给了客户一种迷宫的感觉,有流失客户的潜在可能性。很多网站采用了球拍式菜单来凸显当前所在页面,本文探讨了这种球拍式菜单的实现方式。
阅读全文
摘要: SQL注入攻击的基本原理,是从客户端合法接口提交特殊的非法代码,让其注入到服务器端执行业务的SQL中去,进而改变SQL语句的原有逻辑和影响服务器端正常业务的处理。SQL注入攻击是Web应用中一个重要的安全问题,虽然Java具备较高的安全性,但如果开发人员不注意,也有可能留下安全隐患,本文将对此展开一些粗浅的探讨,欢迎批评指正。
阅读全文
摘要: 将网页链接做成按钮形状是侧边菜单栏和顶端菜单栏常采用的形态,本文总结了四种常用链接按钮形态的CSS制法。
阅读全文
//*****************************************************
// 《飞翔》 话剧《切格瓦拉》插曲 词 黄继苏 曲 张广天
//*****************************************************
陆地淹没了,
你就在海上飞翔。
海洋干涸了,
你就在天上飞翔。
天雷滚动了,
你就在火里飞翔。
火焰熄灭了,
你就在苦难中飞翔。
过去倒下了,
你就在未来飞翔。
未来退却了,
你就在现在飞翔。
现在迟疑了,
你就在心中飞翔。
心灵败坏了,
你就在创造中飞翔。
飞翔,飞翔,
永远的飞翔。
飞翔,飞翔,
不朽的飞翔!
//*****************************************************
// 随遇而安方为福 曾国藩
//*****************************************************
人生世间,自有知识以来,即有忧患如意事。小儿叫号,皆其意有不平。自幼至少至壮至老,如意之事常少,不如意之事常多。虽大富贵之人,天下所仰慕以为神仙,而其不如意处各自有之,与贫贱人无异,特所忧虑之事异尔。故谓之缺陷世界,以人生世间无足心满意者。能达此理而顺受之,则可稍安。
//*****************************************************
// 治学之道 《中华处世绝学》 一三九页
//*****************************************************
治学之道,最紧要的是立下坚卓不俗的大志,立志是事业的大门,一个人追求的目标越高,他的学问长进就越快。
当然,仅有高大的志向是远远不够的。治学,还要有“只问耕耘,不问收获”的务实精神,避免奢谈,踏实认真。要明白学问的取得,不是一朝一夕的事情,必须勤学好问,持之以很。学问好比金字塔,基础越深越搏越好,这样才能在广播的基础上求得高精尖。做学问,必须重视读书的方法,不要贪多,而要专注于一书,力求吃透。同时,治学须避免门户之见,博采众长,兼收并蓄,为我所用,才能学贯中西,博古通今。而依赖于不俗的才学,一个人才可能为国立功,为己立德,为人立言,受到后人的敬仰。
//*****************************************************
// 子曰
//*****************************************************
君子博学而日参省乎己 则知明而行无过矣.
子曰:赐也,汝以予为多学而识之者与?对曰:然,非与?曰:解也!予一以贯之。
//*****************************************************
// 尼采:在世纪的转折点上 周国平著
//*****************************************************
大自然的星空,群星灿烂。哪最早闪现的,未必是最亮的星宿。有的星宿孤独的燃烧着,熄灭了,很久很久以后,它的光才到达我们的眼睛。
历史和文化的星空何尝不是如此呢?
谁终将声震人间,必长久深自缄默;谁终将点燃闪电,必长久如云漂泊。
一个精神贫乏,缺乏独特个性的人,当然不会遭受精神上危机的折磨。
许多人的所谓成熟,不过是被习俗磨去了棱角,变得世故而实际了。那不是成熟,而是精神的早衰和个性的夭亡。真正的成熟,应当是独特个性的形成,真实自我的发现,精神上的结果和丰收。
当一个人要靠作品来批准自己的一生,他在根基上就变得极为苛求了。
书籍,知识,他人的思想都只能拿来为我所用,而不应当成为目的本身。
伟大的思想,与美丽的女子有相同的趣味,绝不肯让萎靡的弱者来占有自己。
人只以勇敢和毅力所许可的限度接近真理。强者必须认识并肯定现实,正如弱者必须害怕和逃避现实一样。
一个人倘若有健全旺盛的内在生命力,他是不会屈服于悲观主义的,悲观主义是生命力衰退的表现,屈服于悲观主义有如屈服于霍乱,表明肌体已经患病。
一个人健康,他就向往人生的快乐;一个人羸弱,他就念念不忘死亡,就悲观厌世。一个要在世间有所建树的人最忌悲观主义“看破红尘--这是巨大的疲劳和一切创造者的末日。”
没有痛苦,人只能有卑微的幸福。伟大的幸福正是战胜巨大痛苦所产生的生命的崇高感。痛苦磨练了意志,激发了生机,解放了心灵。
热爱人生的人纵然比别人感受到更多更多强烈的痛苦,同时也感受到更多更强烈的生命之欢乐。与痛苦相对抗,是人生最有趣味的事。
假如你在伟大的事业中失败了,你自己因此便是失败了么?假如你们自己是失败了,人类因此便是失败了么?假如人类也是失败了,好吧,别在意!
坚强而沉重,或者坚强而阴郁,仍然不符合酒神精神。人生的伟大肯定者应该兼有坚硬的骨头和轻捷的足,和歌者,武士与自由精神为一体。他应当学会“神圣的舞蹈”,学会欢笑。
//*****************************************************
// 自悟
//*****************************************************
唯有才华具有穿透心灵和穿越时空的力量。
//*****************************************************
// 其它摘录
//*****************************************************
可以缺钱、可以缺吃、缺化,却不可以缺德、确信用、缺操守!公司可以缺资金、缺设备,确不可以确伦理、缺道德、缺人才!
世事复杂,干什么事都不是那么简单。要想在一生中有所作为,干一番事业,思想上必须有这样的准备:别怕麻烦,肯于吃苦,受些窝囊气也能挺得住。否则,遇到麻烦事,意外事,不顺心事就急躁,想逃避,不肯吃苦,不能耐心处理麻烦事;或者遇见不公就会生怒气,发牢骚而不再努力,这样的人事业难成。
平时外表懒散而身怀绝技的高手只存在于古龙的武侠小说里,平时不认真,关键时刻肯定掉链子。
你可以靠谎言暂时领先,可不能靠它领先一辈子。
生活是一面镜子,他照出了你的现实,别人对你不好,一定是自己的原因,决不是别人。
只有挣到钱、这才是男人成功的绝佳体现。有时候想,钱的确比文凭、文章等一切虚无的东西更实在,更能证明一个人。
问题是带人走出困境的最好的向导 危机是教人进行创造的最好的老师 看到问题就是看到出路 碰到危机就是碰到机会。对于勇视现实 不满现状 只求进取 冷静观察 深入分析 甚至敢于自绘败状 自觉接受挑战的人来说 问题就是希望 危机就是专辑。 或者简单地说,只有在危机中不能惊觉新转机的人,有真正的危机.
摘要: 模拟Blogjava制作的一个用CSS控制样式的表格,比较简单。
阅读全文
摘要: 工字型布局是Web中应用比较广泛的布局,它将整个页面分成页头,侧面导航栏,内容栏和页脚栏四部分,页头一般包括logo,网站标题等;侧面导航栏是导航菜单,根据客户的喜好可以放在左边也可以放在右边;内容是正文部分,左右也可以根据用户的喜好放置;页脚包括版权信息,联系我们等。根据content栏的宽度是否会随着浏览器的宽度改变可以将工字型布局分为固定两栏方式和可变两栏方式,本文讨论了这两种方式的制法。
阅读全文
摘要: 除了Div,Table,Form外,我们最常用的Html元素之一就是无序列表ul,使用它通常可以实现以下形式:
1.实现文本数据列表,这是无序列表的原始意图。
2.嵌套使用无序列表,以实现树状结构。
3.修改无序列表的样式,将它作为菜单使用。相对于用表格制作的菜单项,它修改起来比较方便,样式也很容易设置。
第三点就是本文的主要议题。
阅读全文
摘要: 一次将数据库设计三范式应用于表设计的实践过程,比较浅显。
阅读全文
摘要: 表单是Web应用中一个重要的组成部分,用户向服务器端提交数据主要依靠表单进行. 好的表单能帮助用户顺利的完成数据的填写, 不好的表单会让用户对填写过程充满困惑和挫折感.这些都会影响客户的心理,进而会影响客户对整个网站的感觉.
我觉得,前台的表单设计和后台的业务组件都很重要,和程序设计一样,表单的设计也要遵照一定的原则和规范.
设计一个良好的表单,程序员需要综合运用HTML,CSS,JavaScript等方面的知识,下面就是本人的一些关于表单设计的粗浅想法,斗胆拿出来和大家一起探讨探讨.
阅读全文
摘要: 本文就分页的理由,分页的方式和MySql,Oracle中两种不同的分页技术进行了一些阐述,比较浅显。
阅读全文
摘要: 此文是“Web页面表单域验证方式的改进”的续篇。
示例页面:登录页面
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ taglib uri="/WEB-INF/tld/struts-html.tld" prefix="html"...
阅读全文
摘要: 在基于Model2的应用中,控制层的类总会包含对业务层诸类的调用,业务层诸类不可避免的要产生各种异常,如果统一到控制层进行处理的话会导致代码变得庞大臃肿还有不少重复,这种的例子在Web应用中的Servlet和Action诸类中并不少见。
如果我们使用模板方法模式(Template Method Pattern)将业务处理和异常处理分开,能有效简化控制层诸类的代码,借用这种模式,我们可以把固定的异常处理代码放在基类中,而让子类来实现具体的业务,如果执行业务过程中出现异常如数据库无法连接,用户找不到等异常后,直接将异常抛出让基类来处理,这样做成功的把业务处理和异常处理分开到了子类和基类两种类中,涉及具体业务处理的子类代码得到了很大的简化,更方便阅读,修改和管理。
有点疑惑的是,现在还不确定这样做会有什么消极印象,如安全性或结构方面的,大家要是觉得有问题请不吝赐教。
阅读全文
摘要: 一般来说涉及数据库的应用中,表的主键有两种生成方案,一种是专门定义一个主键表,在其中放置一个自增长的字段为其它表提供主键;另一种是使用Oracle的sequence。这两种方案都有一定麻烦,Spring为此专门提供了一个ID增长器以简化具体步骤,下文就是它的相关使用方法的,使用的数据库是MySql5.
归纳
使用Spring的自增长ID生成器完成以下三步即可:
1)配置自增长id生成器,它需要一个数据源的支持。
2)根据配置将自增长id生成器注入DAO各类中。
3)使用nextStringValue,nextIntValue,nextLongValue方法得到ID。
阅读全文
注意:本文说到的log4j版本为1.2.15,使用的配置文件是属性文件(properties),如果这些与您的环境不符则请快速离开,以免耽误你的宝贵时间。
一.log4j在桌面程序中的配置
这个相对简单了,它的步骤就这样两步:
1)将log4j-1.2.15.jar引入到工程的lib目录中.
2)确保配置文件log4j.properties在程序的代码目录(如src目录,cfg目录)中,它编译后应该位于类路径classes中.
log4j.properties示例(可以拷贝使用):
- log4j.rootLogger=debug, stdout, R
-
- log4j.appender.stdout=org.apache.log4j.ConsoleAppender
- log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-
- # Pattern to output the caller's file name and line number.
- log4j.appender.stdout.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%n
-
- log4j.appender.R=org.apache.log4j.RollingFileAppender
- log4j.appender.R.File=输出log文件.log
-
- log4j.appender.R.MaxFileSize=1000KB
- # Keep one backup file
- log4j.appender.R.MaxBackupIndex=1
-
- log4j.appender.R.layout=org.apache.log4j.PatternLayout
- log4j.appender.R.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%n
要对这个文件进行修改的话,基本上改两个地方就行了。
一个是输出文件名称,一个是输出等级设置。
1) 输出文件名称:
log4j.appender.R.File=输出log文件.log
2) 输出等级:
log4j.rootLogger=debug, stdout, R
Debug说明只要是logger.debug以上的都记录
配置到这里,就结束了。下面请看如何在程序中使用log4j。
二.log4j的使用
1) 首先,那个类要用到log4j记录日志,就应该为类添加一个静态的成员变量loogger,示例如下:
- public class Main{
- private static Logger logger = Logger.getLogger(Main.class);
-
- public static void main(String[] args){
- logger.info("成员管理程序启动");
- new MemberMngCtrl();
- }
- }
2) 其次,你就可以使用logger.debug ,logger.info, logger.warn, logger.error, logger.fatal等函数(记录等级依次提高)来记录日志内容了,确实是很简单方便的。
三.log4j在Web工程中的配置
与桌面程序一样的是,properties文件也需要能被编译到classes(WEB-INF/classes/)中,建议将属性文件放在特定的目录下并设置为源码目录,另外放在WEB-INF\src下也不错。
这一步比前面稍多的是需要配置一个初始化log4j的initServlet,就是在一开始就启动的Servlet,代码如下:
- public class Log4jInit extends HttpServlet {
- private static final long serialVersionUID = -4499302208753939187L;
- static Logger logger = Logger.getLogger(Log4jInit.class);
-
- public void init(ServletConfig config) throws ServletException {
- String prefix = config.getServletContext().getRealPath("/");
- String file = config.getInitParameter("log4j");
- String filePath = prefix + file;
- Properties props = new Properties();
-
- try {
- FileInputStream istream = new FileInputStream(filePath);
- props.load(istream);
- istream.close();
-
- String logFile = prefix + props.getProperty("log4j.appender.R.File");
- props.setProperty("log4j.appender.R.File",logFile);
-
-
- PropertyConfigurator.configure(props);
- } catch (IOException e) {
- System.out.println("Could not read configuration file [" + filePath + "].");
- System.out.println("Ignoring configuration file [" + filePath + "].");
- return;
- }
- }
- }
然后,在Web.xml中配置一下,让它在一开始启动就可以了。
-
- <servlet>
- <servlet-name>log4j-init</servlet-name>
- <servlet-class>
- com.sitinspring.action.Log4jInit
- </servlet-class>
- <init-param>
- <param-name>log4j</param-name>
- <param-value>WEB-INF/classes/log4j.properties</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
全文完。
一般来说, 在创建一个应用程序之前,首先要决定这个应用程序的体系结构。应用程序体系结构(Application Architecture)由应用程序开发者设计,它指定了在各种各样的终端系统上,应用程序是如何组织在一起的。为了降低设计难度,大部分程序都以层(称为layer或level)的方式组织在一起,每一层都建立在它的下层基础上,使用下层提供的服务,下层对上层隐藏了许多服务实现的细节。这种方法几乎应用于整个计算机科学领域,也可以称为信息隐藏,数据类型抽象,数据封装,面向对象编程等。
分层即是对类进行一些规划,以流程中的类的用途和所处环节划分,把程序中将要用到的各个类分别归纳到各个包(目录)中。分层是对系统进行细分的第一步,它旨在将系统按具体功能和用途分解为相对独立的各个部分.如果说细分是将把难以解决的大问题分解成了各个容易解决的小问题的话,分层则是把解决同类小问题的类归纳到一起,这样程序的结构更加清晰,程序的可读性和可维护性越好,也更容易得到重用。
从大的尺度来讲,一个程序可粗略的分成三个层次:
界面层(UI layer),这是用户能直接感受到的,包含显示和控制两部分;
业务层(Business layer),其中包含了业务逻辑和业务处理;
持久层(Persistence layer),它用来将数据存储和将数据从持久层提取出来。
界面层(UI layer)中,包含两个层次:视图层View和控制层Controller.
视图层View是用户查看数据,输入和向用户输出结果的一层,这一层是用户唯一能够感受软件功能的窗口,它或者由Swing组件搭建(桌面系统或C/S系统中),或者由JSP搭建(B/S系统),它负责让用户输入数据和将控制层返回的数据显示给客户。其中返回的数据一般是领域对象的变体或者直接就是领域对象或其集合。在Web程序中jsp基本就属于这一层的。
控制层Controller是用来将界面和业务层联系在一起的,在系统的各层次中,应该和View层打交道一般只有Controller层, Controller层是View层和系统其它层次进行交互的中介者, View层越过中介者直接调用其它层次的行为应该尽量避免。
一般来说,为了减少耦合,提高程序的可维护性,我们一般采用MVC架构模式将业务层,视图层和控制层分开。
业务层(Business layer)中包含领域层 Domain,服务层 Service和实用工具层Util。
业务层是整个系统的关键部分,它主要由领域模型和业务逻辑组成,领域模型定义系统内相互作用的各个实体,业务逻辑则定义了领域模型所能执行的不同操作, 领域层的各个类代表了领域模型,而服务层的各个类代表了业务逻辑. 领域层和服务层是起点,其它各层都从这里起步.
领域层 Domain:领域对象是对现实世界业务处理对象的抽象和归纳,领域层中的类基本上都是实体(Entity)类,如员工管理系统中的Employee,学籍管理系统中的Student,借贷管理系统中的Contract等,系统的业务处理中用到那些实体对象,领域层中一般就应该有和这个实体对象相对应的实体类。这些类在刚开始设计时可能只有一些属性和对应的getter/setter方法,以后会不断的加入新的内容(主要是方法),如果有必要的话,可以为这些领域对象设计一些上层的抽象类或者接口,借助于泛型,反射,控制反转等高级技能能在一定程度上简化程序的编写过程。此外,领域层是程序的核心内容,因为其他层次都在很大程度上依赖Domain层的设计,如果这一层设计不够完善会使以后的工作步履蹒跚.
服务层Service:这一层就是为领域对象提供服务用的,领域对象一般不直接和表现层,持久层直接打交道而是通过服务层进行代理.服务层是UI层到持久层的中间通道,它处于上通界面下通持久层的中间环节,这个特性是使的这一层决定了软件功能的多少。
一般来说,UI层向服务层传入的是用户输入的一些参数,服务层进行验证,重组后向下层DAO传输;而服务层从Dao层收到的是领域对象或其集合,而它向UI层返回的是领域对象或者其集合的变体或者直接是领域对象或者其集合本身。Service诸类的实例在桌面程序和CS程序中一般作为Model的一个私有成员,而在Web程序中常常要用到时再创建出来。除领域层外,其余各层是在围绕它而设计.
实用工具层Util:这一层相对简单,它包含了各种工具类,类中包含的主要是静态函数和静态成员变量,这些类对共通的函数,变量进行了归纳,它旨在消除重复代码,降低主体代码的复杂程度.一般此层中类的复用程度很高.值得通过项目积累.
持久层(Persistence layer)是直接与持久介质打交道的层次,持久介质可以是常见的关系型数据库,文件甚至Web Service,它一般包含两个部分。
数据存储对象层(DAO层),sql语句一般写在这层中, 然后由它调用;DAO层是最低的一层,与持久介质直接打交道,它包含具体文件的位置,数据库连接等;
另一个部分就是持久介质,通常是关系型数据库。
Dao层中各个类一般作为Service的私有成员,供Service调用。
下图是各层间的位置关系图:
如何从需求中分析出诸个层次中的类呢,我们在大尺度上可以按照下面的步骤进行:
Domain the first:首先从业务流和业务规则中归纳总结出领域对象.
Service the second:为领域对象设计服务类。
Persistence the third:持久层的负责领域对象持久化到持久介质以及逆过程,它的设计在领域层和服务层之后,比较典型的持久层设计有数据库表的设计和ER图(实体关系图)的绘制.
View the last:最后设计表现层,表现层受领域层和服务层制约, 容易变化且易于修改,通常放在最后实现.
具体步骤如下
1.理解,分析,钻研需求,彻底了解你的客户想要什么,需要你做些什么.
2.将大系统分解成一个个子系统,细分出各个层次,搞清楚各层的任务。
3.分析业务逻辑,归纳出业务流.
4.从业务流和业务规则中总结出领域对象.
5.为领域层实现服务层.
6.以Domain层和Service层为核心设计表现层和持久层,直到形成完整的程序.
7.加入实用层消除重复代码,梳理结构和简化流程,.
8.限制跨层的调用.
软件开发过程中,唯一不变的就是变化。这是一句老生常谈,也就是说软件开发中永恒的主题就是变化。当你把代码都写好了,测试也完成了,准备交付的时候客户忽然要求你在指定时间做出变化,这种情况在外包行业中很常见;而对一些银行金融项目,边调研边开发边测试屡见不鲜;对于一些Web项目,从来就只有永远的Beta版,改来改去的事更是家常便饭。对此,程序员一定要求清晰的认识,抱怨只能是嘴上痛快,不解决实际问题。真要解决实际问题,非要动一番脑筋不可,如果合理使用了设计模式,反射或是Spring的IoC,便能变修改噩梦为一次有趣的智慧之旅。
首先我们看原始要求:客户要求将一批雇员名单存入到CSV和XML两种文件中去,以后还有可能增加别的文件格式,比如PDF,XLS等,虽然这是下一期的内容,但这一期应该考虑到变化,客户要求扩展性一定要好。
没问题,有了设计模式响应变化不难。这时我们可以用到模板方法模式:
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
先请看骨架抽象类:
- public abstract class FileMaker {
-
-
-
- private List<Employee> employees;
-
-
-
-
-
-
-
- public final void makeFile(List<Employee> employees,String fileName){
- setEmployees(employees);
- makeFile(fileName);
- }
-
-
-
-
-
- protected abstract void makeFile(String fileName);
-
- public final void setEmployees(List<Employee> employees) {
- this.employees = employees;
- }
-
- public List<Employee> getEmployees() {
- return employees;
- }
- }
很好,固定的函数和步骤都在抽象基类中写定了,再看两个具体实现类,它们要实现的就是makeFile函数而已。
- public class CSVFileMaker extends FileMaker{
- protected void makeFile(String fileName){
- try {
- BufferedWriter out = new BufferedWriter(new FileWriter(fileName));
-
- for(Employee emp:getEmployees()){
- String line="";
- line+=emp.getName()+",";
- line+=(emp.isMale()?"男":"女")+",";
- line+=emp.getAge()+",";
-
- out.write(line+"\r\n");
- }
-
- out.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- public class XMLFileMaker extends FileMaker{
- protected void makeFile(String fileName){
- try {
- Document document = DocumentHelper.createDocument();
- Element root = document.addElement("employees");
-
- for(Employee emp:getEmployees()){
- Element empElm=root.addElement("employee");
-
- Element nameElm=empElm.addElement("name");
- nameElm.setText(emp.getName());
-
- Element sexElm=empElm.addElement("sex");
- sexElm.setText(emp.isMale()?"男":"女");
-
- Element ageElm=empElm.addElement("age");
- ageElm.setText(String.valueOf(emp.getAge()));
- }
-
- OutputFormat format = OutputFormat.createPrettyPrint();
- format.setEncoding("GBK");
- XMLWriter writer = new XMLWriter(new FileWriter(fileName),format);
-
- writer.write(document);
- writer.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
这样昨完以后感觉很好,因为我们成功的把变化和不变分离开来,不变的部分放在了抽象基类中,而容易变化的部分放在了两个具体的子类中,这样如果再增加一种新文件格式,从抽象基类再扩展出一个子类即可。很好,这样就不怕变化了。客户对此也没有异议。
调用示例如下:
- List<Employee> emps=new ArrayList<Employee>();
- emps.add(new Employee("Andy",true,21));
- emps.add(new Employee("Bill",false,23));
- emps.add(new Employee("Cindy",true,25));
- emps.add(new Employee("Douglas",false,28));
-
- FileMaker fileMaker=new CSVFileMaker();
- fileMaker.makeFile(emps, "1.csv");
-
- fileMaker=new XMLFileMaker();
- fileMaker.makeFile(emps, "2.xml");
客户看到了我们的调用的例子,觉得应该更灵活一些,他说存成各种不同的文件是通过点击按钮来实现的,如果每个按钮的事件处理函数都要生成具体子类岂不是太死板了吗?这样做每个文件下载按钮的事件处理代码不是都不一样?
有点道理,如今理解到这一层的客户实在是不多见了。不过很容易满足他的需求,我们可以引入反射的方法:
- public static void main(String[] args) {
- List<Employee> emps=new ArrayList<Employee>();
- emps.add(new Employee("Andy",true,21));
- emps.add(new Employee("Bill",false,23));
- emps.add(new Employee("Cindy",true,25));
- emps.add(new Employee("Douglas",false,28));
-
- callByReflect("csv",emps,"1.csv");
- callByReflect("xml",emps,"2.xml");
- }
-
- public static void callByReflect(String type,List<Employee> emps,String fileName){
- try{
- Class cls=Class.forName("com.heyang."+type.toUpperCase()+"FileMaker");
- FileMaker fileMaker=(FileMaker)cls.newInstance();
- fileMaker.makeFile(emps, fileName);
- }
- catch(Exception ex){
- ex.printStackTrace();
- }
- }
因为按钮上的文字和类名是有关的,如下载CSV的按钮上就有CSV的文字,这可以通过正则表达式取道,再组合一下不就是类名了吗?csv到com.heyang.CSVFileMaker,xml到com.heyang.XMLFileMaker,其实变化就是三个字母而已。如果增加按钮,取出按钮中的三个字母再调用callByReflect函数即可,这个过程简直可以固化。
客户看到反射方法以后很是满意,没有意见了。待客户走后,项目经理把你拉到一边,说:
“你刚才的方法不错,确实很强,但看得懂反射并能灵活掌握的人水平要够一年经验才行,维护的活让一年经验的人去干太可惜了,最好改改,最好达到让新手也能掌握并修改的程度。”。
没办法,领导总有领导的考虑,他这么说也很合理,成本问题我可以不考虑,但如果把程序搞得复杂貌似NB,能让一些学艺不精的人产生云山雾罩的感觉,有时还能被人尊称一声“大侠”,但谁也不比谁傻多少,这声大侠不是白叫的,但是出了问题或是有了变化别人还是要找你,到头来还是给自己添乱,这些都是义务劳动,何苦来呢?还是应该改得容易些,让大家都能修改,我可不愿意半夜三更被人叫起来问问题。
用Spring的IoC就可以解决问题,写一个新类并配置到XML文件中对新手来说问题不大,这下可以让领导放心了,自己就更放心了。
IoC方案代码如下:
- public class Main {
- public static void main(String[] args) {
- List<Employee> emps=new ArrayList<Employee>();
- emps.add(new Employee("Andy",true,21));
- emps.add(new Employee("Bill",false,23));
- emps.add(new Employee("Cindy",true,25));
- emps.add(new Employee("Douglas",false,28));
-
- callByIoc("csv",emps,"1.csv");
- callByIoc("xml",emps,"2.xml");
- }
-
- public static void callByIoc(String type,List<Employee> emps,String fileName){
- try{
- ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
- FileMaker fileMaker=(FileMaker)ctx.getBean(type);
- fileMaker.makeFile(emps, fileName);
- }
- catch(Exception ex){
- ex.printStackTrace();
- }
- }
- }
Bean。xml文件内容很简单吧:
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
- <beans>
- <bean id="csv" class="com.heyang.CSVFileMaker"/>
- <bean id="xml" class="com.heyang.XMLFileMaker"/>
- </beans>
好了。到这里问题就彻底结束了,终于满足了客户和上级的要求,可以回家睡个好觉了,不用担心别人打搅了。
态度改变一切,变化来了人总是要多做一些,心理当然是不愿意的,但抱怨或是消极抵制都不是解决问题之道;如果把它看做一个挑战的契机,凡事多思考一些,不但能解决问题,自己也会有所提高,这就是积极的态度带来的好处。
摘要: 在工程中经常有发送邮件的任务,如果使用JavaMail来发送邮件,用到的代码较多,过程和细节也相对复杂,而使用Spring的MailSender能相对简单方便些,这样使程序员能更快捷的完成邮件发送任务。下面请看示例代码:
注意在执行代码前,请确认已经将activation.jar,commons-logging-1.0.4.jar,mail.jar和spring.jar载入工程。...
阅读全文
在Web开发中,文本邮件发送的任务比较常见,我们可以利用它进行一些客户通知和异常通知,文本邮件发送一般用到JavaMail API,下面是一个我有时用到的邮件发送实用工具类,把其中一些参数修改一下就能为你所用。
注意:在执行代码前,请把mail.jar和activation.jar载入工程。
代码如下:
- package com.heyang;
-
- import java.util.Date;
- import java.util.Properties;
-
- import javax.mail.Address;
- import javax.mail.Message;
- import javax.mail.Session;
- import javax.mail.Transport;
- import javax.mail.internet.InternetAddress;
- import javax.mail.internet.MimeMessage;
-
-
-
-
-
-
- public final class MailUtil {
-
- private static final String SenderEmailAddr = "XXXXXXX@163.com";
-
-
- private static final String SMTPUserName = "XXXX";
-
-
- private static final String SMTPPassword = "XXXXXXX";
-
-
- private static final String SMTPServerName = "smtp.163.com";
-
-
- private static final String TransportType = "smtp";
-
-
- private static Properties props;
-
-
-
-
-
- private MailUtil() {
-
- }
-
-
-
-
- static {
- MailUtil.props = new Properties();
-
-
- MailUtil.props.put("mail.smtp.host", MailUtil.SMTPServerName);
-
- MailUtil.props.put("mail.smtp.auth", "true");
- }
-
-
-
-
-
-
-
- public static void sendMail(String emailAddr, String mailTitle,
- String mailConcept) {
-
- Session s = Session.getInstance(MailUtil.props, null);
-
-
- s.setDebug(false);
-
-
- Message message = new MimeMessage(s);
- try {
-
- Address from = new InternetAddress(MailUtil.SenderEmailAddr);
- message.setFrom(from);
-
-
- Address to = new InternetAddress(emailAddr);
- message.setRecipient(Message.RecipientType.TO, to);
-
-
- message.setSubject(mailTitle);
-
- message.setText(mailConcept);
-
- message.setSentDate(new Date());
-
- message.saveChanges();
-
- Transport transport = s.getTransport(MailUtil.TransportType);
-
- transport.connect(MailUtil.SMTPServerName, MailUtil.SMTPUserName,
- MailUtil.SMTPPassword);
-
-
- transport.sendMessage(message, message.getAllRecipients());
- transport.close();
-
- System.out.println("发送邮件,邮件地址:" + emailAddr + " 标题:" + mailTitle
- + " 内容:" + mailConcept + "成功!");
- } catch (Exception e) {
- System.out.println(e.getMessage());
- System.out.println("发送邮件,邮件地址:" + emailAddr + " 标题:" + mailTitle
- + " 内容:" + mailConcept + "失败! 原因是" + e.getMessage());
- }
- }
-
-
-
-
-
- public static void main(String[] args){
- MailUtil.sendMail("XXXXXX@gmail.com", "title", "concept");
- }
- }
在面向对象编程中,我们一般采用从顶向下的编程方式,即先设计类的层次,如View,Controller,Service,Dao,Domain,Util等,再完善各层中的类。在这个过程中,我发现按功能和形态来分,系统中类可分为以下几个类别:
1.实体类(Entity Classes):这种类一般是现实世界事物在代码世界中的抽象表示,和现实事物有着一一对应关系.存储到持久介质中时一般对应着一条记录.如MIS系统中常见的雇员类Employee,论坛系统中常见的Topic等.由于这些类可以直接从现实事物中归纳抽象得来,写出它们的框架代码一般相对方便容易,但要真正理顺实体类之间的关系需要投入不少精力,这些类一般处于Domain层中.
2.通道类(Plumbing Classes):这种类一般用于充当传输实体类的通道,在编程中,经常需要从持久层取出一个或多个实体类的对象或是将实体类的对象存储到持久层中,这种任务一般由通道类来完成.它们一般由Service或是Dao层中的类来承担.这些类一般不保存状态,对外界来说,它们的对外接口(Public Interface)一般比具体的实现重要,在数量较多时,也经常抽象出一些上层的抽象类或是接口出来以方便调用.
3.辅助类(Assistant Classes):这些类一般起辅助任务,一般可以把共通的处理和变量放在其中供其他层次类调用,这样做一能避免散弹式修改,二能减少重复代码,三能提高代码复用度.辅助类一般放在Util包中.
4.框架类(Framework Classes):这些类一般由固定的框架提供,程序员不能改变.在类的层次上它一般处于界面和业务层之间,即控制层的位置,jsp/servlet中的Servlet,Struts1,2中的Action都是这样的类,它承担了接受用户输入,并展示业务处理的结果的任务.
一个技术人员要生存,要发展,要成一番事业,必须遵循一定固定的法则,若逆天而行而不自觉,轻则徒劳无功,白费精力;重则无法立足,庸碌一生。因此把握住自己的发展之道是技术人首要的大事,只有走在正确的道路上,前进才有意义。
一个技术人员,首先要固本培元,什么是技术人的根本呢?无论语言,框架和技术如何发展,数据结构和算法都是其核心内容,所谓万变不离其宗,有了良好的数据结构和算法的根基,接受并掌握一个新兴事物不过旬月时间,若没有而盲目跟随,事倍而功半矣。另外面向对象的精髓也要把握,从根本上来讲,任何现代框架其核心思想还是没有超越面向对象的范畴,都是面向对象的继承和发展,理解掌握了面向对象的思想,就把握住了框架根本性的东西,学习掌握起来就更容易把握其本质.
其次,技术人员必须把握主流技术方向才不至于迷失自己。若在支流中迷失自己恐有空执屠龙之技无用武之地之忧,古代也许还能自娱自乐,现代社会这样做温饱都无法解决,房子,车子,孩子更是白扯;只有置身主流,才能继续奋斗下去。当前的主流技术方向,无非SSH(Struts1/2,Spring,Hibernate)而已,彻底弄清楚它们,才有安身立命之本.君不见诸多招聘广告,均写SSH乎.这三项其实也不好掌握,尤其Hibernate,掌握不精深也不行,有些大侠也曾阴沟里翻过船。
其三,技术人员要乐于善于总结提高,对于已经掌握的内容,要及时归纳总结到纸面上,这样做一能梳理脉络,让自己掌握得更全面细致;二能查漏补缺,发现以前忽视或是未接触过的领域;三能求其友声,放在博客上供大家分析阅读讨论,弥补自己的不足.有此三益,于己于人都是一件大好事,何乐而不为呢?
其四,技术人员要展示自己的能力和价值,应该具备自己的产品,它可以用来巩固和展现自己的实力,在产品的研发过程中,技术人员能把自己的知识智慧实用化,可避免走入象牙塔之患;外界也能通过产品来了解发掘自己.这也是一件于己于人都有利的事情.
其五,技术人员应该具备完整的思想体系,有自己独到的见解并能有所突破创新. 人云亦云无异于鹦鹉学舌,有何能哉? 要想上一个层次,必须鲤鱼跃龙门. Gosing和Kaven两人可作为最好的榜样。
最后,广博的知识不可少.拘泥于一处难免死钻牛角尖,很多情况下换一种思维顿时有拨云见日之感,如有闲暇,技术人员应该跳出圈子,广采能用之材为我所用.
摘要: 称球问题经常是面试中的常客,这里我用做了一个称球的程序,主要的方法就是递归和扫描,贴出来请大家指正。
阅读全文
摘要: /** *//**
* 二叉树节点类
* @author HEYANG
* @since 2008-7-26 下午02:59:06
*/
class Node<T extends Comparable> {
 ...
阅读全文
摘要: 这是一个美国IT企业的面试题,原题大意是从一个文件中读取出可连通的城市对,给出两个城市,判断是否可连通,如果可连通就输出yes,不可连通就输出no,否则给出命令行帮助。
其实判断连接状态不用遍历图,用蔓延法即可,具体做法就是从起始城市开始,依次改变其周边连通城市的连通状态,再从周边开始向周边连通城市蔓延,如果能蔓延到结束城市的周边可连通城市,则说明两个城市是完全可连通的。这种做法和多米诺骨牌效应很像。我姑且称之为蔓延法。
阅读全文
找出以下字符串=符号后面对应的属性值
"职务=GM 薪水=50000 , 姓名=职业经理人 ; 性别=男 年龄=45 ";
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** *//**
* 用正则表达式找出每个属性对应的值
* @author HEYANG
* @since 2008-7-23 下午08:12:45
*/
public class RegexFindProperty {
public static void main(String[] args) {
String input = "职务=GM 薪水=50000 , 姓名=职业经理人 ; 性别=男 年龄=45 ";
// =号和空白符之间是非空格字符,这种写法比去分开组合字母,数字和汉字的方式要快捷
Pattern pattern = Pattern.compile("=(\\S+)\\s*");
// 用Pattern类的matcher()方法生成一个Matcher对象
Matcher m = pattern.matcher(input);
// 使用find()方法查找第一个匹配的对象
boolean result = m.find();
// 使用循环找出模式匹配的内容打印
while (result) {
// 取得匹配的结果
String replaceStr = m.group(1);
System.out.println("匹配的属性等于=" + replaceStr);
result = m.find();
}
}
}
摘要: package com.sitinspring.datetime;
import java.util.ArrayList;
import java.util.List;
public class MonthlyCalendar{
private static f...
阅读全文
摘要: 输出示例:
当前日期时间为:2008.07.18 10:48:57
当前日期为:2008.07.18
当前日期为:2008.7.18
当前时间为:10:48:57
2008.07.05与2008.07.18之间相隔:13天
当前年月为:2008.07
本月第一天为周2
本月有31天
阅读全文
Comparator的具体实现类
public class AgeComparator implements Comparator {
public int compare(Object op1, Object op2) {
Employee eOp1 = (Employee) op1;
Employee eOp2 = (Employee) op2;
// 按年龄排序
return eOp1.getAge()-(eOp2.getAge());
}
}
public class NameComparator implements Comparator {
public int compare(Object op1, Object op2) {
Employee eOp1 = (Employee) op1;
Employee eOp2 = (Employee) op2;
// 按姓名排序
return eOp1.getName().compareTo(eOp2.getName());
}
}
public class SalaryComparator implements Comparator {
public int compare(Object op1, Object op2) {
Employee eOp1 = (Employee) op1;
Employee eOp2 = (Employee) op2;
// 按薪水排序
return eOp1.getSalary()-(eOp2.getSalary());
}
}
Employee类:
public class Employee{
protected String name;
protected int age;
protected int salary;
public Employee(String name,int age,int salary){
this.name=name;
this.age=age;
this.salary=salary;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}
测试:
public class Main{
public static void main(String[] args){
List<Employee> employees=new ArrayList<Employee>();
employees.add(new Employee("Andy",21,2000));
employees.add(new Employee("Felix",21,3000));
employees.add(new Employee("Bill",35,20000));
employees.add(new Employee("Helen",21,10000));
employees.add(new Employee("Cindy",28,8000));
employees.add(new Employee("Douglas",25,5000));
// 按名称排序
Collections.sort(employees,new NameComparator());
display(employees);
// 按年龄排序
Collections.sort(employees,new AgeComparator());
display(employees);
// 按薪水排序
Collections.sort(employees,new SalaryComparator());
display(employees);
}
public static void display(List<Employee> employees){
for(Employee e:employees){
System.out.println("雇员名="+e.getName()+" 年龄="+e.age+" 薪水="+e.getSalary());
}
System.out.println();
}
}
输出:
雇员名=Andy 年龄=21 薪水=2000
雇员名=Bill 年龄=35 薪水=20000
雇员名=Cindy 年龄=28 薪水=8000
雇员名=Douglas 年龄=25 薪水=5000
雇员名=Felix 年龄=21 薪水=3000
雇员名=Helen 年龄=21 薪水=10000
雇员名=Andy 年龄=21 薪水=2000
雇员名=Felix 年龄=21 薪水=3000
雇员名=Helen 年龄=21 薪水=10000
雇员名=Douglas 年龄=25 薪水=5000
雇员名=Cindy 年龄=28 薪水=8000
雇员名=Bill 年龄=35 薪水=20000
雇员名=Andy 年龄=21 薪水=2000
雇员名=Felix 年龄=21 薪水=3000
雇员名=Douglas 年龄=25 薪水=5000
雇员名=Cindy 年龄=28 薪水=8000
雇员名=Helen 年龄=21 薪水=10000
雇员名=Bill 年龄=35 薪水=20000
摘要: 回溯法有“通用的解题法“之称。用它可以系统的搜索一个问题的所有解或任一解。会所法是一个既带有系统性又带有跳跃性的搜索算法,他在包含问题的所有解的解空间树中,按照深度有限的策略,从根节点出发搜索解空间树,算法搜索至解空间树的任一节点时,总是先判断该节点是否肯定不包含问题的解。如果肯定不包含,则跳过对该节点为根的子树的系统搜索,逐层向其祖先节点回溯,否则进入该子树,继续按照深度优先的策略进行搜索。回溯法在用来求问题的任一接时,只要搜索到问题的一个解就可以结束。
这种深度优先的解的算法称为回溯法,它适合于解一些组合数较大的问题。
用回溯法解n皇后问题时,可以用一棵完全n叉树来表示其解空间。剪去不满足行列和斜线攻击的子树后,剩下的就是问题的解答。
阅读全文
摘要: package com.sitinspring.roundtable;
/** *//** *//** *//**
* 循环链表节点类
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-7-1-...
阅读全文
摘要: package com.sitinspring;
/** *//**
* 单链表节点类
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-7-1-下午10:42:49
* @param&nb...
阅读全文
摘要: 代码:
package com.sitinspring;
import java.util.Arrays;
/** *//**
* 泛型动态数组类,以数组为数据容器实现动态数组的功能
* @author: sitinspring(junglesong@gmail.com)
*&nbs...
阅读全文
摘要: package com.sitinspring;
/** *//**
* 整形数组实用类,能求两数组的并交差集,不借助于集合类
* @author: sitinspring(junglesong@gmail.com)
* @date: 2008-6-24-下午10:13:33
&...
阅读全文
摘要: 判断集合中存在重复是常见编程任务之一,当集合中数据量比较大时我们通常希望少进行几次扫描,这时双重循环法就不可取了。
位图法比较适合于这种情况,它的做法是按照集合中最大元素max创建一个长度为max+1的新数组,然后再次扫描原数组,遇到几就给新数组的第几位置上1,如遇到5就给新数组的第六个元素置1,这样下次再遇到5想置位时发现新数组的第六个元素已经是1了,这说明这次的数据肯定和以前的数据存在着重复。这种给新数组初始化时置零其后置一的做法类似于位图的处理方法故称位图法。它的运算次数最坏的情况为2N。如果已知数组的最大值即能事先给新数组定长的话效率还能提高一倍。
阅读全文
摘要: package com.sitinspring;
/** *//**
* 从8*8的方阵中找出最大价值轰炸目标,轰炸范围为9格
*
* @author sitinspring(junglesong@gmail.com)
* @since 2008-6-17...
阅读全文
sitinspring(http://www.blogjava.net)原创,转载请注明出处.