代码规范(二)——java篇

51. 

try {

    newMAC = PosCryptor.getMAC(PosServerConfig.getKeyHost(),PosServerConfig.getKeyPost(),

            entNo, posNo, operNo,requestMsgTypeId, newBody);

    LOGGER.debug("newMAC:"+LoUtils.byte2HexStr(newMAC));

}catch (UnknownHostException e){

    throw new PosRunTimeException("keyserver 连接异常!",e);

}catch (IOException e){

    throw new PosRunTimeException("keyserver io异常!",e);

}catch (Exception e){

    throw new PosRunTimeException("keyserver格式解析异常!",e);

}

建议:参考第19节。

建议:org.apache.commons.codec.binary.Hex

52. 

@Service

public class XxxServiceImpl implements XxxService {

private static Object invokeGet(Object o, String fieldName) throws Exception {

String[] fieldNames = fieldName.split("[.]");

for (String fn : fieldNames) {

o = o.getClass().getMethod(fn).invoke(o, new Object[0]);

}

return o;

}

建议:通用的static函数,尽量放在Util里,尽量减少XxService的长度。

建议:分隔string,不应该用“.”,因为它在regex里有特殊意义。应该是逗号“,”。

org.springframework.beans.propertyeditors.StringArrayPropertyEditor.

DEFAULT_SEPARATOR就是“,

建议:这个函数的本身意义,就有很大毛病:

A)变量o被赋值多次,却被当成结果只返回一次。

B)变量o既作为输入参数,又作为输出参数。

C)变量o前后的意义都不同。

输入时的意义是:“某个普通对象”,将要被调用。

输出时的意义是:“某个普通对象”调用某method的结果。

 53. 有人说:持续集成,只是编译打包就行了,不需要跑自动化测试。

建议:参考第38节。

 55.  

建议:在循环里搞删除,影响性能,应该使用sql批量删除,输入的参数为:1,2,3

建议:return行,返回:10,这是C风格,不是Java风格。

Java风格是:使用Exceptionpublic void myDeleteFunction(String str) throws Exception

建议:扔掉try-catch。参考第19节。

建议:经过了以上3个和第19节的建议,这个函数只需要1行就行了:

msgPrivateMapper.deleteXxx (ids);

 56.  

建议:所有的能跑的东东,都应该写在单元测试(junit)里,而不是main里。

 

建议:有大量的注释(///*  */),就是错误。

要么删除;要么解开,改变成为一个个的单独的junit test method

那么,在持续构建里,就能总是能把那些功能跑到,总是能自我检查尽早检查。

 

建议:main()最后1行,不需要显式调用System.exit(0)

 58.  

建议:把重用多次的NY做成常量。

59.  

建议:deladdupdate,都是相同性质的事务,Spring默认的行为就够用了。

所以,rollback-forno-rollback-for2项,可以删除。

 60. 有人说:代码写到一坨了,没法搞单独的TestCase

那就说明:这个程序比较差,是紧耦合的。好的是:松散耦合。所以,要重构了。

解耦的最好方式是:接口+组合。例如:

public class MyBusiness {

private  MyBusniess00  bn00;

private  MyBusniess01  bn01;

private  MyBusniess02  bn02;

……

//  setter & getter

}

这也是Spring最常用的套路。

附带提个常识:

如果某程序有好的单元测试和覆盖率,那就肯定说明:它的结构是好的,松散耦合的。

 61. 如何对Java程序做格式化?

建议:这是个来自于Spring源码里的文件,用Eclipse导入:

Preferences>Java>Code Style>Formatter-->Import 

62. 某项目有600class,修改了2个,影响了些啥?

如果有完善的单元测试,跑跑,很快就知道影响了些啥,就能快速修改,极大的增强信心。

所以,如果你按照XP/TDD的核心思想(单元测试)做程序,将会越做越顺!

63. 源码里有192.168.1.xxx,如何评价?

如果这个IP是自己的,下班就关机了……

A)那么,夜间Jenkins服务器跑CI(持续构建)/每日构建(Daily Build),肯定失败。

