利用Spring AOP实现业务和异常日志记录
AOP是面向切面编程,利用这个技术可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分的耦合性降低,提高代码的可重用性,同时提高开发效率(来自百度百科)。
实际上这个确实非常好用。最近碰到一个问题,就是发现以前action中的日志记录的不够完善,需要在所有action中的每个接口改下调用日志的方法,这种工作量太大而且毫无意义,因此就想到用AOP。(当然也可以用拦截器)
通过AOP把所有action中的接口作为切点,设置对应的切面和方法,让接口返回后进行返回通知,在这个通知方法中进行日志记录,既减少了代码量,也利于修改。
Spring AOP有两种实现方式,一种是在spring-mvc中进行配置,一种是通过注解的方式实现。
spring-mvc配置
<!-- 启用AOP -->
<aop:aspectj-autoproxy />
<!-- aop日志记录方法 -->
<bean id="busiLogAopAction" class="com.edf.optrace.core.LogAopAction"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切点表达式 -->
<aop:pointcut id="busiLogPointcut" expression="execution(* com.edf.*.controller.*.*(..)) || execution(* com.abc.*.controller.*.*(..))" />
<!-- 配置切面及配置 -->
<aop:aspect order="3" ref="busiLogAopAction">
<!-- 前置通知
<aop:before method="beforMethod" pointcut-ref="busiLogPointcut"/> -->
<!-- 后置通知
<aop:after method="afterMethod" pointcut-ref="busiLogPointcut"/> -->
<!-- 返回通知 -->
<aop:after-returning method="afterReturnMethod" pointcut-ref="busiLogPointcut" returning="result"/>
<!-- 返回异常
<aop:after-throwing method="afterThrowingMethod" pointcut-ref="pointcut" throwing="ex"/> -->
</aop:aspect>
</aop:config>
public class LogAopAction {
@Autowired
private OpHistoryService opHistoryService;
/**
* 返回通知(在方法正常结束执行的代码)
* 返回通知可以访问到方法的返回值!
* @param joinPoit
*/
public void afterReturnMethod(JoinPoint joinPoint, Object result){
//获取方法入参list
List<Object> args = Arrays.asList(joinPoint.getArgs());
String methodName = joinPoint.getSignature().getName();//获取方法名
String className = joinPoint.getTarget().getClass().getName();//获取所在实体类
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
String params = "";
if(args!=null && args.size()>0){
//1、处理request中入参,但获取不到注解的bean
Enumeration em = request.getParameterNames();
while (em.hasMoreElements()) {
String name = (String) em.nextElement();
String value = request.getParameter(name);
params += name +"=" + value +",";
}
params += ";";
//2、处理注解的bean
for ( int i = 0; i < args.size(); i++) {
try {
params += JSONObject.fromObject(args.get(i)) + ";";
} catch (Exception e) {
e.printStackTrace();
}
}
}
String costTime = (String) request.getSession().getAttribute("costTime");
if(!"".equals(StringUtil.parseString(className))){
//插入业务日志
opHistoryService.setOperateHistory(request, className.substring(className.lastIndexOf(".")+1), methodName, params, result.toString(), costTime);
request.getSession().removeAttribute("costTime");
}
}
}
注解方式
/**
* 设置异常日志AOP
*/
@Aspect
@Order(2)
@Component
public class ExLogAopAction {
@Autowired
private OpHistoryService opHistoryService;
/**
* 定义一个方法,用于声明切入点表达式,方法中一般不需要添加其他代码
* 使用@Pointcut声明切入点表达式
* 后面的通知直接使用方法名来引用当前的切点表达式
*/
@Pointcut("execution(* com.edf..*.*(..)) || execution(* com.abc..*.*(..))")
public void declearJoinPointExpression(){}
/**
* 异常通知(方法发生异常执行的代码)
* 可以访问到异常对象;且可以指定在出现特定异常时执行的代码
* @param joinPoint
* @param ex
*/
@AfterThrowing(value="declearJoinPointExpression()", throwing="ex")
public void afterThrowingMethod(JoinPoint joinPoint, Exception ex){
String methodName = joinPoint.getSignature().getName();
String entity = joinPoint.getTarget().getClass().getName();
OpHistory opEx = new OpHistory();
opEx.setDictionaryId("new");
opEx.setOperateType("1");//1 通用异常;后期可加上
opEx.setOperateName("Exception");
opEx.setExt6(methodName);//方法名
opEx.setExt5(entity);//所在模块
opEx.setUpdateContent(StringUtil.getStringFixedLen(ex.toString(),3800));//异常信息内容
//插入异常到DB
opHistoryService.insertExErrorHistory(opEx);
}
}