1.
Result result=new Result();
result.setCode(201);
result.setStr(re);
result.setMessage("獲取id成功");
建議:對常用的功能,可以:新增Constructor,把4行code用1行搞定。
Result result=new Result(code, str, msg);
2.
StringBuffer stringBuffer=newStringBuffer();
String[]formatStr=snFormatStr.split(splitChar);
for(String inStr:formatStr){
//判斷以str開頭,不分大小寫
if(inStr.matches("^[Ss][Tt][Rr].*")){
stringBuffer.append(processStr(inStr));
建議:StringBuffer改成StringBuilder。
倒數第二行用commons-lang3的,既能避免硬編碼,又避免了寫正則表達式。
StringUtils.startsWithIgnoreCase(CharSequence,CharSequence)
3.
private String processDate(String str)throws Exception{
String[] strings=str.split(innerChar);
Stringdef="yyyyMMdd";
建議:凡是屬於 “無狀態的” “通用的”功能,可以放在Util.java裏。
如果確實需要硬編碼,放在Util.java裏,讓它們只永遠出現一次。
4.
String re="";
……
if(ar.length>1&&add!=""){
建議:org.apache.commons.lang3.StringUtils.EMPTY
重用常量,不要自己新創建。
5.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Attemptingto resolve a principal...");
建議:既然用了slf4j,裏面就封裝了判斷log level的功能。
LOGGER.isDebugEnabled()是多餘的。
6.
if (attributes == null) {
return null;
……
if (itemNo.length()!=3) {
thrownew RuntimeException("ItemCode has exceed 3 bits !");
建議:
jdk,throw new IllegalArgumentException(...);
jdk,throw new IllegalStateException(...);
org.springframework.util.Assert.isTrue(boolean,String)
org.springframework.util.Assert.state(boolean,String)
7.
if (null != sos) {
try {
sos.close();
}catch (IOException e) {
LOGGER.error("handleRequest 關閉流出現異常! ",e);
}
}
建議:
org.apache.commons.io.IOUtils.closeQuietly(OutputStream)
org.apache.commons.io.IOUtils.closeQuietly(Writer)
8.
public void setApplicationContext(finalApplicationContext applicationContext) {
super.setApplicationContext(applicationContext);
this.applicationContext = applicationContext;
}
建議:既然父類已經有了ApplicationContext,子類的就是無用的,可以刪除。
9.
public classImageVaditeAuthenticationViaFormAction
if(this.credentialsBinder != null &&this.credentialsBinder.supports(credentials.getClass())) {
this.credentialsBinder.bind(request, credentials);
}
建議:Web層裏,傳遞給Service層的東東,不應該有ServletAPI。
10.
UserCacheVO vo = new UserCacheVO();
vo.setLoginIP(request.getRemoteAddr());
vo.setLoginTime(DateUtil.DateTimeToString(newDate()));
vo.setResourceNo(resourceNo);
vo.setUserName(loginId);
vo.setUserSymbol(userSymbol);
建議:做成UserCacheVO vo =new UserCacheVO(w,x,y,z);
11.
建議:合併。每個package裏,有幾個幾十個類是正常的。
com.gy.prvg.acl.constant裏的多個常量類,合併爲一個。Enum,也做在常量類裏面。
(2014/09/22增補)目前公司裏還有個類似的狀況,工程師們喜歡狂建項目——
只有幾個類的十幾個類的,都能做個單獨的項目出來。
建議:一個工程有了2000--4000個類,可以考慮拆分。幾百個類的,先保持在一起。
好處:便於開發,便於查找,便於檢錯,便於調試,便於維護,便於測試。
12.
public static final List<AccountType>AllTypes() {
List<AccountType>types = new ArrayList<AccountType>();
for(AccountType accountType : AccountType.values()) {
types.add(accountType);
}
returntypes;
}
建議:List<TimeUnit>list = java.util.Arrays.asList(TimeUnit.values()); 一句話搞定。
13.
@Override
public String toString() {
Map<String,Object> map = new HashMap<String, Object>();
map.put("loginId",loginId);
map.put("resourceNo",resourceNo);
map.put("deptNo",deptNo);
map.put("userName",userName);
map.put("userCode",userCode);
map.put("cardId",cardId);
map.put("phone",phone);
map.put("email",email);
map.put("status",status);
map.put("roleType",roleType);
map.put("corpName",getCorpName());
map.put("deptNo",getDeptName());
returnJSON.toJSONString(map);
建議:
可以用JSON.toJSONString(this);一句搞定。或者加上@JsonIgnore能屏蔽些field。
搞json格式,全部項目應該用統一的jar。推薦:fastjson。
14.
import org.apache.log4j.FileAppender;
import org.apache.log4j.Layout;
importorg.apache.log4j.helpers.CountingQuietWriter;
import org.apache.log4j.helpers.LogLog;
importorg.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.spi.LoggingEvent;
public class AclLogFileAppender extendsFileAppender
建議:org.apache.log4j.RollingFileAppender應該足夠用了,不用自建class。
15.
public enum RoleType {
PlatAdmin("平臺管理員"),
CorpAdmin("公司管理員"),
Normal("普通角色");
if ("財務視圖".equals(view.getViewName())) {
view.setViewType("Finance");
}
if ("管理視圖".equals(view.getViewName())) {
view.setViewType("Manage");
}
if ("參數視圖".equals(view.getViewName())) {
view.setViewType("Param");
建議:用ASCII表裏的英文字母或數字。
16.
public interface ILoginService {
voidloadPrivilegeItemList(String resourceNo, String loginId, HttpServletRequestrequest);
HttpResult logon(HttpServletRequest request);
建議:
團長能夠指揮士兵,士兵不能指揮團長。
上層能調用下層,下層不能調用上層。
Service層裏,不應該有Web層api。
17.
if(data.get("uri").indexOf(action.getItemContent())>=0) {
建議:java.lang.String.contains(CharSequence)
org.apache.commons.lang3.CharEncoding.UTF_8
18.
int len = roleCode.length() - 3;
int maxNo =Integer.valueOf(roleCode.substring(len));
String leafNo = String.valueOf(maxNo + 1);
leafNo = (leafNo.length() == 3) ? leafNo :(leafNo.length() == 2 ? "0" + leafNo : "00" + leafNo);
code = roleCode.substring(0, len) + leafNo;
建議:
org.apache.commons.lang3.StringUtils.leftPad(String,int, char)
org.apache.commons.lang3.StringUtils.leftPad(String,int, String)
19.
try {
……
} catch (SystemException se){
LOGGER.error("Finding listCorporation is error !",se);
thrownew SystemException(se.getErrorCode(), se.getMessage());
} catch (Exception e) {
LOGGER.error("Finding listCorporation is error !",e);
thrownew SystemException(ErrorCode.ERROR_9004,"查詢公司出現異常!", e);
每個項目裏的每層的每個類裏,都有這些catch,有實際意義嗎?
白白的增加了幾千幾萬行code。
建議:絕大多數情況,不需要catch。public void someMethod() throws Exception是最簡潔的。
只是在必要之處,例如:返回給頁面之前,才做catch。
附:java的checked exception是個設計錯誤。
按照現代的程序理論:在任何地方,catch都是可有可無的,不應該強迫搞catch。
Java5以前的Runnable run(),就是強迫catch { },讓開發者感到臃腫。
Java5+的Callable,V call() throws Exception; catch { } 就是可有可無的,很清爽。
20.
userRole.setRoleName(URLDecoder.decode(userRole.getRoleName(),"UTF-8"));
建議:凡是可能有編碼毛病之處,用POST方式,
把org.springframework.web.filter.CharacterEncodingFilter當做過濾器,就實現了統管,
就不用在多處搞多個URLDecoder.decode()了。
21.
int index =StringUtil.isEmpty(pageLeafCode) ? 0 : pageLeafCode.indexOf("[");
if (index > 0) {
pageLeafCode= pageLeafCode.substring(index + 1,pageLeafCode.length() - 1);
建議:org.apache.commons.lang3.StringUtils.substringAfter(String,String)
22.
public String toString() {
return"Leaf [leafId=" + leafId + ", leafName=" + leafName
+", leafCode=" + leafCode + ", nodeCode=" + nodeCode
+", leafType=" + leafType + ", leafContent=" + leafContent
+", description=" + description + ", subSystemId="
+subSystemId + "]";
建議:在基類裏定義toString()一次就行了。
org.apache.commons.lang3.builder.ToStringBuilder.reflectionToString(
this, ToStringStyle.SHORT_PREFIX_STYLE);
23.
Map<String,String> data = newHashMap<>();
data.put("uri",request.getRequestURI());
data.put("loginId",SSOConstant.getLoginId(request));
data.put("resourceNo",SSOConstant.getResourceNo(request));
建議:凡是常用的hardcode,都做成靜態常量。
24.
CacheLoadUtil.getRelationMap().put(roleCode,map);
建議:緩存的東東,不應該在static map的裏面,而應該在obj map裏面。
25.
public static String objectToString(Objectobj){
returnobj.toString();
建議:刪除這個函數。
26.
public static StringreplaceSpecialStr(Object value){
if(null!= value && !"".equals(value)){
returnvalue.toString().replaceAll("'", "’").trim();
建議:
org.apache.commons.lang3.StringUtils.isNotEmpty(CharSequence)
java.lang.String.replaceAll(String, String)適合於:正則表達式。
java.lang.String.replace(CharSequence,CharSequence) 更適合此處。
27.
public static String nullConvert(Stringvalue){
returnnull==value?"":value;
建議:該報錯的時候,就報錯,用org.springframework.util.Assert.notNull(Object)
如果確實有用,用:org.apache.commons.lang3.StringUtils.defaultString(String)
28.
void modifyAuditStatus(Long[] ids, StringoperType, Map<String,String> data)
建議:Long[] ids改成:List<Long>,面向對象編程,少用array,多用List。
29.
Map<String, List<Role>> map =new HashMap<String,List<Role>>();
map.put("leftRoles", leftRoles);
map.put("rightRoles",rightRoles);
建議:既然只放兩個,可以用:org.apache.commons.lang3.tuple.Pair.of(left,right)
30.
for (String loginId : addUsers) {
UserRoleur = new UserRole();
ur.setCreated(now);
ur.setCreatedBy(operator);
ur.setIsActive('0');
ur.setLoginId(loginId);
ur.setResourceNo(resourceNo);
ur.setRoleCode(roleCode);
ur.setStatus("1");
ur.setUpdated(now);
ur.setUpdatedBy(operator);
建議:
ur.setIsActive('0');ur.setStatus("1"); 把類似的風格做成兩樣東東了,建議都用int風格。
建議:
用多參數的Constructor,把10行變成1行。
有人對此提出疑問:把10個參數放在Constructor裏,太多了……
他說的,適合於啥情況呢?
OO設計,有幾條重要原則:
(A)迪米特法則——“最少知識原則”。“不要和陌生人說話”。
(B)強內聚,弱耦合。即:關係越少越好。
UserRole,裏面所有的屬性都是“同類的傻傻的boolean/int/String/…”,是很簡單的容器,
不屬於OO設計範圍,就算Constructor裏有200+個參數,也是正確的。
下圖,如果用了3個參數的Result(x, y, z); 將會大量縮減代碼行數。
31.
if(StringUtil.isNotEmpty(viewVO.getViewName())) {
viewVO.setViewName("%"+ viewVO.getViewName() + "%");
where.append("and viewName LIKE :viewName ");
}
if(StringUtil.isNotEmpty(viewVO.getViewType())) {
viewVO.setViewType("%"+ viewVO.getViewType() + "%");
where.append("and viewType LIKE :viewType ");
}
if(StringUtil.isNotEmpty(viewVO.getCreatedBy())) {
viewVO.setCreatedBy("%"+ viewVO.getCreatedBy() + "%");
where.append("and createdBy LIKE :createdBy ");
}
建議:
把"%"改成'%'
public static final char SQL_WILDCARD ='%';
把常用的拼接功能做成個靜態函數:
public static void wildcardSqlWord(Stringstr) {
return Util.SQL_WILDCARD + str + Util.SQL_WILDCARD;
}
32.
// 轉換用戶狀態
switch (u.getStatus()) {
case "0":
u.setStatus("New");//新建
break;
case "1":
u.setStatus("Normal");//正常
break;
case "2":
u.setStatus("Forbidden");//禁用
break;
default:
u.setStatus("Illegal");//非法
break;
// 轉換參數狀態
switch (sys.getStatus()) {
case "0":
sys.setStatus("Normal");//正常
break;
case "1":
sys.setStatus("Forbidden");//禁止
break;
default:
sys.setStatus("Illegal");//非法
break;
}
// 操作級別轉換
switch (sys.getOperationGrade()) {
case "0":
sys.setOperationGrade("無");
break;
case "1":
sys.setOperationGrade("查詢");
break;
case "2":
sys.setOperationGrade("修改");
break;
case "3":
sys.setOperationGrade("刪除");
break;
case "4":
sys.setOperationGrade("全部");
break;
default:
sys.setOperationGrade("Illegal");
break;
建議:
在default後面,不要寫break。
switch(x)裏,儘量不用String,而用enum。
如果確實需要switch(數字),就在enum里加入成員常量。例如:
public enum CmdCategory implements MyEnum {
/**
*<code>dummy = 0;</code>
*/
dummy(0, 0),
/**
* 統一官網
*/
official(1, 10000),
/**
* 個人系統
*/
person(2, 20000),
/**
* 企業系統
*/
company(3, 30000),
33.
private CacheLoadUtil() {
super();
}
public class StringUtil {
privateStringUtil(){
super();
};
建議:
public Util() { // 這裏用了public,是爲了覆蓋率的完美。
throw new java.lang .NoSuchMethodError();
}
34.
String sql = " select l.* fromT_PVG_LEAF l join T_PVG_ROLE_LEAF rl on l.leafCode
建議:應該回避”l”。
”l”長得很像數字1和i的大寫字母,java的語言規範中都回避,long 3用”3L”表示。
35.
if (obj == null) {
result= "PO00000000";
}else {
result= LeafRelation.nextBriefCode(String.valueOf(obj));
}
建議:如果兩個長度都中等,可以合併爲一行,用java的三元運算符:
result = (null == obj) ? x : y
36.
public class CacheLoadUtil {
/**
* <p>以企業資源號爲key 公司對象爲value</p>
*/
privatestatic final Map<String, Corporation> CORPS = new HashMap<String,Corporation>();
/**
* <p>以部門編號爲key 部門對象爲value</p>
*/
privatestatic final Map<String, Department> DEPTS = newHashMap<String,Department>();
/**
* <p>以角色代碼爲key 角色對象爲value</p>
*/
privatestatic final Map<String, Role> ROLES = new HashMap<String,Role>();
建議:緩存,不要搞static Map,用實例化的對象,最好用框架EHCache、Memcache……
37. 推薦的Java測試組件
頁面層:HtmlUnit
業務層:Mockito/EasyMock +JUnit
持久層:HsqlDB/H2/Derby +JUnit + Spring Context
測試結果報表:Cobertura /JaCoCo
38. 自動化測試的重要信息
(1) 想要在程序這條路上走幾十年,搞自動化測試是最正確的路線。
(2) ……
(3) ……
待續。
39. 有人問:爲什麼推薦JUnit4?爲什麼拋棄TestNG?
JUnit,簡單易用,最好了。JUnit4,約束更少,功能更強大。
Testng,本身過度複雜,在各大IDE上的版本都不同,
本身也有內存泄露等毛病,新版本久不更新,應該拋棄。
40.
public static final Map<String,Corporation> getCorps() throws SystemException {
if(CORPS.isEmpty()) {
List<Corporation>cps = SpringBeanUtil.getBean(AclConstants.CORP_SERVICE,CorporationServiceImpl.class).queryCorporationAll();
…………
public class SpringBeanUtil implementsApplicationContextAware {
privatestatic ApplicationContext ctx;
privateSpringBeanUtil() {
super();
}
publicstatic <T> T getBean(String id, Class<T> clazz) {
if(ctx == null) {
thrownew NullPointerException("ApplicationContext is null");
}
return(T) ctx.getBean(id);
}
@Override
publicvoid setApplicationContext(ApplicationContext applicationContext)
throwsBeansException {
ctx= applicationContext;
}
}
建議:刪除Constructor,或者參考第33節
建議:扔掉getBean函數,用:
org.springframework.beans.factory.BeanFactory.getBean(String,Class<T>)
BeanFactory 是ApplicationContext的父接口。
建議:Spring搞的都是OO,我們用Spring也應該遵循OO。OO和靜態的東東是相排斥的。
把Spring的ctx做成靜態的引用,會有多種缺陷。例如:
(A)潛在的內存泄露。
(B)清理對象的時候,總是不能清理static ctx,這是災難性的錯誤。
建議:getBean可能是作者的使用目的,是以static的方式訪問的。
可它的初始化,竟然是以實例的方式搞的!!!
@Override
publicvoid setApplicationContext
建議:經過上面4條建議,可以刪除掉SpringBeanUtil這個可憐的類了。
41.
public String resetPassword(String loginId,String resourceNo,
Map<String,String>data) throws SystemException {
Stringmessage = "";
StringuserSymbol = CacheCommonUtil.getUserSymbol(loginId, resourceNo);
Useruser = CacheLoadUtil.getUsers().get(userSymbol);
SystemParamdPassword =systemDao.selectSystemParamByKey(SystemParamConstants.PARAM_PWD_GROUP,user.getResourceNo());
try{
if(dPassword==null||StringUtil.isEmpty(dPassword.getParamValue())) {//若無公司的默認密碼,則默認爲登錄名
user.setPassword(CoderUtils.toHex(CoderUtils.encryptMD5(user.getLoginId())));
}else{
user.setPassword(CoderUtils.toHex(CoderUtils.encryptMD5(dPassword.getParamValue())));
}
user.setUpdated(newDate());
user.setUpdatedBy(data.get("loginId"));
userDao.update(user);
CacheLoadUtil.getUsers().put(userSymbol,user);
//增加操作日誌
SystemLogUtil.addSystemLog(data,FuncType.SYSTEM_SETTING,AclConstants.SYS_RESET_PASSWORD, "密碼******", "密碼******");
message= "success";
建議:第4行,凡是從緩存取數據的操作,應該建立個成員變量(member variable),
用Spring的標準set方式注入cacheManager對象,堅決拋棄靜態功能。
建議:CoderUtils,詞彙Coder明顯意義錯誤。
可以改成CodecUtils,標準依據:org.apache.commons.codec.*
建議:加密用:org.apache.commons.codec.digest.DigestUtils.md5Hex(byte[] data)
建議:倒數第3行,密碼,不應該以明文方式出現在log裏。
42.
public class SystemServiceImpl implements ISystemService{
privatestatic final Logger LOGGER = LoggerFactory.getLogger(SystemServiceImpl.class);
建議:實例範圍的類,就用實例範圍的Logger。
private Logger LOGGER =LoggerFactory.getLogger(getClass());