Spring AOP实现线程安全的日志输出功能

在我们的工作中很多的时候都需要对接口的输入和输出进行监管,这样可以方便我们对接口的调试,也方面我们项目后期的维护。
那么我们怎么使用spring进行接口的输入和输出?
在之前我们可以直接用spring的拦截器来实现
问题:
1. 拦截器可以轻松抓取接口的参数,不能抓取接口的结果
2. 拦截器需要进行配置,不能实现灵活多变的使用
为什么使用aop:
1.aop的底层是使用的代理模式,我们不经可以得到接口的参数还能得到接口的结果
2.aop的拦截比较灵活,更方便我们对日志的管理
那么我们aop是如何实现的呢?
1.首先我们需要定义一个接口 该接口主要定义一个输入拦截方法和一个输出拦截放
如:

 package com.oa.common.proxy;

import java.lang.reflect.Method;

/**
 * @name 日志输出类,保证线程安全
 * @author Yang
 * @date 2018-08-07
 * @version 1.0.2
 */
public interface LoggerInterceptorHandler {

    /**
     * 日志输入的实现类
     */
    static String LOGGER_ADDRESS="interceptor.address";

    /**
     *  日志拦截前的处理
     *
     * @param object
     * 当前的对象
     *
     * @param method
     * 当前的方法
     *
     * @param args
     * 参数
     *
     * @throws Exception
     */
    void interptBefore(Object object, Method method,Object args) throws Exception;

    /**
     * 日志拦截后的方法
     *
     * @param object
     * 当前对象
     *
     * @param method
     * 当前的方法
     *
     * @param args
     * 参数
     *
     * @param result
     * 返回的结果:可以为空
     *
     */
    void interceptPost(Object object,Method method,Object args,Object result);

}

2.定义一个拦截器的注解
代码如:

package com.oa.common.annotion;

import java.lang.annotation.*;

/**
* @name 最新线程安全的拦截器
* @author Yang
* @date 2018-08-07
* @version 1.0.2
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoggerInterceptor {
}

**3.调用spring Aop的相关方法** 实现方法拦截的功能
***重点**代码如:
package com.oa.common.proxy;

import com.oa.common.annotion.LoggerInterceptor;
import com.oa.common.config.ConfigProperties;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @name  实现日志输出的代理类
 * @author Yang
 *
 */
@Aspect
@Component
public class CglibInterceptorProxy implements MethodInterceptor {


    private AtomicReference<LoggerInterceptorHandler> handlerAtomicReference= new AtomicReference<>();

    @Pointcut("@annotation(org.springframework.web.bind.annotation.ResponseBody)")
    public void loginauto(){

    }

    @Around("loginauto()")
    public Object around(ProceedingJoinPoint point){
        Object result= null;
        //获取访问的名称
        String methodName= point.getSignature().getName();
        Class[] methods= ((MethodSignature)point.getSignature()).getParameterTypes();
        Object target= point.getTarget();
        Class<?> clazz= target.getClass();
        try {
            Method method=clazz.getMethod(methodName,methods);
            //判断方法是否使用 LoggerInterceptor 注解
            if(!isContainer(method)){
                return point.proceed();
            }
            LoggerInterceptorHandler interceptorHandler= this.handlerAtomicReference.get();
            //判断是否加载了日志输出类
            if (interceptorHandler==null){
                //初始化日志输出类
                String path=ConfigProperties.getValue(LoggerInterceptorHandler.LOGGER_ADDRESS);
                interceptorHandler= new ClassRefletInterceptHandlerFactory(path).instance();
                this.handlerAtomicReference.set(interceptorHandler);
            }
            RequestAttributes requestAttribute= RequestContextHolder.currentRequestAttributes();
            HttpServletRequest request= ((ServletRequestAttributes)requestAttribute).getRequest();
            Set<Object> allParams = new LinkedHashSet<>();
            //获取query string 或 posted form data参数
            Map<String, String[]> paramMap = request.getParameterMap();
            Map<String,String> map= new HashMap<>();
            for(Map.Entry<String,String[]> entry :paramMap.entrySet()){
                map.put(entry.getKey(),entry.getValue()[0]);
            }
            if(map.size() > 0){
                allParams.add(map);
            }
            //输出开始日志
            interceptorHandler.interptBefore(target,method,allParams);
            //执行方法
            result= point.proceed();
            //输出结果日志
            interceptorHandler.interceptPost(target,method,allParams,result);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return result;
    }

    /**
     * 判断是否有日志拦截器
     *
     * @param method
     * 当前方法
     * @return
     */
    private boolean isContainer(Method method){
        LoggerInterceptor interceptor= method.getAnnotation(LoggerInterceptor.class);
        return interceptor!=null;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        return null;
    }
}

4.最后一步写输出日志的实现类

package com.villager.proxy;

import com.alibaba.fastjson.JSONObject;
import com.oa.common.proxy.LoggerInterceptorHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;

public class InterceptorHandler implements LoggerInterceptorHandler {

    private static final Logger logger= LoggerFactory.getLogger(InterceptorHandler.class);


    private String wrapJson(Object obj, int maxLength) {
        if (obj == null) {
            return "";
        }
        String json=JSONObject.toJSONString(obj);
        if (json == null) {
            return "";
        }
        if (json.length() > maxLength) {
            json = json.substring(0, maxLength - 1);
        }
        return json;
    }


    @Override
    public void interptBefore(Object object, Method method, Object args) throws Exception {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n当前对象类class={" + object.getClass() + "},调用方法method={" + method.getName() + "}");
        stringBuilder.append("-->{");
        stringBuilder.append("参数:"+this.wrapJson(args,400));
        stringBuilder.append("\n}");
        logger.warn(stringBuilder.toString());
        stringBuilder.setLength(0);
    }

    @Override
    public void interceptPost(Object object, Method method, Object args, Object result) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n当前对象类class={" + object.getClass() + "},调用方法method={" + method.getName() + "}");
        stringBuilder.append("-->{");
        stringBuilder.append("\n    结果:" + this.wrapJson(result, 400));
        stringBuilder.append("\n}");
        logger.warn(stringBuilder.toString());
        stringBuilder.setLength(0);
    }
}

备注:
1.最后一步可以根据自己的需求进行调整。
2.该方法支持log4j 和 logback 等常用的日志开源工具
3.在配置文件里面需配置日志输出实现类的地址

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值