spring aop的概念,很早就写博客介绍了,现在在工作中真正使用。
我们很容易写出的代码
我们很容易写出带有很多try catch 和 logger.warn(),logger.error()的代码,这样一个方法本来的业务逻辑只有5行,有了这些,代码就变成了10行或者更多行,如:
public ResultDTO<UserDTO> queryUserByCardId(String cardId) {
ResultDTO<UserDTO> result = new ResultDTO<UserDTO>();
StringBuilder log = new StringBuilder();
log.append("queryUserByCardId:" + cardId);
try {
checkCardIdNotNull(cardId);
StationUserDO userDO = userDAO.queryUserByCardId(cardId);
UserDTO stationUserDTO = DataTypeConvertUtils.DOToDTO(userDO);
result.setData(stationUserDTO);
logger.warn(log.append(" result:").toString() + result);
} catch (StationErrorCodeException e) {
//logger.error(log.append("catch StationErrorCodeException!").toString(), e);
result.setSuccess(false);
result.setErrorCode(e.getErrorCode().getErrorCode());
result.setErrorMessage(e.getErrorCode().getErrorMessage());
} catch (Exception e) {
logger.error(log.append("catch Exception!").toString(), e);
result.setSuccess(false);
result.setErrorCode(StationErrorCodeConstants.STA10001.getErrorCode());
result.setErrorMessage(StationErrorCodeConstants.STA10001.getErrorMessage());
}
return result;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
实际上,我们的业务逻辑就几行而已,中间却夹杂着那么多的异常处理代码及日志信息代码。
如何改进代码
我们可以使用springaop,做一个切面,这个切面专门做记录日志和异常处理的工作,这样就能减少重复代码。
代码如下:
@Override
public ResultDTO<StationUserDTO>queryUserByCardId(String cardId) {
ResultDTO<StationUserDTO> result = new ResultDTO<StationUserDTO>();
checkCardIdNotNull(cardId);
StationUserDO userDO = stationUserDAO.queryStationUserByCardId(cardId);
StationUserDTO stationUserDTO = DataTypeConvertUtils.DOToDTO(userDO);
result.setData(stationUserDTO);
return result;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
我们在切面中做异常处理和记录日志:
@Aspect
public class CardServiceAspect {
private final Logger logger = LoggerFactory.getLogger("card");
// 切入点表达式按需配置
@Pointcut("execution(* *.*(..)))")
private void myPointcut() {
}
@Before("execution(* *.*(..)))")
public void before(JoinPoint joinPoint) {
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
logger.warn(className + "的" + methodName + "执行了");
Object[] args = joinPoint.getArgs();
StringBuilder log = new StringBuilder("入参为");
for (Object arg : args) {
log.append(arg + " ");
}
logger.warn(log.toString());
}
@AfterReturning(value = "execution(* *.*(..)))", returning = "returnVal")
public void afterReturin(Object returnVal) {
logger.warn("方法正常结束了,方法的返回值:" + returnVal);
}
@AfterThrowing(value = "StationCardServiceAspect.myPointcut()", throwing = "e")
public void afterThrowing(Throwable e) {
if (e instanceof StationErrorCodeException) {
logger.error("通知中发现异常StationErrorCodeException", e);
} else {
logger.error("通知中发现未知异常", e);
}
}
@Around(value = "StationCardServiceAspect.myPointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
logger.warn("前置增强...");
Object result = null;
try {
result = proceedingJoinPoint.proceed();
} catch (Exception e) {
ResultDTO resultDTO = new ResultDTO();
if (e instanceof StationErrorCodeException) {
StationErrorCodeException errorCodeException = (StationErrorCodeException) e;
resultDTO.setSuccess(false);
resultDTO.setErrorCode(errorCodeException.getErrorCode().getErrorCode());
resultDTO.setErrorMessage(errorCodeException.getErrorCode().getErrorMessage());
} else {
resultDTO.setSuccess(false);
resultDTO.setErrorCode(StationErrorCodeConstants.STA10001.getErrorCode());
resultDTO.setErrorMessage(StationErrorCodeConstants.STA10001.getErrorMessage());
}
return resultDTO;
}
return result;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
然后我们在spring配置文件中配置切面
<!-- 配置切面的类 -->
<bean id="serviceAspect" class="com.lirui.StationCardServiceAspect"/>
<!-- 配置成注解方式寻找要被代理的对象 -->
<aop:aspectj-autoproxy/>
- 1
- 2
- 3
- 4
这样,我们就可以统一处理异常和日志了。
不足点
利用这种方式,只能打入参和出参,还有抛出异常时打异常日志,不能打方法运行中的中间值,目前我只能想到,方法中间值的日志,就是用原来的方式打出,不知道大家有没有什么好的方法。
spring aop的其他使用
推荐使用aspectJ来完成面向切面编程。我们还可以利用aop完成其他功能如记录程序运行时间等。