mybaitis拦截器(3)— 打印调用者service的信息(获取到调用者的信息) 1. 数据准备 2. 代码实现

需求:我们在使用mybatis的拦截器做一些工作时,有时候需要打印出调用者的信息以便能够快速处理。

1. 数据准备

SpringBoot整合Mybatis+druid快速开发

插件详情:

@Slf4j
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
                RowBounds.class, ResultHandler.class})})
public class MybatisLogInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        log.info("========{},{}", MoreReflection.getCallerPlace().getClassName(),
                MoreReflection.getCallerPlace().getMethodName());
        //直接过去
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

2. 代码实现

想输出如图所示的调用者:

原理:利用异常的堆栈信息+过滤路径的前缀方式,最终输出符合我们要求的第一个类信息。

@Slf4j
public class MoreReflection {

    private static final StackTraceProvider STACK_TRACE_PROVIDER=new StackTraceProviderJdk8();


    /**
     * 获取当前方法的调用者,通过获取当前的调用栈向上查找获得
     *
     * @return StackTraceElement
     */
    @Nullable
    public static StackTraceElement getCallerPlace() {
        return getCallerPlace(MoreReflection.class);
    }

    /**
     * 获取当前方法的调用者,从指定的类位置向上查找
     *
     * @param locationAwareClass 指定类的类型
     * @return StackTraceElement
     */
    @Nullable
    public static StackTraceElement getCallerPlace(Class<?> locationAwareClass) {
        String name = locationAwareClass.getName();
        return STACK_TRACE_PROVIDER.getCallerPlace(name::equals, Predicates.alwaysFalse());
    }

}

接口类:

public interface StackTraceProvider {


    @Nullable
    default StackTraceElement getCallerPlace(Class<?> locationAwareClassChecker) {
        String name = locationAwareClassChecker.getName();
        return getCallerPlace(name::equals, Predicates.alwaysFalse());
    }

    /**
     * 返回 {@param locationAwareClassChecker} 最接近调用方方向第一个元素
     */
    @Nullable
    StackTraceElement getCallerPlace(Predicate<String> locationAwareClassChecker, Predicate<String> ignore);
}

实现类:

public class StackTraceProviderJdk8 implements StackTraceProvider {

    //反射相关的类
    static final String[] REFLECTION_PREFIXES = {"sun.reflect.", "java.lang.reflect.", "jdk.internal.reflect."};
    //mybatis相关的类
    static final String[] ibatis_PREFIXES = {"org.apache.ibatis.", "org.mybatis.", "com.tellme.Intercept."};
    //代理相关的类
    static final String[] proxy_PREFIXES = {"com.sun.proxy."};


    @Nullable
    @Override
    public StackTraceElement getCallerPlace(Predicate<String> locationAwareClassChecker, Predicate<String> ignore) {
        boolean afterSelf = false;
        boolean afterDeprecated = false;
        String deprecatedClass = null;
        // 之所以用异常获取而不是Thread.currentThread().getStackTrace(),是因为它内部实现其实也是判断当前线程了
        StackTraceElement[] stackTrace = (new Exception()).getStackTrace();
        for (StackTraceElement stack : stackTrace) {
            String stackClassName = stack.getClassName();
            if (isReflection(stackClassName)) {
                continue;
            }
            if (isIbatis(stackClassName)) {
                continue;
            }
            if (isProxy(stackClassName)) {
                continue;
            }
            if (locationAwareClassChecker.test(stackClassName)) {
                afterSelf = true;
                continue;
            }
            if (afterSelf) {
                if (deprecatedClass == null) {
                    deprecatedClass = stackClassName;
                }
            }
            if (stackClassName.equals(deprecatedClass)) {
                afterDeprecated = true;
            }
            //此处跳出循环
            if (afterDeprecated) {
                if (ignore.test(stackClassName)) {
                    continue;
                }
                //打印出堆栈信息
                return stack;
            }
        }
        return null;
    }

    private boolean isReflection(String stackClassName) {
        return StringUtils.startsWithAny(stackClassName, REFLECTION_PREFIXES);
    }

    private boolean isIbatis(String stackClassName) {
        return StringUtils.startsWithAny(stackClassName, ibatis_PREFIXES);
    }

    private boolean isProxy(String stackClassName) {
        return StringUtils.startsWithAny(stackClassName, proxy_PREFIXES);
    }

}

输出结果:

========com.tellme.service.impl.UserServiceImpl,findById
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章