В приведенном ниже примере из Telerik используется элемент управления ShapedForm, но измените его на обычную форму Windows. Это самый легкий и лучший способ, который я видел.
Одно важное замечание перед тем, как я отвечу на ваш вопрос: плохой практикой является доступ к слою пользовательского интерфейса на уровне DAO. Это создает зависимость в неправильном направлении. Внешние слои вашего приложения могут обращаться к внутренним слоям, но в этом случае это наоборот. Вместо этого вам нужно создать класс, который не принадлежит ни одному слою и будет (или, по крайней мере, может) использоваться всеми слоями приложения. Его можно называть как MetricsHolder
. Перехватчик может хранить в нем значения, а в другом месте, где вы планируете получать показатели, которые вы можете прочитать из него (и использовать их напрямую или хранить в запросе, если он находится в слое пользовательского интерфейса, и там доступен запрос).
Но теперь возвращаюсь к вам вопрос. Даже если вы создадите что-то вроде MetricsHolder
, вы все равно столкнетесь с проблемой, которую вы не можете ввести в перехватчик mybatis.
Вы не можете просто добавить поле с аннотацией Autowired
к перехватчику и ожидать он должен быть установлен. Причиной этого является то, что перехватчик создается экземпляром mybatis, а не весной. Таким образом, у весны нет возможности вводить зависимости в перехватчик.
Один из способов справиться с этим - делегировать обработку перехвата весеннему компоненту, который будет частью весеннего контекста и может получить доступ к другим компонентам. Проблема заключается в том, как сделать этот компонент доступным для перехватчика.
Это можно сделать, сохранив ссылку на такой компонент в локальной переменной потока. Вот пример, как это сделать. Сначала создайте реестр, который будет хранить весенний боб.
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;
}
Затем вы можете создать перехватчик, который будет делегировать обработку весной bean:
@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
, которая делает то, что вам нужно, и сделать ее весенним бобом (здесь вы можете получить доступ к другому весеннему бобу, включая запрос, нет, как я писал выше):
@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
}
}
Теперь единственная проблема заключается в установке и очистке делегата в реестре.
Я сделал это с помощью аспекта, который был применен к моей службе (но вы можете сделать это вручную или весной mvc-перехватчик). Мой аспект выглядит так:
@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();
}
}
}