B)别人从SVN拉下来项目,跑,也肯定失败。

所以,这个IP绝不应该是自己的。

 65. 如何查找性能瓶颈?

DeveloperA负责项目P,最核心的功能B,性能总数字是100/12秒。

里面有这些东东:通讯协议解析C,业务D,业务E,业务F

请他把这4项的耗时的具体数字和TestCase拿出来,他没有。

请他把业务全部扔掉,只把C的耗时的具体数字和TestCase拿出来,他没有。

 

建议:较好的查找性能瓶颈的路线:

A)设定个固定次数,例如:600,还可以设置大些,后面会多次用到这个。

设定基线,即:assert(); 每次跑完,必须要能看见绿条(Green Bar)作为结果

B)单独把核心的功能B600次。

C)单独把C/D/E/F分别跑600次。

D)看看C/D/E/F里,谁耗时最多,就最先优化谁。每修改一次,就重新跑一趟。

搞好了这项,往往达到70%的效果。

E)再优化耗时第2多的。搞好了这项,又增加了20%的效果。

 

 

优化某项,里面又有多个组件R/S/T,如何分辨性能瓶颈?

建议:重复之前的路线,针对R/S/T都建立TestCase……各个击破。

67. 跟性能最密切的程序段,可能是些啥?

WebApp里的Filter、Inteceptor,普通App的协议的解析和反解析……

是每次必须经过的路线。

针对这些,必须有专门的TestCase,反复跑,反复优化。

69.  

建议:HashMap是有关算法的,肯定要内建hash表和计算hash

如果key是纯粹的数字,而且很小,那就直接做成数组/List,数组的index就是key

数组是直接存取,不需要计算hash,更快。

 

建议:既然用了Spring,就有了BeanFactory,那就用它建立Singleton,不要自己手写getInstance()了。例如:

<bean id="dataSources" class="org.springframework.beans.factory.config.ListFactoryBean">

    <property name="targetListClass">

        <value>java.util.ArrayList</value>

    </property>

    <property name="sourceList">

        <list>

            <ref bean="dataSource_0" />

            <ref bean="dataSource_1" />

 

有人说:“做到spring里面去,效果是一样,还是这样好使用”

建议:列几项软件原则:

A)“不做code,少做code。”  “代码行数越少越好。”

B)朝着稳定的方向依赖。

完成同一个功能,是用Spring框架搞得稳定呢?还是用自己写的code搞得稳定呢?

 70. 

Map<Integer,String> messageBody = new LinkedHashMap<Integer,String>();

messageBody.put(3,termTradeCode);

messageBody.put(42,entNoAsc);

messageBody.put(62, "0012"+versionsAsc);

建议:

LinkedHashMap有复杂的算法,尽量回避。

Map显式的分配空间,尽量大些,这里分配256就足够了。

List<Pair<K, V>>,只是简单的顺序的存取,很实用。参考第29节和上节。

或者,用数组,更快。

 71. 有人说:我做的程序,无法测试性能。还有人说:我做的这个框架,必须带些业务才能测试性能,无法单独测试纯粹的性能。

建议:

(A) 所有程序都能测试出来性能。如果无法测试出来,或者测试出的数字差,说明:

这个程序或者测试程序有毛病,需要改善。

(B) 业务是业务,框架是框架,它们之间应该是弱耦合的,针对接口编程的,能替换其它实现类的。它们都是能单独测试的,还能用上针对接口的Mock的东东。

72. 

这个极品工程里,只有几个class,每个class还占了一个package。

建议:

(A) 不要瞎成立maven工程。

对于不确定是否需要独立的工程,先都放在一个里,便于修改。

这个cache-api工程,可以合并到common工程里。如果code太烂,干脆就废除掉。

(B) 静态方法调用要回避,应该做成实例形式,便于搞Object的模拟和测试。

 75.  

建议:

