首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在MyBatis拦截器中获取请求

如何在MyBatis拦截器中获取请求
EN

Stack Overflow用户
提问于 2018-07-13 07:06:25
回答 1查看 2.6K关注 0票数 0

我希望测量由MyBatis (Spring )和绑定到其他请求参数运行的sql执行时间,这样我就可以获得有关特定请求的性能问题的完整信息。在这种情况下,我通过以下方式使用了MyBatis拦截器:

代码语言:javascript
复制
@Intercepts({
    @Signature(
            type = Executor.class,
            method = "query",
            args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
    @Signature(
            type = Executor.class,
            method = "query",
            args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class QueryMetricsMybatisPlugin implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Stopwatch stopwatch = Stopwatch.createStarted();
        Object result = invocation.proceed();
        stopwatch.stop();
        logExectionTime(stopwatch, (MappedStatement) invocation.getArgs()[0]);
        return result;
    }
}

现在,当涉及到与请求绑定时,我希望将这些指标存储在request 中,作为属性。我尝试过这个简单的get请求解决方案,但这不起作用,因为请求总是为null (我读过这个解决方案在异步方法中不起作用,但是对于MyBatis阻断器及其方法,我认为并非如此):

代码语言:javascript
复制
@Autowired
private HttpServletRequest request;

那么,问题是如何在MyBatis拦截器中正确地获取请求呢?

EN

回答 1

Stack Overflow用户

发布于 2018-07-16 14:58:11

在回答您的问题之前,请注意一点:访问DAO层中的UI层是一种糟糕的做法。这会在错误的方向上造成依赖。应用程序的外部层可以访问内部层,但在这种情况下,情况正好相反。相反,您需要创建一个不属于任何层的类,并且应用程序的所有层都将(或至少可能)使用它。它可以命名为MetricsHolder。拦截器可以将值存储到它,并且在计划获取度量的其他地方,您可以从它读取数据(如果它在UI层中并在那里可用,则直接使用或存储到请求中)。

但现在回到你的问题上。即使您创建了类似于MetricsHolder的东西,您仍然将面临这样的问题:您不能将它注入到mybative拦截器中。

您不能只向拦截器添加一个带有Autowired注释的字段,并期望设置它。这样做的原因是拦截器是由马提斯而不是春天实例化的。因此spring没有机会将依赖项注入到拦截器中。

处理此问题的一种方法是将拦截的处理委托给spring,该bean将成为spring上下文的一部分,并可能访问那里的其他bean。这里的问题是如何使该bean在拦截器中可用。

这可以通过在线程局部变量中存储对此类bean的引用来实现。下面是如何做到这一点的例子。首先,创建一个将存储spring的注册表。

代码语言:javascript
复制
public class QueryInterceptorRegistry {

    private static ThreadLocal<QueryInterceptor> queryInterceptor = new ThreadLocal<>();

    public static QueryInterceptor getQueryInterceptor() {
        return queryInterceptor.get();
    }

    public static void setQueryInterceptor(QueryInterceptor queryInterceptor) {
        QueryInterceptorRegistry.queryInterceptor.set(queryInterceptor);
    }

    public static void clear() {
        queryInterceptor.remove();
    }

}

这里的查询拦截器如下所示:

代码语言:javascript
复制
public interface QueryInterceptor {
    Object interceptQuery(Invocation invocation) throws InvocationTargetException, IllegalAccessException;
}

然后,您可以创建一个拦截器,将处理委托给spring:

代码语言:javascript
复制
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
                RowBounds.class, ResultHandler.class }),
        @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
                RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}) })
public class QueryInterceptorPlugin implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        QueryInterceptor interceptor = QueryInterceptorRegistry.getQueryInterceptor();
        if (interceptor == null) {
            return invocation.proceed();
        } else {
            return interceptor.interceptQuery(invocation);
        }
    }

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

    @Override
    public void setProperties(Properties properties) {
    }

}

您需要创建一个QueryInterceptor的实现,以满足您的需要,并使其成为spring (您可以在那里访问其他spring,包括请求,正如我前面所写的那样,它是不需要的):

代码语言:javascript
复制
@Component
public class MyInterceptorDelegate implements QueryInterceptor {

    @Autowired
    private SomeSpringManagedBean someBean;

    @Override
    public Object interceptQuery(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
      // do whatever you did in the mybatis interceptor here
      // but with access to spring beans
   }
}

现在唯一的问题是设置和清理注册表中的委托。

我通过应用于服务层方法的方面(但您可以手动完成,也可以在spring拦截器中执行此操作)。我的外表是这样的:

代码语言:javascript
复制
@Aspect
public class SqlSessionCacheCleanerAspect {

    @Autowired MyInterceptorDelegate myInterceptorDelegate;

    @Around("some pointcut that describes service methods")
    public Object applyInterceptorDelegate(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        QueryInterceptorRegistry.setQueryInterceptor(myInterceptorDelegate);
        try {
            return proceedingJoinPoint.proceed();
        } finally {
            QueryInterceptorRegistry.clear();
        }
    }

}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51319476

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档