Spring注解实现日志记录

来源:互联网 发布:男性捏脸数据 编辑:程序博客网 时间:2024/06/05 20:54

之前总结写了一篇通过XML配置的方式,切面编程实现日志记录的功能demo

http://blog.csdn.net/weiweiai123456/article/details/38561085

可参考http://blog.csdn.net/heirenheiren/article/details/36634497 ,讲的是注解实现

现在实现一个通过注解方式实现的样例:

一:准备

xml中需要开启CGLIB动态代理

<!-- 启用CGliB --><aop:aspectj-autoproxy proxy-target-class="true"/>
切面编程----AOP,依赖的是代理,即JDK代理和CGLIB代理,而代理的实现依靠的是反射。

maven配置省...


二:注解类

SaveSysLog.java

package com.cooya.partner.metadata.entity.baseConfig;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** *  * Description: 保存系统日志注解接口 * * @author suoww * @date 2017-2-8 * */@Retention(RetentionPolicy.RUNTIME)  //注解会在class中存在,运行时可通过反射获取@Target(ElementType.METHOD) //注解到方法public @interface SaveSysLog {    //调用方      1:嗨赚客户端   2:支付宝   3:微信       4:钱宝     5:其他第三方    int send() default 1;         //接口url(从二级目录记起)    String url();        //接口类型(前台,后台)    int type();}
定义三个成员,这里只能定义八种基本数据类型,分别是


byte-->Byte

short-->Short

int-->Integer

long-->Long

float-->Float

double-->Double

char-->Character

boolean-->Boolean

注意:只能是上述这些8种类型的变量


三:切面类

SysLogAspect.java

