Получить класс вызывающего в Java CDI Interceptor - PullRequest
0 голосов
/ 28 октября 2019

Я пытаюсь реализовать кеш, который содержит результаты определенного вызова бизнес-метода, а затем обновляется каждые 30 минут.

Мне удалось это сделать с помощью одноэлементного EJB-компонента с использованием запланированного метода;однако теперь каждый класс, который вызывает этот бизнес-метод, должен вместо этого вызывать метод из одноэлементного файла, который предоставляет результаты из кэша.

Я хочу избежать такого поведения и сохранить код из этих классов как есть, поэтому я подумал об использовании перехватчика, который будет перехватывать каждый вызов этого конкретного бизнес-метода и возвращать вместо этого результаты из одиночного кэша.

Однако в этом решении приложение останавливается, так как синглтон вызывает сам перехваченный бизнес-метод для кэширования его результатов, поэтому перехватчик перехватывает вызов (извините за повторение) и пытается вернуть результат одноэлементного метода, который предоставляеткэшированные значения, пока синглтон все еще ожидает вызова бизнес-метода.

Наиболее очевидным решением было бы получить вызывающий метод от перехватчика и проверить, соответствует ли его класс синглтону. ;если это так, продолжите вызов, в противном случае верните кэшированные результаты из синглтона. Однако, похоже, что объект InvocationContext, используемый перехватчиком, не предоставляет никаких методов для доступа к информации о вызывающем объекте перехваченного метода. Есть ли другой способ получить доступ к классу вызывающего абонента или какое-либо решение этой проблемы?

Вот мой одноэлементный класс:

@Singleton
@Startup
public class TopAlbumsHolder {

    private List<Album> topAlbums;

    @Inject
    private DataAgent dataAgent;

    @PostConstruct
    @Schedule(hour = "*", minute = "*/30", persistent = false)
    private void populateCache() {
        this.topAlbums = this.dataAgent.getTopAlbums();
    }

    @Lock(LockType.READ)
    public List<Album> getTopAlbums() {
        return this.topAlbums;
    }

}

А вот мой перехватчик:

@Interceptor
@Cacheable(type = "topAlbums")
public class TopAlbumsInterceptor {

    @Inject
    private TopAlbumsHolder topAlbumsHolder;

    @AroundInvoke
    public Object interceptTopAlbumsCall(InvocationContext invocationContext) throws Exception {
        // if the caller's class equals that of the cache singleton, then return invocationContext.proceed(); 
        // otherwise:
        return this.topAlbumsHolder.getTopAlbums();
    }

}

Обратите внимание, что аннотация @Cacheable является пользовательской привязкой перехватчика, а не javax.persistence.Cacheable.

РЕДАКТИРОВАТЬ: Я изменил метод перехватчика следующим образом:

@AroundInvoke
public Object interceptTopAlbumsCall(InvocationContext invocationContext) throws Exception {
    for (StackTraceElement stackTraceElement : Thread.currentThread().getStackTrace())
        if (TopAlbumsHolder.class.getName().equals(stackTraceElement.getClassName()))
            return invocationContext.proceed();
    return this.topAlbumsHolder.getTopAlbums();
}

Но я сомневаюсь, что это самое чистое решение, и я не знаю, является ли оно портативным.

РЕДАКТИРОВАТЬ 2: В случае, если это не достаточно ясно, мне нужно получить доступ к информации о invoker класс перехваченного метода, а не вызванный класс, у которого его метод перехвачен;вот почему я перебираю трассировку стека для доступа к классу вызывающего, но считаю, что это не элегантное решение, даже если оно работает.

Ответы [ 3 ]

1 голос
/ 01 ноября 2019

Для того, что вам нужно сделать, я бы сказал, использовать перехватчик или декоратор. Ваш перехватчик, однако, не так. Во-первых, вам не хватает основной части, которая является вызовом InvocationContext.proceed(), который перенаправляет вызов следующему в строке перехватчику (если есть) или сам вызов метода. Во-вторых, точка инъекции, которую вы поместили туда, очень специфична и поможет вам, только если вы перехватите этот тип бобов. Как правило, метод перехватчика вокруг вызова вызывается следующим образом:

    @AroundInvoke
    Object intercept(InvocationContext ctx) throws Exception {
        // do something before the invocation of the intercepted method
        return ctx.proceed(); // this invoked next interceptor and ultimately the intercepted method
        // do something after the invocation of the intercepted method
    }

Кроме того, если вам нужна информация метаданных о том, какой компонент был перехвачен, каждый перехватчик может внедрить специальный встроенный компонент только для этого. Из метаданных вы можете собрать информацию о том, какой бин вы сейчас перехватываете. Вот как вы получаете эти метаданные:

    @Inject
    @Intercepted
    private Bean<?> bean;

Обратите внимание, что перехватчики не знают, какой тип они перехватывают, это может быть что угодно, и поэтому вам обычно нужно работать с простым Object. Если вам нужно что-то более конкретное, CDI предлагает шаблон Decorator, который в основном является перехватчиком с учетом типов. Он имеет специальную точку инъекции (делегат), которая дает вам прямой доступ к украшенному бобу. Возможно, он лучше подходит вашему сценарию, посмотрите на эту часть спецификации CDI, объясняющую декораторы .

0 голосов
/ 01 ноября 2019

Итерация по трассировке стека для проверки на TopAlbumsHolder не является хорошим способом.
Чтобы избежать вызова перехватчика во время вызова класса getTopAlbums() из DataAgent, вы можете указать планировщик непосредственно в DataAgent, который собирает данные и помещает их в TopAlbumsHolder. Вы можете сделать это по-другому, но ваш главный пункт - прямой вызов getTopAlbums() в компоненте DataAgent без участия прокси (в этом случае перехватчик не будет применяться).

PS Обратите внимание, что кэшированные данные должныбыть неизменным (как коллекция, так и ее объекты).

0 голосов
/ 30 октября 2019

Есть недоразумение. Вы не вводите объект, который перехватывается в перехватчик, но используете invocationContext. Вам просто нужно позвонить invocationContext.proceed(), тогда рекурсии нет. Результат продолжения () вы можете кешировать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...