基于aop思想实现日志打印

基于aop思想实现日志打印

基于切点表达式实现:

  1. execution详解
    execution的语法表达式如下:execution(<修饰符> <返回类型> <类路径> <方法名>(<参数列表>) <异常模式> )
    其中,修饰符和异常是可选的,如果不加类路径,则默认对所有的类生效。它常用实例如下:
  2. 通过方法签名、返回值定义切点:
  • execution(public * *Service(..)):定位于所有类下返回值任意、方法入参类型、数量任意,public类型的方法
  • execution(public String *Service(..)):定位于所有类下返回值为String、方法入参类型、数量任意,public类型的方法
  1. 通过类包定义切点:
  • execution(* com.yc.controller.BaseController+.*(..)):匹配任意返回类型,对应包下BaseController类及其子类等任意方法。
  • execution(* com.*.(..)):匹配任意返回类型,com包下所有类的所有方法
  • execution(* com..*.(..)):匹配任意返回类型,com包、子包下所有类的所有方法
    注意.表示该包下所有类,…则涵括其子包。
  1. 通过方法入参定义切点
  • 这里“*”表示任意类型的一个参数,“…”表示任意类型任意数量的参数
  • execution(* speak(Integer,*)):匹配任意返回类型,所有类中只有两个入参,第一个入参为Integer,第二个入参任意的方法
  • execution(* speak(..,Integer,..)):匹配任意返回类型,所有类中至少有一个Integer入参,但位置任意的方法。
  1. 常用切点表达式
  • execution(* com.yc.service..*.*(..))在配置service层的事务管理时常用,定位于任意返回类型(第一个”*”) 在com.yc.service包以及(“..”)子包下的(第一个“*”)所有类(第二个”*”)下的所有方法(第三个”*”),且这个方法的入参为任意类型、数量(体现在 “(..)“)
/**
 * Created by yangmin on 2020/10/26
 */
@Aspect
@Component
public class LoggerAop {
    private static Logger log = LoggerFactory.getLogger(LoggerAop.class);

    /**
     * 此处的切点是注解的方式
     * 只要出现 @LogAnnotation注解都会进入
     */
     

	/**
     * 此处的切点使用的是切点表达式
     * execution(* com.yc.service.*.*(..))在配置service层的事务管理时常用,定位于任意返回类型(第一个”*”) 在com.yc.service包下的所有类(第二个”*”)下的所有方法(第三个”*”),且这个方法的入参为任意类型、数量(体现在 “(..)“)
     * 
     */
    @Pointcut("execution(public * com.study.studyaop.service..*.*(..))")//切入点描述,这个是service包的切入点
    public void logPointCut() {
    }

    /**
     * 环绕增强,相当于MethodInterceptor
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("==============================================start==================================================");
        Object result=null;
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取类名
        String className = joinPoint.getTarget().getClass().getName();
        //获取方法名
        String methodName = signature.getName();
        long beginTime = System.currentTimeMillis();
        //获取方法参数
        Object[] args = joinPoint.getArgs();
        //在参数中去除Request或者Response对象,(joinPoint.getArgs()返回的数组中携带有Request或者Response对象,导致序列化异常。)
        Stream<?> stream = ArrayUtils.isEmpty(args) ? Stream.empty() : Arrays.stream(args);
        List<Object> logArgs = stream
                .filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)))
                .collect(Collectors.toList());
        String params = null;
        if (args.length != 0) {
            params = JSON.toJSONString(logArgs);
        }
        HttpServletRequest request = HttpContextUtil.getRequest();
        //获取请求方法的类型:get 或则post
        String method = request.getMethod();
        //获取请求路劲
        String url = request.getRequestURL().toString();
        //获取ip
        String ip = IpUtils.getIP(request);
        log.info("请求的路径为     : {}",url);
        log.info("请求ip           : {}",ip);
        log.info("方法请求类名     : {}",className);
        log.info("方法名           : {}",methodName);
        log.info("方法类型         : {}",method);
        log.info("请求参数         : {}",params);
        //执行方法
        try {
             result = joinPoint.proceed();
        } catch (Exception e) {
            log.error("异常信息         : {}",e.getMessage());
            e.printStackTrace();
        }finally {
            //执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
            log.info("执行时间         : {}",time);
            log.info("==============================================end==================================================");
            return result;
        }
    }

/**
获取request的方法
*/
 public HttpServletRequest getRequest() {
        return  ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
    }

/**
     * 获取IP地址的方法
     * @return
     */
    public  String getIpAddress() {
        HttpServletRequest request = getRequest();
        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.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}

基于注解实现
annotation 此注解用于定位标注了某个注解的目标切点。下面我们来看一个模拟用户登录成功后日志记录的示例

1.自定义注解

/**
 * 日志注解
 *
 * @author lastwhisper
 */
@Target(ElementType.METHOD) // 方法注解
@Retention(RetentionPolicy.RUNTIME) // 运行时可见
public @interface LogAnno {
    String operateType();// 记录日志的操作类型
    int type();
}
  1. 目标方法
    /**
     *登录方法
     * @param request
     * @return
     * @throws Exception
     */
    @LogAnno(operateType = "登录",type=10)
    @RequestMapping(value = "/tologin", method = RequestMethod.POST)
    public String login(@RequestBody Map<String,String> map , HttpServletRequest request, HttpServletResponse response) throws Exception {
        String username = map.get("username");
        String password = map.get("password");
        String code = map.get("code");
        LoginResult loginResult = loginService.login(code,username, password,request,response);
        String s = JsonUtils.toString(loginResult);
        return s;
    }

3.增强

@Order(3)//优先级
@Component
@Aspect
public class LogAopAspect {

    @Resource
    private PermissApiClient permissApiClient;

    @Around("@annotation(com.woyaoce.core.annotation.LogAnno)")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        // 1.方法执行前的处理,相当于前置通知
        // 获取方法签名
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        // 获取方法
        Method method = methodSignature.getMethod();
        // 获取方法上面的注解
        LogAnno logAnno = method.getAnnotation(LogAnno.class);
        // 获取操作描述的属性值
        String operateType = logAnno.operateType();
        int type = logAnno.type();
        // 创建一个日志对象(准备记录日志)
        CmsLog cmsLog = new CmsLog();
        cmsLog.setType(type);
        String ip = HttpContextUtil.getIpAddress();
        cmsLog.setIp(ip);
        Object result = null;
        try {
            // 让代理方法执行
            result = pjp.proceed();
            // 2.相当于后置通知(方法成功执行之后走这里)
            cmsLog.setStatus(0);
            cmsLog.setLogMsg(operateType);// 设置操作结果
        } catch (Exception e) {
            // 3.相当于异常通知部分
            cmsLog.setStatus(1);
            cmsLog.setLogMsg(operateType);// 设置操作结果
        } finally {
            // 设置操作人,从session中获取,
            Subject LoginUser = SecurityUtils.getSubject();
            Session session = LoginUser.getSession();
            String userName =(String) session.getAttribute("userName");
            cmsLog.setLoginName(userName);
            // 4.相当于最终通知
            //cmsLog.setCreateDate(new Date());// 设置操作日期
           // 添加日志记录
            permissApiClient.addLog(cmsLog);
        }
        return result;
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值