J2EE开发规范
一 JAVA编码规范
1 命名规范
1.1 包命名
? 包名称必须全部用小写。
? 命名方式:业务领域名.公司名.项目名.模块名 如com.yr.xxx.dao。
1.2 类命名
类名以英文单词取名,首字母必须大写,多个英文单词以大写字母间隔,避免使用单词的缩写,除非它的缩写已经广为人知,如HTTP。类名中不允许‘_’、 ‘-’等符号。
1.3 属性/local变量命名
? 定义属性的位置,在类定义的开始,按照public,protected,package,private顺序放置。定义local变量尽量在那段代码的开始处,如方法的开始处。如果是if,for,while段,尽量在左大括号“{”的下一行处定义要使用的local变量。
? 尽量用相同含义英文单词表示,不允许‘_’、 ‘-’等符号,如:custName。第一个字母小写,中间单词的第一个字母大写。不要用_或&等符号作为第一个字母。 单字符的变量名一般只用于生命期非常短暂的变量。如:i,j,k,m,n一般用于int。如果变量是集合,则变量名应用复数,即以小写s结尾 。例如:
序号 变量名称 注 释
1 strfileName “文件名”字符串类型
2 intfileCount “文件总数”整型
3 strnames 多个“文件名”的集合
4 gMemory 全局变量
? 基本类型变量命名清单
类型 前缀 示例
int int intPageIndex
long lng lngCount
byte byt bytPixel
boolean bln bln Empty
double dbl dblSalary
float flt fltSalary
String str strUserName
char chr chrDrive
Object obj objAccount
1.4 常量命名
所有常量名均全部大写,单词间以‘_’隔开。例如:
序号 常量名称 注 释
1 MAX_NUM 最大数
2 public static final String FUNCTION_LIST = "function_list"; …
3 … …
1.5 方法命名(非rest风格)
方法命名采用“动作+属性” 的方法。并且,动作以小写字母开始,属性以大写字母开始。常用的动作有:is、get、set、save、add、del等。
例如:getName、setName、isSysManager、saveXXX、mdfXXX、delXXX等。
规则名称 规则 说明
新增数据 addXXX
修改数据 updateXXX
变更数据 alertXXX
删除数据 delXXX
查看详细信息 viewXXX
查询数据 queryXXX
进入某个功能页面 entryXXX
下一步 next、nextToXXX或entryXXX
1.6 其他命名
开发人员如果遇到上述表格中未列举的类型,请书面通知相关管理人员,由管理人员集中更新列表内容,不得擅自启用未经确定的新变量前缀。
2 编码规范
2.1 程序基本构成
a) 公用元素
公用元素指“公用变量”、“公用类”等,由于公用元素对程序中所有类都是可见的,故对它们的修改将会对整个程序造成影响。“公用变量”过多也会破坏数据的隐藏性,与“面向对象编程”的规范不符。所以,公用变量的使用请慎重考虑。
b) 函数返回值
注意函数返回值的初始化。
c) 代码段注释
“//”注释用于对程序的注解等。
“/*....*/”注释用于对程序修改时临时删除部分程序代码。
注:是否作为“临时代码删除和对程序的注释”仅作为参考,根据具体情况而定
d) 设定初值
所有变量均应设定其初值,而且变量的初值不应依靠编译程序自动设定。
e) 错误返回值
调用方法后,应检查errno 或方法返回值,以确定操作是否完成。若返回错误,返回对应的错误编码。
f) 内存释放
当原先分配的内存不在需要时,应将其释放。
g) 类及方法注释
提供elipse配置。
2.2 异常处理
在当前的系统架构中,系统自下而上被分为持久化层(OP)、业务处理层(BP)、请求处理层(RP)、springMVC、界面层(UI),本规范约定了持久层、业务处理层、请求处理层的异常处理机制。
I 异常处理总原则
1. 下层只向上层抛一类异常。如:业务处理层只向请求处理层抛ApplicationException异常。
2. 为了使系统能够更好的跟踪运行情况,必须把底层异常放入新异常中。如:throw new ApplicationException("查询单位时发生系统异常",e)
3. 如果一个层要抛出多个异常,那么所有自定义异常必须统一继承一个父类异常。这样上层可以通过父类异常捕获
4. 异常统一在请求处理层(rp层),即controller里进行处理,请求处理层以下的层次在处理异常时,只需要把底层的异常类放到本层约定的异常类中,并抛出,如有需要可以加适当的异常消息,并不需要记录LOG,LOG在框架里会自动处理。
II 请求处理层的异常处理
所有的controller继承于统一异常父类
并由统一异常处理模块收集处理异常,并分类处理(把对应的异常信息和用户信息处理)和跳转(Spring带有统一异常处理类)
III 页面层异常的处理
引入xxx标签库,然后在页面中加入<xxx:errors />标签,页面就会自动将Action中保存的错误信息取出,显示在弹出窗口。
如:
IV 异常处理原则
Java中的异常大致分成三类:
JVM 异常这种类型的异常由JVM 抛出。OutOfMemoryError 就是JVM 异常的一个常见示例。对JVM 异常我们无能为力。它们表明一种致命的情况。唯一的办法退出办法是停止应用程序服务器,然后重新启动系统。
应用程序异常应用程序异常是一种定制异常,由应用程序或第三方的库抛出。这种异常往往
是不满足某个应用条件,由应用抛出。
系统异常在大多数情况下,系统异常由JVM 作为RuntimeException 的子类抛出。这种异常往往是编码错误,例如,NullPointerException 或ArrayOutOfBoundsException 将因代码中的错误而被抛出。另一种类型的系统异常在系统碰到配置不当的资源(例如,拼写错误的JNDI 查找(JNDI lookup))时发生。在这种情况下,系统就将抛出系统异常。最重要的规则是,如果您对某个异常无能为力,那么它就是一个系统异常并且向上抛出。
以下是一些普遍接受的异常处理原则:
1. 如果无法处理某个异常,那就不要捕获它。
2. 如果捕获了一个异常,请不要胡乱处理它。
3. 尽量在靠近异常被抛出的地方捕获异常。
4. 在捕获异常的地方将它记录到日志中,除非您打算将它重新抛出。而不是把它吞掉。
5. 需要用几种类型的异常就用几种,尤其是对于应用程序异常
6. 如果系统使用了异常,那么就不要再使用错误码
结论:
本系统使用SSM框架,DAO+Service+Controller三层架构,捕获原则是只有将低级系统异常转化为应用异常的需要才进行捕捉。
各层的处理方式如下:
DAO层:引发DAO异常的问题往往是不可恢复的,如数据连接失败,SQL语句存在语法错误,强制捕捉的检查型异常除了限制开发人员的自由度以外,并没有提供什么有意义的作用。因此,Spring的异常体系都是建立在运行期异常的基础上,这些异常都继承于DataAccessException(RuntimeException异常子类),所以,除了出于将低级系统异常转化为应用异常的需要,没有必要捕获异常,让DAO类自动上抛异常即可。
Service层:只捕获自定义应用异常,其他异常上抛。
Controller:只捕获自定义应用异常,其他异常上抛。springmvc提供了统一异常模块,统一异常模块将定义的异常捕获,记录日志,然后根据配置的异常的类型顺序跳转到相应的页面。
2.3 关于日志
1) 关于java日志的几大恶劣设计
在电信级或者银行级的软件系统,在稳定性和可靠性上要求要苛刻得多,出现问题后要能够进行快速定位。这就依赖于一个好的日志系统。研发人员虽然对系统比较熟悉,对功能如何实现的也有一定的把握,但一个系统往往是庞大的,更为可能的是系统已经经过"几代人"接管,到最后,可能整个项目组内没有对整个系统的每一个角落都了如指掌,这时候,问题就出现了。对于研发人员来说,最擅长的就是通过问题现象去代码里进行分析,现场产品因为在运行,一般来说是不被允许使用调试工具直接在现场进行调试的。而问题只能通过日志进行分析。也就是说,日志对于问题的定位至关重要。系统日志设计的好坏会直接影响你解决问题的效率和质量。下面就是一些具有"坏口味"的日志实现:
1. 吞掉异常。
try{
... ...
}atch(Exception e){
//什么都没做
}
吞掉异常是最恶劣的代码习惯。如果发生问题,无人直到发生了什么问题。现场支持人员能做的惟一的事情是去猜到底系统发生了什么?
2. 吃掉原始异常,抛出另外一个自定义的异常。
try{
... ...
}
catch(Exception e){
/吞掉原始异常,再抛出一个自定义异常
MyException myE = new MyException();
logger.log(LogLevel.ERROR,myE);
throw myE;
}
原始异常最能反映问题的实际情况,里面的错误信息是最全的,包括发生问题的调用上下文,以及行号等。而自己再抛出另外一个异常,无疑是将这些最重要的信息给隐藏掉了,无端地给问题定位带来难度。
3. 多此一举的自定义错误码
try{
... ...
}
catch(Exception e){
logger.log(LogLevel.ERROR,ERROR_CODE,"Error reason");
return ERROR_CODE; //吞掉原生异常,直接返回错误码
}
Java对于错误,缺省的情况下是通过异常来表达,包括Java自带库也是通过这种方式
实现的。使用异常方式是Java代码的最佳选择,这样不但保证了整个系统的一致性,同时能保证原始的错误信息毫无遗漏地暴露在我们的面前。如果自己的系统使用错误码,不但多此一举,而且容易将最有用的信息给屏蔽掉,实在是无一点价值。如下面的异常,如果不直接打印出来,会将最有用的信息unable to create new native thread给漏掉,定位问题时还以为是普通的堆内存溢出。
Exception in thread "main"
java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:574)
at TestThread.main(TestThread.java:34)
4. 不正确的日志级别
try{
... ...
}
catch(Exception e){
logger.log(LogLevel.DEBUG,e); //日志级别为DEBUG
}
在真实的生产环境,基于性能的考虑,一般日志的运行级别只会设置为ERROR/WARN,如果代码中将这种出错情况的日志级别设为DEBUG, 那么这种日志在现场根本不会打印出来。
2) 什么是好的日志?
众所周知,在log4j中定义5个最常用日志级别:debug、info、warn、error、fatal,级别依次严重。
可大家思考过,什么时候应该记info,exception时是应该记warn,还是error呢 ?
debug:程序内部的信息,对于外部使用的人是没有意义。比如:函数的执行时间。(fine-grained information events that are most useful to debug an application)debug记录的是细粒度的事件信息,对于调式程序是非常有用的。
info:informational messages that highlight the progress of the application at coarse-grained level.(强调应用的执行的进度,关键分支的记录)。比如:关键分支记录(输入参数等),
对运维工程师来说这些信息也是有价值的,info指明程序的运行是否符合正确的业务逻辑。
对于warn与error级别,是我自己最困惑的 ?
先讲个形象的例子 :
有一天,李三说请你帮个忙,帮他收拾一下自行车,但是他的自行车是放在他自己家里的,房屋上锁。
1、李三:给你钥匙,此时你没有工具,但是你可以用你自家的工具修理自行车。结果:完成任务;过程:存在问题,内部可以处理此问题。
2、李三:没给你钥匙,但给你工具或者没给你工具,此时你不能接触到自行车。结果:无法完成任务;过程:存在问题,无法处理此问题。
结论:1情况,记warn;2情况,记error。观点:关注的是最小单元的业务是否能够完成,而对于应用来说,最小单元的业务就是“method方法”,每个方法去完成的任务都是一个独立的、最小单元的业务。
提醒:必须结合具体场景来判断,比如:dao从数据库获取数据失败,对自身来说是error,没有完成获取数据的任务;但对业务层来说,或者有其它方法修复此异常,如通过http重新获取等,此时业务层是完成自身任务的,所以为warn。
warn:potentially harmful situations.(潜在的有害状态)。比如:广告投放,淘宝搜索右侧p4p广告会根据地域展现,但某次用户搜素,ip地址获取失败,可能会用默认值替代或者为空,但是并不影响右侧广告的展现。所以展现广告的任务是完成的,对于业务来说是执行成功的,尽管过程中出现问题。
error:error events that might still allow the application to continue running.(错误事件发生,程序或许依然能够运行)。比如:广告前段展现,通过http从引擎获取数据时,因为引擎的机器连接数达到上限或者临时网络原因,timeout,但程序能正常运行,next请求引擎则成功。从函数角度看,此函数任务没有完成,记error。
fatal:very severe error events that will presumably lead the application to abort.(错误可能会导致应用崩溃)。
总结:
满足了如下条件就是好的日志:
1. 打印的是最原始的错误信息,没有经过任何转换
2. 给出了正确的日志级别(参考上面),以保证在出错情况下,关心的日志能够真得打印出来
3. 在异常发生时,日志中有明确的调用上下文。
遇到异常,下面的日志打印就非常有效,既简单,又实用。
try{
... ...
}
catch(Exception e){
logger.log(LogLevel.DEBUG,e); //日志级别为DEBUG
}
2.4 事务处理(如果使用spring可以交由spring管理,管理方式有两种。这里不再提)[按框架规范执行]
当前的系统架构中支持嵌套事务,事务管理必须通过系统框架提供的事务管理类(TransManager)来完成。
1) 事务调用方法
? 开始事务
trans.begin()
? 提交事务
trans.commit()
? 回滚事务
trans.rollback()
事例代码