项目重构的一些思考与经验总结(待续)
项目重构的一些思考与经验总结
要么爬过坑走过弯路,要么用脑子思考。看你如何选择?
那年那月那样的我
还依稀记得刚出道不久的自己照葫芦画瓢的写着代码,不知道为什么要这么写,更无从说这样写的好处与坏处了,从网上复制粘贴,实现了功能就不管了,这也是大部分码农干的话吧。那段时间虽然在经验上积累了一些,但是在技术上没有从心底的去领悟一些东西,没有思考,更没有尝试着去创新。吃了亏才知道改,走了弯路知道接下来的每一步都要谨慎,都要用心。
悟性决定成长
你能悟多少,你才能成长多少。跑偏了,回归正题。今年来,到这家公司没有什么新颖的技术,技术体系基本上是 ant + spring + mybaits + mysql + bootStrap + html + js。也由于我负责的这几个项目进度要求不是很紧急,所以有时间去研究项目的架构,代码优化,重构等,抛弃以前蹩脚的编程方式。
哪里让我感到蹩脚
1、每每遇到异常时,不知道是抛出还是处理,要怎么处理,没有一个很理性的认识,处理异常总是凭感觉看心情。
2、日志的打印问题,打多了不好,打少了也不好,甚至出现打了很多日志,但是出错时发现没有打印相关信息。时常看到有代码基本上有一半的篇幅是日志,很不雅观。
3、Controller层接口返回五花八门问题,这我也是醉了,负责重构的这个项目返回值有Map,List,Void还有封装的resultBean 两三种。这样前端处理对应着也就五花八门了。
4、大小写不统一问题,这个是重构最头疼的一个问题。举个栗子,在使用Mybaits时候,有时候查询结果直接封装的对象里,有时候省懒直接用Map封装,问题来了,对象封装返回字段名转变成了驼峰法,Map直接是查询的字段名,这样混着来,查问题很费解。
5、Ajax没有进行统一封装问题。一般不会有太大问题,但是,因为Ajax是局部刷新,所以不能进行页面跳转的操作,在Session过期跳转登录页面上如果不对Ajax统一封装,那就尴尬了。
总结的处理方式
AOP统一处理异常并打印日志
AOP日志辅助
统一ResultBean
直接上代码
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.f2f.voi.common.CommonContainer;
@Component
public class BaseInterceptor {
@Autowired
protected CommonContainer commonContainer;
protected static final Logger logger = LoggerFactory.getLogger(BaseInterceptor.class);
protected boolean isContainAnnotationName(JoinPoint pjp, String name) {
Signature signature = pjp.getSignature();
MethodSignature methodSignature = (MethodSignature)signature;
Method targetMethod = methodSignature.getMethod();
Annotation[] annotations = targetMethod.getAnnotations();
for(int i = 0; i < annotations.length; i++){
Annotation annotation = annotations[i];
if(annotation.annotationType().getName().contains(name))
return true;
}
return false;
}
}
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
/**
* @describe 日志打印控制
* @author YoungSmith
* @date 2018年6月14日 下午2:14:06
*/
@Aspect
@Component
public class LogInterceptor extends BaseInterceptor {
private static final Boolean isLogPretty = false;
@Pointcut("execution(public * com.f2f.voi.*.controller.*.*(..))")
public void myMethod() {
};
/**
* 前置通知:在某连接点之前执行的通知,但这个通知不能阻止连接点前的执行
*
* @param jp
* 连接点:程序执行过程中的某一行为
*/
@Before("myMethod()")
public void before(JoinPoint jp) {
String method = jp.getTarget().getClass().getName() + "." + jp.getSignature().getName();
StringBuilder message = new StringBuilder(method + "() started.");
logger.info(message.toString());
if(methodHasNonAopLogAnnotation(jp)){ // 方法上有不打印日志注解
return;
}
List<Integer> list = getNonAopLogParamsIndexs(jp);
Object[] args = jp.getArgs();
StringBuilder logStr = new StringBuilder(method + "'s param is [");
for (int i = 0; i < args.length; i++) {
try {
if(list.contains(i))
continue;
logStr.append(JSONObject.toJSONString(args[i], isLogPretty) + ",");
} catch (Exception e) {
// do nothing
}
}
logStr.deleteCharAt(logStr.length() - 1);
logStr.append("]");
logger.info(logStr.toString());
}
/**
* 获取方法中没有NonAopLog注解的参数的下标列表
* @param jp
* @return
*/
private List<Integer> getNonAopLogParamsIndexs(JoinPoint jp) {
MethodSignature signature = (MethodSignature) jp.getSignature();
Method methodObj = signature.getMethod();
Annotation[][] parameterAnnotations = methodObj.getParameterAnnotations();
List<Integer> list = new ArrayList<>();
outer: for(int i = 0; i < parameterAnnotations.length; i++){
Annotation[] annotations = parameterAnnotations[i];
for(int j = 0; j < annotations.length; j++){
Annotation annotation = annotations[j];
if(annotation.annotationType() == NonAopLog.class){
list.add(i);
continue outer;
}
}
}
return list;
}
@After("myMethod()")
public void after(JoinPoint jp) {
String logStr = jp.getTarget().getClass().getName() + "." + jp.getSignature().getName() + " finished.";
logger.info(logStr);
}
/**
* 环绕通知:包围一个连接点的通知,可以在方法的调用前后完成自定义的行为,也可以选择不执行。
* 类似web中Servlet规范中Filter的doFilter方法。
*
* @param pjp
* 当前进程中的连接点
* @return
*/
@Around("myMethod()")
public Object doAround(ProceedingJoinPoint pjp) {
long time = System.currentTimeMillis();
Object result = null;
String logStr = pjp.getTarget().getClass() + "." + pjp.getSignature().getName();
try {
result = pjp.proceed();
} catch (BusinessException e) {
logger.info(logStr + " has BusinessException. {}", e.toString());
return ResultUtil.fail(e.getErrorCode(), e.getErrorMessage(), e.getErrorData());
} catch (BaseException e) {
logger.error(logStr + " has BaseException. The Error Message is:", e);
return ResultUtil.fail(Constants.SYSTEM_ERROR, commonContainer.getMessage(Constants.SYSTEM_ERROR));
} catch (Exception e) {
logger.error(logStr + " has Exception. The Error Message is:", e);
return ResultUtil.fail(Constants.SYSTEM_ERROR, commonContainer.getMessage(Constants.SYSTEM_ERROR));
} catch (Throwable e) {
logger.error(logStr + " has Throwable. The Error Message is:", e);
return ResultUtil.fail(Constants.SYSTEM_ERROR, commonContainer.getMessage(Constants.SYSTEM_ERROR));
}
logger.info(logStr + " Execution Time is:" + (System.currentTimeMillis() - time)/1000.00d + "s");
return result;
}
private boolean methodHasNonAopLogAnnotation(JoinPoint pjp) {
return isContainAnnotationName(pjp, "NonAopLog");
}
}
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @describe 不通过AOP打印日志,需要自己手动打印
* @author YoungSmith
* @date 2018年6月26日 下午3:03:57
*/
@Target(value={METHOD, PARAMETER})
@Retention(RUNTIME)
@Documented
public @interface NonAopLog {
String value() default "";
}