springMVC自定义注解,用AOP来实现日志记录

需求背景

最近的一个项目,在项目基本完工的阶段,客户提出要将所有业务操作的日志记录到数据库中,并且要提取一些业务的关键信息(比如交易单号)体现在日志中。

为了保证工期,在查阅了资料以后,决定用AOP+自定义注解的方式来完成这个需求。

准备工作

自定义注解需要依赖的jar包有 aspectjrt-XXX.jar ,aspectjweaver-XXX.jar,XXX代表版本号。

自定义注解

在项目下单独建立了一个log包,来存放日志相关的内容

**.common.log.annotation  //自定义注解存放位置
**.common.log.aop         //aop工具类存放位置

在annotation包下面新建自定义注解类:

package **.common.log.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface XXXOperateLog {
    /**
     * 操作类型描述
     * @return
     */
    String operateTypeDesc() default "";

    /**
     * 操作类型
     * @return
     */
    long operateType() default -1;

    /**
     * 模块编码
     * @return
     */
    String moudleCode() default "M30";

    /**
     * 模块名称
     * @return
     */
    String moudleName() default "XX模块";

    /**
     * 业务类型
     * @return
     */
    String bussType() default "";
    /**
     * 业务类型描述
     * @return
     */
    String bussTypeDesc() default "";
}

在aop包下新建XXXOperateLogAop

package **.common.log.aop;

import ** ;//省略

@Aspect
@Component
public class XXXOperateLogAop{
    @Autowired
    SystemLogService systemLogService;
     HttpServletRequest request = null;
     Logger logger = LoggerFactory.getLogger(XXXOperateLogAop.class);
    ThreadLocal<Long> time = new ThreadLocal<Long>();
    //用于生成操作日志的唯一标识,用于业务流程审计日志调用
    public static ThreadLocal<String> tag = new ThreadLocal<String>();

    //声明AOP切入点,凡是使用了XXXOperateLog的方法均被拦截
    @Pointcut("@annotation(**.common.log.annotation.XXXOperateLog)")
    public void log() {
        System.out.println("我是一个切入点");
    }

