利用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);        
    }
}