Я не пользователь Spring или Java EE, но я могу помочь вам с аспектом.Я тоже немного погуглил, потому что ваши фрагменты кода без импорта и имен пакетов немного противоречивы, поэтому я не могу просто скопировать, вставить и запустить их.Судя по JavaDocs для JpaRepository и JpaSpecificationExecutor , которые вы расширяете в своем ExampleRepository
, вы пытаетесь перехватить
Page<T> PagingAndSortingRepository.findAll(Pageable pageable)
(унаследовано от JpaRepository
) и вместо этого позвоните
List<T> JpaSpecificationExecutor.findAll(Specification<T> spec, Pageable pageable)
, верно?
Таким образом, теоретически мы можем использовать это знание в наших советах и советах, чтобы быть более безопасными для типов и избегать уродливых приемов отражения,Единственная проблема здесь в том, что перехваченный вызов возвращает Page<T>
, а метод, который вы хотите вызвать, вместо этого возвращает List<T>
.Вызывающий метод, безусловно, ожидает первого, а не второго, если только вы не всегда используете Iterable<T>
, который является суперинтерфейсом для обоих рассматриваемых интерфейсов.Или, может быть, вы просто игнорируете возвращаемое значение?Если вы не ответите на этот вопрос или не покажете, как вы изменили свой код, чтобы сделать это, вам будет сложно действительно ответить на ваш вопрос.
Так что давайте просто предположим, что возвращаемый результат либо игнорируется, либо обрабатывается как Iterable
,Тогда ваша пара точек / советов выглядит так:
@Around("execution(* findAll(*)) && args(pageable) && target(exampleRepository)")
public Object filterQueriesByCompany(ProceedingJoinPoint thisJoinPoint, Pageable pageable, ExampleRepository exampleRepository) throws Throwable {
return exampleRepository.findAll(CompanySpecification.fromCompany(user), pageable);
}
Я проверил, все работает.Я также думаю, что это немного более элегантно, безопасно для типов и читабельно, чем то, что вы пробовали или что было предложено Eugen.
PS: Другой вариант - вручную преобразовать список в соответствующую страницу перед возвратом изсовет аспекта, если вызывающий код действительно ожидает, что объект страницы будет возвращен.
Обновление в связи с дополнительным вопросом:
Eugen wrote:
Для другой сущности, скажем, Foo
, хранилище будет public interface FooRepository extends JpaRepository<Foo, Long>, JpaSpecificationExecutor<Foo> { }
Хорошо, тогда давайте просто обобщим pointcut и предположим, что он всегда должен нацеливаться на классыкоторые расширяют оба рассматриваемых интерфейса:
@Around(
"execution(* findAll(*)) && " +
"args(pageable) && " +
"target(jpaRepository) && " +
//"within(org.springframework.data.jpa.repository.JpaRepository+) && " +
"within(org.springframework.data.jpa.repository.JpaSpecificationExecutor+)"
)
public Object filterQueriesByCompany(ProceedingJoinPoint thisJoinPoint, Pageable pageable, JpaRepository jpaRepository) throws Throwable {
return ((JpaSpecificationExecutor) jpaRepository)
.findAll(CompanySpecification.fromCompany(user), pageable);
}
Часть зареза, которую я закомментировал, является необязательной, поскольку я сужаюсь до вызовов JpaRepository
методов уже через target()
привязку параметра с использованием сигнатуры совета.Однако следует использовать второй within()
, чтобы убедиться, что перехваченный класс действительно расширяет второй интерфейс, чтобы мы могли без проблем приводить и выполнять другой метод.
Обновление 2:
Как сказал Евгений, вы также можете избавиться от приведения, если привязаете целевой объект к типу JpaSpecificationExecutor
- но только если вам не нужен JpaRepository
вкод вашего совета до этого.В противном случае вам придется разыграть другой путь.Здесь кажется, что это действительно не нужно, поэтому его идея делает решение более скудным и выразительным.Спасибо за вклад.: -)
@Around(
"target(jpaSpecificationExecutor) && " +
"execution(* findAll(*)) && " +
"args(pageable) && " +
"within(org.springframework.data.jpa.repository.JpaRepository+)"
)
public Object filterQueriesByCompany(ProceedingJoinPoint thisJoinPoint, Pageable pageable, JpaSpecificationExecutor jpaSpecificationExecutor) throws Throwable {
return jpaSpecificationExecutor.findAll(CompanySpecification.fromCompany(user), pageable);
}
Или же, если вы не хотите сливаться execution()
с within()
(дело вкуса):
@Around(
"target(jpaSpecificationExecutor) && " +
"execution(* org.springframework.data.jpa.repository.JpaRepository+.findAll(*)) && " +
"args(pageable)"
)
public Object filterQueriesByCompany(ProceedingJoinPoint thisJoinPoint, Pageable pageable, JpaSpecificationExecutor jpaSpecificationExecutor) throws Throwable {
return jpaSpecificationExecutor.findAll(CompanySpecification.fromCompany(user), pageable);
}
Менее безопасно для типов,но также вариант, если вы считаете, что других классов с подписью * findAll(Pageable)
нет:
@Around("target(jpaSpecificationExecutor) && execution(* findAll(*)) && args(pageable)")
public Object filterQueriesByCompany(ProceedingJoinPoint thisJoinPoint, Pageable pageable, JpaSpecificationExecutor jpaSpecificationExecutor) throws Throwable {
return jpaSpecificationExecutor.findAll(CompanySpecification.fromCompany(user), pageable);
}
Вы можете заметить, что это подозрительно похоже на мое первоначальное решение для одного конкретного подчиненного интерфейса, и вы правы,Однако я рекомендую быть немного более строгим и не использовать последний вариант, даже если он работает в моем тестовом примере, и вам, вероятно, будет хорошо.
Наконец, я думаю, что мы рассмотрели большинство основк настоящему времени.