    /**
     * 在所有标注@Log的地方切入
     * @param joinPoint
     */
    @Before("log()")
    public void beforeExec(JoinPoint joinPoint) {
        time.set(System.currentTimeMillis());   
        info(joinPoint);
        //设置日志记录的唯一标识号
        tag.set(UUID.randomUUID().toString());
        request=  ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

    }
    @After("log()")
    public void afterExec(JoinPoint joinPoint) {
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method method = ms.getMethod();
        logger.debug("标记为" + tag.get() + "的方法" + method.getName()
                + "运行消耗" + (System.currentTimeMillis() - time.get()) + "ms");   
    }
    //在执行目标方法的过程中,会执行这个方法,可以在这里实现日志的记录
    @Around("log()")
    public Object aroundExec(ProceedingJoinPoint pjp) throws Throwable {
        Object ret = pjp.proceed();
        try {
            Object[] orgs = pjp.getArgs();
            SystemLog valueReturn = null;
            for (int i = 0; i < orgs.length; i++) {
                if(orgs[i] instanceof SystemLog){
                    valueReturn = (SystemLog) orgs[i];
                }   
            }   
            if(valueReturn==null){
                valueReturn =  new SystemLog();
            }
            if(valueReturn!=null&&request!=null){

                MethodSignature ms = (MethodSignature) pjp.getSignature();
                Method method = ms.getMethod();
                //获取注解的操作日志信息
                XXXOperateLog log = method.getAnnotation(XXXOperateLog.class);
                String businessType = log.bussType();
                String businessDesc = log.bussTypeDesc();
                HashMap requestMap = ServletUtils.getParametersToHashMap(request) ;
                //从参数中寻找业务类型
                if(businessType.equals(""))
                {
                    Object objBusinessType = requestMap.get("business_type");
                    businessType = objBusinessType == null ? "" : objBusinessType.toString();
                }
                //从执行结果的申请单中找业务类型
                Object apply = request.getAttribute("apply") ;
                if(apply != null){
                    JSONObject obj =  JSONFactory.toJSONAbstractEntity(apply);
                    if(obj != null)
                    {
                        valueReturn.setOtherDesc("申请单号:"+obj.getString("apply_no"));
                        if(businessType.equals(""))
                        {
                            businessType = obj.getString("business_type");
                        }
                    }
                }
                //从方法的执行过程参数中找业务类型(一般是手动设置)
                if(businessType.equals(""))
                {
                    businessType = (String) request.getAttribute("business_type");
                    businessType = businessType == null ? "" : businessType;

                }

                if(!businessType.equals("") && businessDesc.equals(""))
                {
                    businessDesc = XXXSysConstant.BUSINESS_TYPE.getName(businessType);
                }

                valueReturn.setBussType(XXXSysConstant.BUSINESS_TYPE.getNumber(businessType));
                valueReturn.setBussTypeDesc(businessDesc);

                valueReturn.setMoudleCode(log.moudleCode());
                valueReturn.setMoudleName(log.moudleName());
                valueReturn.setOperateResult(XXXSysConstant.YesOrNo.YES);
                valueReturn.setOperateType(log.operateType());
                valueReturn.setInputUserId(((UserContext)WebUtils.getSessionAttribute(request, "XXXuserContext")).getSysUser().getId());
                valueReturn.setOperateTypeDesc(log.operateTypeDesc());
                valueReturn.setRequestIp(getRemoteHost(request));
                valueReturn.setRequestUrl(request.getRequestURI());
                valueReturn.setServerIp(request.getLocalAddr());

                valueReturn.setUids(tag.get());
                //保存操作日志
                systemLogService.saveSystemLog(valueReturn);
            }else{
                logger.info("不记录日志信息");
            }
            //保存操作结果    
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ret;
    }
    //记录异常日志
    @AfterThrowing(pointcut = "log()",throwing="e")
    public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        try {
            info(joinPoint);
            Object[] orgs = joinPoint.getArgs();
            SystemLog valueReturn = null;
            for (int i = 0; i < orgs.length; i++) {
                if(orgs[i] instanceof SystemLog){
                    valueReturn = (SystemLog) orgs[i];
                }           
            }
            if(valueReturn==null){
                valueReturn =  new SystemLog();
            }
            if(valueReturn!=null&&request!=null){
                MethodSignature ms = (MethodSignature) joinPoint.getSignature();
                Method method = ms.getMethod();
                XXXOperateLog log = method.getAnnotation(XXXOperateLog.class);
                String businessType = log.bussType();
                String businessDesc = log.bussTypeDesc();
                if(businessType.equals(""))
                {
                    Object objBusinessType = ServletUtils.getParametersToHashMap(request).get("business_type");
                    businessType = objBusinessType == null ? "" : objBusinessType.toString();
                    businessDesc = XXXSysConstant.BUSINESS_TYPE.getName(businessType);
                }
                valueReturn.setBussType(XXXSysConstant.BUSINESS_TYPE.getNumber(businessType));
                valueReturn.setBussTypeDesc(businessDesc);
                valueReturn.setMoudleCode(log.moudleCode());
                valueReturn.setMoudleName(log.moudleName());
                valueReturn.setOperateType(log.operateType());
                valueReturn.setOperateTypeDesc(log.operateTypeDesc());
                valueReturn.setInputUserId(((UserContext)WebUtils.getSessionAttribute(request, "XXXuserContext")).getSysUser().getId());
                valueReturn.setOperateResult(XXXSysConstant.YesOrNo.NO);
                String errMes = e.getMessage();
                if(errMes!=null && errMes.length()>800){
                    errMes = errMes.substring(0, 800);
                }
                valueReturn.setErrorMessage(errMes);
                valueReturn.setRequestIp(getRemoteHost(request));
                valueReturn.setRequestUrl(request.getRequestURI());
                valueReturn.setServerIp(request.getLocalAddr());
                valueReturn.setUids(tag.get());
                systemLogService.saveSystemLog(valueReturn);
            }else{
                logger.info("不记录日志信息");
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

    private void info(JoinPoint joinPoint) {
        logger.debug("--------------------------------------------------");
        logger.debug("King:\t" + joinPoint.getKind());
        logger.debug("Target:\t" + joinPoint.getTarget().toString());
        Object[] os = joinPoint.getArgs();
        logger.debug("Args:");
        for (int i = 0; i < os.length; i++) {
            logger.debug("\t==>参数[" + i + "]:\t" + os[i].toString());
        }
        logger.debug("Signature:\t" + joinPoint.getSignature());
        logger.debug("SourceLocation:\t" + joinPoint.getSourceLocation());
        logger.debug("StaticPart:\t" + joinPoint.getStaticPart());
        logger.debug("--------------------------------------------------");
    }
    /**
     * 获取远程客户端Ip
     * @param request
     * @return
     */
    private  String getRemoteHost(javax.servlet.http.HttpServletRequest request){
        String ip = request.getHeader("x-forwarded-for");
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ip = request.getHeader("Proxy-Client-IP");
        }
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
            ip = request.getRemoteAddr();
        }
        return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
    }   
}

修改配置文件spring-mvc.xml,添加如下配置

    <!-- 开启AOP拦截 -->
    <aop:aspectj-autoproxy proxy-target-class="true" /> 
    <mvc:annotation-driven />
    <!-- 定义Spring描述Bean的范围  -->
    <context:component-scan base-package="**.common.log" >
         <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

需要注意的是,上述配置必须放在同一个xml文件里面,要么spring-mvc.xml,要么spring-context.xml,否则可能不生效,暂时还未查明是为什么。

注解的使用

@XXXOperateLog(
            bussType=XXXSysConstant.BUSINESS_TYPE.YYYY
            ,bussTypeDesc="业务类型描述"
            ,operateType = XXXSysConstant.LogOperateType.QUERY
            ,operateTypeDesc = "操作描述"
    )
    @RequestMapping(value = "/**/**/queryXXXXX4DataGrid.json", method = RequestMethod.POST)
    public void queryXXXXX4DataGrid(HttpServletRequest request, HttpServletResponse arg1, Model model, Writer writer)
    {
        logger.info("==========验票查询(出库)交易信息 开始=====================");
        try {
            //do something for business
        } catch (SystemException se) {

            throw se;
        } catch (BusinessException be) {

            throw be;
        } catch (Exception e) {

            throw new SystemException(e);
        }

    }
展开阅读全文

没有更多推荐了,返回首页