我希望测量由MyBatis (Spring )和绑定到其他请求参数运行的sql执行时间,这样我就可以获得有关特定请求的性能问题的完整信息。在这种情况下,我通过以下方式使用了MyBatis拦截器:
@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阻断器及其方法,我认为并非如此):
@Autowired
private HttpServletRequest request;那么,问题是如何在MyBatis拦截器中正确地获取请求呢?
发布于 2018-07-16 14:58:11
在回答您的问题之前,请注意一点:访问DAO层中的UI层是一种糟糕的做法。这会在错误的方向上造成依赖。应用程序的外部层可以访问内部层,但在这种情况下,情况正好相反。相反,您需要创建一个不属于任何层的类,并且应用程序的所有层都将(或至少可能)使用它。它可以命名为MetricsHolder。拦截器可以将值存储到它,并且在计划获取度量的其他地方,您可以从它读取数据(如果它在UI层中并在那里可用,则直接使用或存储到请求中)。
但现在回到你的问题上。即使您创建了类似于MetricsHolder的东西,您仍然将面临这样的问题:您不能将它注入到mybative拦截器中。
您不能只向拦截器添加一个带有Autowired注释的字段,并期望设置它。这样做的原因是拦截器是由马提斯而不是春天实例化的。因此spring没有机会将依赖项注入到拦截器中。
处理此问题的一种方法是将拦截的处理委托给spring,该bean将成为spring上下文的一部分,并可能访问那里的其他bean。这里的问题是如何使该bean在拦截器中可用。
这可以通过在线程局部变量中存储对此类bean的引用来实现。下面是如何做到这一点的例子。首先,创建一个将存储spring的注册表。
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();
}
}这里的查询拦截器如下所示:
public interface QueryInterceptor {
Object interceptQuery(Invocation invocation) throws InvocationTargetException, IllegalAccessException;
}然后,您可以创建一个拦截器,将处理委托给spring:
@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,包括请求,正如我前面所写的那样,它是不需要的):
@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拦截器中执行此操作)。我的外表是这样的:
@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();
}
}
}https://stackoverflow.com/questions/51319476
复制相似问题