Spring注解实现日志记录

之前总结写了一篇通过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
@Component
public 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配置方式要省事很多。











  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOP是一个强大的框架,可以帮助我们实现各种切面,其中包括日志记录。下面是实现日志记录的步骤: 1. 添加Spring AOP依赖 在Maven或Gradle中添加Spring AOP依赖。 2. 创建日志切面 创建一个用于记录日志的切面。这个切面可以拦截所有需要记录日志的方法。在这个切面中,我们需要使用@Aspect注解来声明这是一个切面,并使用@Pointcut注解来定义哪些方法需要被拦截。 ```java @Aspect @Component public class LoggingAspect { @Pointcut("execution(* com.example.demo.service.*.*(..))") public void serviceMethods() {} @Around("serviceMethods()") public Object logServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable { // 获取方法名,参数列表等信息 String methodName = joinPoint.getSignature().getName(); Object[] args = joinPoint.getArgs(); // 记录日志 System.out.println("Method " + methodName + " is called with args " + Arrays.toString(args)); // 执行方法 Object result = joinPoint.proceed(); // 记录返回值 System.out.println("Method " + methodName + " returns " + result); return result; } } ``` 在上面的代码中,我们使用了@Around注解来定义一个环绕通知,它会在拦截的方法执行前后执行。在方法执行前,我们记录了该方法的名称和参数列表,然后在方法执行后记录了该方法的返回值。 3. 配置AOPSpring的配置文件中配置AOP。首先,我们需要启用AOP: ```xml <aop:aspectj-autoproxy/> ``` 然后,我们需要将创建的日志切面添加到AOP中: ```xml <bean id="loggingAspect" class="com.example.demo.aspect.LoggingAspect"/> <aop:config> <aop:aspect ref="loggingAspect"> <aop:pointcut id="serviceMethods" expression="execution(* com.example.demo.service.*.*(..))"/> <aop:around method="logServiceMethods" pointcut-ref="serviceMethods"/> </aop:aspect> </aop:config> ``` 在上面的代码中,我们将创建的日志切面声明为一个bean,并将其添加到AOP中。我们还定义了一个切入点,并将其与日志切面的方法进行关联。 4. 测试 现在,我们可以测试我们的日志记录功能了。在我们的业务逻辑中,所有匹配切入点的方法都会被拦截,并记录它们的输入和输出。我们可以在控制台中看到这些日志信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值