(A)权威的证明资料:J2EE Development without EJB

我们自建的MyException,都应该extends RuntimeException,而非checked Exception。

(B)重复的、临时的、不伦不类的、命名意义模糊的、小范围使用的class,赶快删除吧。

76.  

建议:PowerDesigner等工具生成的code,也要用眼睛看3遍。明显臃肿的,要删除。

77.  

建议:

A)用void代替boolean。如果出毛病了失败了,就抛出RuntimeException

B)从try开始的几行,只保留Ecms所在的一行就够了。

C)带有@Test method,都应该声明throws Exception,为了里面尽量少写try-catch

78. 

建议:

(A) else-if,要有else { throw new RuntimeException() }

(B) Java7,switch( string )

79.  

建议:用标准的JavaDoc方式,这还能避免某些源码覆盖率工具的解析错误。

80.  

后面requestcache里的值,精确的覆盖了前面requestcache里的值。

 81.  

建议1request应该是个傻傻的的简单的数据容器,里面怎么有个ServerMainHandler

ServerMainHandler调用request,是正常的,还能反过来调用,就是错误。

建议2handlerExecute(...)明显是搞业务的,里面怎么会放个Spring单态类?

应该通过set/get的方式注入Spring单态类才对,不用每次在参数里传入。

 82.  

aWeb项目的有多个springxml配置,可能跟b.jarc.jar里的发生重名冲突。

如今,公司测试环境OS上多个app,配置都统一在一个目录里,配置文件更加容易冲突。

建议:配置文件应该统一命名为projectName_config.xmlprojectName_config.properties

不应该有applicationContext*.xml这种文件名字了。

83.  

建议:把package级的javadoc写在.java里,jdk和开源软件都走这种标准路线了。

 84.  

建议:继承,只是OO的特性,跟常量(Constants)没有关系。

应该杜绝常量类的继承。还应该杜绝常量接口。

85.  

建议:

org.apache.commons.lang3.StringUtils

public static boolean isNotBlank(CharSequence cs)

public static boolean isNotEmpty(CharSequence cs)

 86. 

建议:common有太多的小模块了,全部加起来也只430KB

这会浪费dev很多的编译和查错时间。

应该合并为1个模块。

 87.  

建议:

A)最明显的是5765行,连续出现3次相同的code。应该重新整理if-else

B36行,看看spring源码,initFilterBean()里面是空的。说明:此类不需要spring

原则:性能要紧之处(例如:Filter),继承层级越少越好,设置为final也好。

所以,这里可以扔掉spring的父类,实现最基本的Filter接口就行了。

C42行,用str.contains()

D)多个if-else,就必须要有足够的自动化TC覆盖到每个分支,否则,会出多个bug

EFilter的参数,也应该来自于spring工厂。参考第96节。

88.  

疯狂的if-else,这来自于Apache Continuum,是个反OO的典型范例。

前人蹉跌,后人知警。

89.  

建议 1:第1行,返回的数据容器,应该是个普通的List/Map,怎么是JSONArray

List/Map是性能最高的,JSONArray性能肯定偏低。

JSONArray是个偏重的容器,包含了List,还有些跟数据容器没有关系的东东。

对于网络传输和解析,这些都是缺陷。

 

建议 2mapvalue,被前面的for循环多次赋值,实际却只用了最后的一次赋值!!!

而且,MapKey顺序是不确定的,最后得到的value,也是不确定的!!!

建议 3:高亮的行,JSONArray.parseArray();

参数里应该是普通String才对,实际却是jsonArray.toString()!!!

倒腾几番,参数jsonArray完全没有排上用途,结果还是个String

那干脆做成Map<String, Integer>就更简单了。

建议 4:网络传输,格式可能是json/xml/自定义string/binary/……。

在发送端和接收端,解析出来的都应该是基本的Pojo/Model

如果用Pojo/Model,修改了传输信息格式,jsonàbinary,不会修改传输类(DTO)。

它转换到各种格式,是最容易的,也是最开放的。