package com.cooya.partner.service.baseConfig;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Date;import java.util.HashMap;import java.util.Map;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import org.apache.shiro.SecurityUtils;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.scheduling.annotation.Async;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import com.alibaba.fastjson.JSONObject;import com.cooya.partner.constant.InterfaceTypeConst;import com.cooya.partner.metadata.entity.baseConfig.PartnerSystemLog;import com.cooya.partner.metadata.entity.baseConfig.SaveSysLog;import com.cooya.partner.metadata.entity.user.PartnerUser;import com.cooya.partner.metadata.mapper.baseConfig.PartnerSystemLogMapper;import com.cooya.partner.permission.dto.ShiroUser;/** *  * Description: 切面类记录接口调用失败日志信息 * * @author suoww * @date 2017-2-8 * */@Aspect@Componentpublic class SysLogAspect {        public static final int CODE_SUCCESS = 0;        private Logger logger = LoggerFactory.getLogger(SysLogAspect.class);        @Resource    private PartnerSystemLogMapper partnerSystemLogMapper;        /**     *      * Description: 定义切点名controllerAspect,此方法需要为空,只是标识切点和切面关系     *     * @author suoww     * @date 2017-2-8     */    @Pointcut("@annotation(com.cooya.partner.metadata.entity.baseConfig.SaveSysLog)")    public void controllerAspect(){}        /**     *      * Description:织入后增强      *     * @param join     * @author suoww     * @throws Exception      * @date 2017-2-8     */    @AfterReturning(pointcut = "controllerAspect()", returning = "res")    public void doAfter(JoinPoint joinPoint, Object res) throws Exception{        //获取反射参数        logger.debug("---------------AfterReturning开始--------------");        if(null == res){            return;        }        Map<String, Object> map = Obj2Map(res);        int code = (Integer)map.get("code");        if(code == CODE_SUCCESS){            return;        }        String message = (String)map.get("message");        //类名        String targetName = joinPoint.getTarget().getClass().getSimpleName();        //得到方法名        String methodName = joinPoint.getSignature().getName();        MethodSignature ms = (MethodSignature) joinPoint.getSignature();        //入参key        String[] parameterNames = ms.getParameterNames();        //入参value        Object[] arguments = joinPoint.getArgs();        Method method = ms.getMethod();        //方法的注解对象        SaveSysLog logParam = method.getAnnotation(SaveSysLog.class);          /* logger.debug("SaveSysLog注解参数send:" + logParam.send());          logger.debug("SaveSysLog注解参数url:" + logParam.url());         logger.debug("SaveSysLog注解参数type:" + logParam.type());         logger.debug("targetName:" + targetName);        logger.debug("methodName:" + methodName);        logger.debug("ms:" + ms);        logger.debug("arguments:" + JSONObject.toJSONString(arguments));        logger.debug("parameterNames:" + JSONObject.toJSONString(parameterNames));        logger.debug("method:" + JSONObject.toJSONString(method));*/                //拼参数        PartnerSystemLog sysLog = new PartnerSystemLog();         //获取用户        if(logParam.type() == InterfaceTypeConst.InterfaceType.APP){            sysLog.setUserId(getAppUserId());        }else{            sysLog.setUserId(getMgrUserId());        }        sysLog.setSend(logParam.send());        sysLog.setUrl(logParam.url());        sysLog.setType(logParam.type());        //入参字符串        StringBuffer jsonParamSb = new StringBuffer();        for(int i = 0;i < parameterNames.length;i++){            jsonParamSb.append(parameterNames[i]).append("=").append(JSONObject.toJSONString(arguments[i]));            if(i != (parameterNames.length - 1)){                jsonParamSb.append("&");            }        }        //截取返回json        if(jsonParamSb.toString().length() <= 1000){            sysLog.setJsonParam(jsonParamSb.toString());        }else{            sysLog.setJsonParam(jsonParamSb.toString().substring(0, 1000));        }        //出参        sysLog.setJsonResult(JSONObject.toJSONString(res));        StringBuffer remarkSb = new StringBuffer();        remarkSb.append(targetName).append(".").append(methodName).append("报错信息:").append(message);        //截取remark        if(remarkSb.toString().length() <= 1000){            sysLog.setRemark(remarkSb.toString());        }else{            sysLog.setRemark(remarkSb.toString().substring(0, 1000));         }        sysLog.setCreateTime(new Date());        sysLog.setUpdateTime(new Date());        handleLog(sysLog);        logger.debug("---------------AfterReturning结束--------------");    }        /**     *      * Description: 异步记录接口调用失败的日志     *     * @param systemLog     * @author suoww     * @date 2017-2-8     */    @Async    public void handleLog(PartnerSystemLog sysLog){        //写日志        int row = partnerSystemLogMapper.insertSelective(sysLog);        logger.debug("------日志写入行数:" + row);    }         /**     *      * Description: 对象转map     *     * @param obj     * @return     * @throws Exception     * @author suoww     * @date 2017-2-8     */    public Map<String,Object> Obj2Map(Object obj) throws Exception{        Map<String,Object> map=new HashMap<String, Object>();        Field[] fields = obj.getClass().getDeclaredFields();        for(Field field:fields){            field.setAccessible(true);            map.put(field.getName(), field.get(obj));        }        return map;    }        /**     *      * Description: 获取APP用户ID     *     * @return     * @author suoww     * @date 2017-2-8     */    protected Long getAppUserId() {        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();        HttpSession session = request.getSession();        if (null == session) {            return null;        }        PartnerUser user = (PartnerUser) session.getAttribute("userInfo");        if (null == user) {            return null;        }        return user.getId();    }        /**     *      * Description: 获取Mgr的用户ID     *     * @return     * @author suoww     * @date 2017-2-8     */    protected Long getMgrUserId(){        ShiroUser user = (ShiroUser) SecurityUtils.getSubject().getPrincipal();        return user.getId();    }}


@Aspect和@Component分别表示这是一个切面类、Spring要帮我实例化对象并管理



@Pointcut(XX) :使用SaveSysLog作为注解(annotation)的将作为切点,对应切面controllerAspect



1.@AfterReturning 表示切点后增强,即切入点的方法执行结束后,即执行切面中的增强代码,但是不会改变原切入点方法返回值。下面具体说明

2.pointcut="controllerAspect()" ,returning="res" 表示切点和切面对应关系,一个方法上可以有多个切面,指定顺序

参考:http://blog.csdn.net/rainbow702/article/details/52185827

3.下面的是反射获取的参数,类名,方法名,入参key,参数value,注解对象,方法返回值


四:调用

controller中调用

 /**     *      * Description: 接口:查询场次下商品     *     * @param channelId     * @return     * @author suoww     * @date 2017-1-13     */    @RequestMapping("/queryGoodsUnderChannel")    @ResponseBody    @SaveSysLog(send=InterfaceTypeConst.SendType.HZ, url="/goods/api/queryGoodsUnderChannel.html", type=InterfaceTypeConst.InterfaceType.APP)    public AjaxResult queryGoodsUnderChannel(@RequestParam(value = "channelId", required = true)Long channelId){        try{            List<PartnerChannelGoodsDto> list = partnerGoods2ChannelService.getChannelGoodsDto(channelId);            logger.info("根据channelId:" + channelId + "获取到的商品集合为" + JSONObject.toJSONString(list));            return AjaxResult.success(list, "成功获取频道下商品");        }catch(ResultCodeException e){            e.printStackTrace();            return AjaxResult.failed("校验失败:" + e.getMessage());        }catch(Exception e){            e.printStackTrace();            return AjaxResult.failed("系统异常:" + e.getMessage());        }    }

@SaveSysLog(send=InterfaceTypeConst.SendType.HZ, url="/goods/api/queryGoodsUnderChannel.html", type=InterfaceTypeConst.InterfaceType.APP)

这里对应注解接口三个成员,send,url,type

queryGoodsUnderChannel 这个方法将整体作为一个切入点,结合@AfterReturning 在queryGoodsUnderChannel ()执行结束会,会进入到SysLogAspect.doAfter 执行一段代码,记录日志


五:测试

输入http://localhost:8080/partner-app/goods/api/queryGoodsUnderChannel.html?channelId=17


  入参中channelId=17


参数可以对应。


总结:权限控制,日志记录应该使用AOP,注解方式实际使用时候比XML配置方式要省事很多。











0 0
原创粉丝点击