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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章