如果用JSONArrayjsonàbinary,将会修改大量的传输类(DTO)。

建议 5:原则:朝着稳定的方向依赖。

List/Map稳定呢?还是开源的(可能近几年没人管理或移交给他人的)fastjson稳定呢?

建议 6:避免用Map,避免hash计算,多用List

就算用Map是对的,Mapkey也应该是IntegerStringClass等,绝不是JSONArray

建议 7:从原意看来,Map里只有一个元素(Entry),keylistvalueint

既然信息只有两样,不用Map,恰好可以用apache-commons Pair<List, Integer>

建议 8:从原意看来,如果要携带更多信息,可以自定义class来搞。 

 

建议 9:综上所述,ServerClient端都做得很奇葩。

以后dev再遇到这种code,应该立即提出来。

越早暴露就越好,bug越少,性能越高,今后修改越少,越节省公司的成本。

 

建议 10

在内网里,ServerAServerB取得比较稳定的数据,可以把吞吐量搞大些。例如:

多数情况,总共都只有几百条,那就每次取2000条数据缓存于ServerAsession里。

避免每次取10条取多次而增加request次数。

 90.  

建议 1:成员变量Random被多线程访问,会出错。

建议 2:验证码,应该避免长得像的字符:

A)数字1L的小写和i的大写。

B)数字0和字母o大写。

C)数字2和字母z

建议 3:成员变量都应该显式声明为final,扔掉private

 91.  

建议:这种情况,“可读性”比“性能和硬编码”更重要。

String.format(12%s45%s, 3, 6);

92.  

建议:常用的英文词汇,搞简写就行了。

transactionàtx

account-->acc

currency-->ccy

level-->lv

message-->msg

amount-->amt

commandàcmd

buffer-->buf

receive-->rcv

service-->svc

93. 跑·不跑 

今天是2014/12/31,这几个的最后更新日期,竟然是146天之前!!!

建议:测试类,肯定是经常增加内容的。例如:

A)测试组报了bugdev就应该增加几个TCTestCase)了。

B)有了新需求,应该增加几个TC了。

C)程序的某段,也许有更好的更快的做法,要做个试验吗?增加TC了。

Ddev估计到某块可能出毛病,那么今后肯定会出毛病。增加TC了。

E)需要几个性能测试?增加TC了。

 

程序,就是用来跑的,必须经常的跑,才能检查出健康状况。

持续构建,把工程里几万行code跑几遍几十遍,就是经常的“跑”。

本图,就是典型的“不跑”。

94. 外部配置·main程序(jar 

在spring xml里配置相对的.properties文件路径:

<context:property-placeholder location="file:../../hsserv/config/pos.properties" />

 

这样做的好处是:

(A)只要把同一套配置编辑好了,今后发布N次jar,都不需要修改配置了,

减少了修改次数,这条会让测试人员和部署人员轻松很多。

(B)配置文件放在专门的目录里,设置权限,

只让有权限的人员操作,禁止他人的读写,安全。

(C)在一个OS实例里,appA、appB、appC能共用相同的配置。

(D)相对路径总是优于绝对路径。

(E)不需要设置OS级别的系统变量classpath,不需要设置tomcat的classpath。

 

打包的指令:mvn -DskipTests install -P deploy

point1-0.0.1-SNAPSHOT.jar/META-INF/ MANIFEST.MF里,有两个attribute:

Class-Path: . config/ ../

Main-Class: com.gy.pos.Main

这表明:,classpath包括了:

当前路径,当前的config里的路径,上一级路径。 

有些.xml要单独的拷贝出来,避免出现重复的配置,这用到了maven-resources-plugin

它们必须在classpath里,而且必须在jar外面,而且还能被别的app共用。

完整的配置:

 运行程序的指令:java -jar point1-0.0.1-SNAPSHOT.jar

path就是jar所在的目录

 

如果.xml还要被webapp(.war)共用,这么设置:

<Class-Path>. /opt/xml_conf/ /opt/lib/a.jar</Class-Path>

注意:它们之间的分隔符是空格,不是“;”或“:

ecms_config.xml等放在/opt/xml_conf/里,就能被找到了。

95. 外部配置·web程序(war

如果maven web项目的某子模块用了这个配置,在pom.xml里屏蔽掉。 

在打war包的时候,子模块的jar就 不会 有这个文件了。

然后,在distaggregator子模块里修改spring配置:

<context:property-placeholder location="file:../../hsserv/config/posweb_config.properties" />

或者:

<context:property-placeholder location="classpath:posweb_config.properties" />

这是最好的方案,保持springxml配置在多种环境dev/test/uat/prod都保持一致。

当然,需要先在OS级别设置系统变量:

CLASSPATH=.:/opt/xml_conf:../config
export CLASSPATH

 

注意:它们之间的分隔符是“:”,不是空格。

注意:这是全局性的,如果OS跑几个app,它们的类路径有同名的配置文件,可能会冲突。

最典型的例子:common-log-0.0.1.jar里有log4j.xml,会覆盖pos-server.jarlog4j.xml

所以,pom.xml的打包功能,要排除掉这些文件。另外,也只能用人工来提防这个了。

 

有些是无法用classpath的,例如:log4j.xml里设置log文件,那就只能用文件路径了:

<param name="File" value="/opt/hsserv/log/myProj/debug.log" />

事实上,这个值跟classpath也是完全没有关系的。

 96. 外部配置·把spring的配置值传递给Filter

首先,确认几样东东的执行顺序:

1ContextListener(标准ServletContextListenerSpring ContextLoaderListener

2Filter

3Servlet

所以,在Filter之前,Springctx就已经组装好。

Filter做init()的时候,就能拿到Spring的配置值(myProperties)了。

<util:properties id="my_config" location="classpath:my_config.properties" /> 

以上2行都是spring的标准api 

通用的Util.getConfigBean()还能在 任何时候任何地点 使用,这就足够灵活了。

97. 外部配置·一种配置有3套配置值

怎么会有3套配置值?后面还能覆盖前面的值?像“传染病”有了传递关系?

对于长期的维护,保持唯一性,很重要。

对于长期的维护,配置文件,越少越好。

再例如:

*-config.properties里有个开关runMock=on

*-config-test.properties里没有设置这个开关

*-config-prod.properties里没有(忘记了)设置这个开关

那么,在prod环境,用的就是开发环境的on!实际上我们需要的是off!

所以,建议删除此xml的*-test.properties、*-prod.properties。

98. 外部配置·自定义的扩展类 

目前,多个app用的是公司common里的:GyPropertyPlaceholderConfigurer

作用:把多个.properties内容合并为一个大的,能取得配置值。

A)这样搞,配置值会冲突。

曾经有人说:“制定一个命名方式比较好,比如用子系统标识做前缀”。

其实,“在每个配置文件里面,做监督检查”是很难的。

如同公司规定“禁止全体员工吃牛肉”,很难实现和监督。

Bconfig.propertiesnotice-config.propertiesacl-config.properties等等,

本来都是独立的,就不应该在此class里合并。

 99. 外部配置·多个配置文件·推荐的路线 

优点:

1 不需要自己创建classinterfaceSpring做整合。符合大原则:Don’t Write Code

2 每个配置文件都保持了独立性,都能用Spring getBean()方式得到。

Properties  posConf  = ctx.getBean("pos_PosConf", Properties.class);

里面的Key Value的命名也是最宽松的,随便倒腾都行,不会影响别的.properties

3 多个配置之间,永远不会有覆盖的事情。

100. 外部配置·全局配置·局部配置

如果公共classpath里已经有了some_config.properties

但我的app不用那个,只用自己专有的some_config.properties,怎么办?

很简单,把some_config换个新名字some_config_for_myproj

各种局部配置都可以这么做。


你可能感兴趣的:(代码规范(二)——java篇)