Arhn - архитектура программирования

Как удалять данные сеанса через регулярные промежутки времени, каждый час?

Я генерирую случайные токены по причине, указанной в этом вопросе, которые помещаются в java.util.List, а этот List хранится в область сеанса.

После поиска в Google я решил удалять все элементы (токены) каждый час, содержащиеся в этом List в сеансе.

Я мог бы подумать об использовании API Quartz, но это не представляется возможным для управление сеансом пользователя. То, что я пробовал с Quartz API (1.8.6, 2.x несовместимо с Spring 3.2, который я использую) в Spring, можно увидеть ниже.

package quartz;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

public final class RemoveTokens extends QuartzJobBean
{    
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException
    {
        System.out.println("QuartzJobBean executed.");
    }
}

И было настроено в файле application-context.xml следующим образом.

<bean name="removeTokens" class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass" value="quartz.RemoveTokens" />
    <property name="jobDataAsMap">
        <map>
            <entry key="timeout" value="5" />
        </map>
    </property>
</bean>

<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
      <property name="jobDetail" ref="removeTokens"/>
      <property name="startDelay" value="10000"/>
      <property name="repeatInterval" value="3600000"/>
</bean>

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  <property name="triggers">
      <list>
          <ref bean="simpleTrigger" />
      </list>
  </property>
</bean>

Переопределенный метод в классе RemoveTokens выполняется каждый час с начальным интервалом в 10 секунд, как настроено в XML, но невозможно выполнить какой-либо метод некоторого класса для удаления маркеров, доступных в List, который хранится в сеансе пользователя. Является ли это возможным?

Каков честный способ удалить этот List, хранящийся в области сеанса с определенным интервалом времени (каждый час)? Было бы намного лучше, если бы это стало возможным с помощью Quartz API.


ИЗМЕНИТЬ:

Согласно ответу ниже, я пробовал следующее, но, к сожалению, это не имело значения.

В файле application-context.xml

<task:annotation-driven executor="taskExecutor" scheduler="taskScheduler"/>
<task:executor id="taskExecutor" pool-size="5"/>
<task:scheduler id="taskScheduler" pool-size="10"/>

Для этого требуются следующие дополнительные пространства имен,

xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/task
                    http://www.springframework.org/schema/task/spring-task-3.2.xsd"

Следующий bean-компонент был зарегистрирован как bean-компонент с областью сеанса.

package token;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

@Service
//@Scope("session")
public final class SessionToken implements SessionTokenService
{
    private List<String> tokens;

    private static String nextToken()
    {
        long seed = System.currentTimeMillis(); 
        Random r = new Random();
        r.setSeed(seed);
        return Long.toString(seed) + Long.toString(Math.abs(r.nextLong()));
    }

    @Override
    public boolean isTokenValid(String token)
    {        
        return tokens==null||tokens.isEmpty()?false:tokens.contains(token);
    }

    @Override
    public String getLatestToken()
    {
        if(tokens==null)
        {
            tokens=new ArrayList<String>(0);
            tokens.add(nextToken());            
        }
        else
        {
            tokens.add(nextToken());
        }

        return tokens==null||tokens.isEmpty()?"":tokens.get(tokens.size()-1);
    }

    @Override
    public boolean unsetToken(String token)
    {                
        return !StringUtils.isNotBlank(token)||tokens==null||tokens.isEmpty()?false:tokens.remove(token);
    }

    @Override
    public void unsetAllTokens()
    {
        if(tokens!=null&&!tokens.isEmpty())
        {
            tokens.clear();
        }
    }
}

И интерфейс, который он реализует,

package token;

import java.io.Serializable;

public interface SessionTokenService extends Serializable
{
    public boolean isTokenValid(String token);
    public String getLatestToken();
    public boolean unsetToken(String token);
    public void unsetAllTokens();
}

И этот бин был сконфигурирован в файле application-context.xml следующим образом.

<bean id="sessionTokenCleanerService" class="token.SessionToken" scope="session">
    <aop:scoped-proxy proxy-target-class="false"/>
</bean>

Теперь я внедряю эту службу в следующий класс.

package token;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
public final class PreventDuplicateSubmission
{    
    @Autowired
    private final SessionTokenService sessionTokenService=null;

    @Scheduled(fixedDelay=3600000)
    public void clearTokens()
    {
        System.out.println("Scheduled method called.");
        sessionTokenService.unsetAllTokens();            
    }
}

А в файле application-context.xml,

<bean id="preventDuplicateSubmissionService" class="token.PreventDuplicateSubmission"/>

Оба вышеупомянутых bean-компонента аннотированы @Service, и они должны быть частью context:component-scan в файле dispatacher-servelt.xml (или как там его имя).

Метод, отмеченный аннотацией @Scheduled в предыдущем фрагменте кода, вызывается регулярно с заданной частотой, но вызывает следующее очевидное исключение.

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.sessionTokenCleanerService': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:343)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:33)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:184)
    at $Proxy79.unsetAllTokens(Unknown Source)
    at token.PreventDuplicateSubmission.clearTokens(PreventDuplicateSubmission.java:102)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:64)
    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:53)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
    at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:351)
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:178)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
    at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
    at org.springframework.web.context.request.SessionScope.get(SessionScope.java:90)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:329)
    ... 19 more

Чтобы очистить данные, хранящиеся в сеансе пользователя, метод, который выполняет эту задачу, должен вызываться регулярно через определенный интервал времени от каждого сеанса пользователя, что не относится к этой попытке. Каков путь? Вопрос может быть просто: как вызвать регулярный интервал времени для каждого сеанса пользователя? Поддерживает ли Spring или Servlet API что-то для этого?


  • Попытался определить bean-компонент PreventDuplicateSubmission как bean-компонент области сеанса с <aop:scoped-proxy proxy-target-class="false"/>, но он даже не вызвал метод, аннотированный @Scheduler в том же bean-компоненте. 23.04.2013
  • Как вы загружаете контекст приложения. Попробуйте загрузить его с помощью ContextLoaderListener -› mkyong.com/spring3 /spring-3-mvc-hello-world-example (5. Интеграция веб-приложения с Spring) 23.04.2013
  • Вот похоже. Мой файл web.xml доступен здесь. 24.04.2013
  • не могли бы вы решить проблему? 26.04.2013

Ответы:


1

Комментарии

Поздравляем с использованием токенов для предотвращения повторной отправки (рефакторинг «Ввести синхронизирующий токен» из книги «Основные шаблоны J2EE»). :^)

Кварц ценен для сложного или точного планирования. Но ваши требования не нуждаются в Quartz. Может быть полезнее изучить CDI, java.util.Timer, ScheduledExecutor и/или таймеры EJB.

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

Будьте осторожны, если вы когда-либо сохраняете ссылки на HttpSession или передаете его в качестве параметра. Хранение ссылок предотвращает сборку мусора после завершения сеанса, что приводит к (большой?) утечке памяти. Попытки очистить ссылки с помощью HttpSessionListener и других приемов могут не сработать и привести к беспорядку. Передача HttpSession методам в качестве параметра делает их искусственно зависимыми от контейнера сервлетов и затрудняет модульное тестирование, поскольку вам приходится имитировать сложные объекты HttpSession. Все данные сеанса удобнее заключать в единый объект UserSessionState — хранить ссылки на this в переменных экземпляра сеанса и объекта. Это также известно как шаблон объекта контекста, т. е. хранить все ваши данные в одном или нескольких объектах контекста POJO, независимо от классов протокола HTTP.

Отвечать

Я предлагаю два альтернативных решения вашей проблемы:

  1. Используйте java.util.Timer экземпляр singleton

    Вы можете заменить java.util.Timer (представленный в Java SE 1.3) на ScheduledExecutor (представленный в Java SE 5) для почти идентичного решения.

    Это доступно с JVM. Он не требует установки jar и настройки. Вы вызываете timerTask.schedule, передавая экземпляр TimerTask. Когда расписание подходит к концу, вызывается timerTask.run. Вы удаляете запланированные задачи через timerTask.cancel и timerTask.purge, которые освобождают используемую память.

    Вы кодируете TimerTask, включая его конструктор, создаете новый экземпляр и передаете его методу расписания, что означает, что вы можете хранить любые данные или ссылки на объекты, которые вам нужны, в TimerTask; вы можете сохранить ссылку на него и вызвать методы установки в любое время позже.

    Я предлагаю вам создать два глобальных синглтона: экземпляр Timer и экземпляр TimerTask. В вашем пользовательском экземпляре TimerTask сохраните список всех пользовательских сеансов (в форме POJO, такой как UserSessionState или компонент Spring/CDI, а не в форме HttpSession). Добавьте в этот класс два метода: addSessionObject и removeSessionObject с параметром UserSessionState или подобным. В методе TimerTask.run выполните итерацию по набору UserSessionState и очистите данные.

    Создайте собственный HttpSessionListener — из sessionCreated поместите в сеанс новый экземпляр UserSessionState и вызовите TimerTask.addUserSession; из sessionDestroyed вызовите TimerTask.removeUserSession.

    Вызовите синглтон timer.schedule глобальной области действия, чтобы запланировать экземпляр TimerTask для очистки содержимого ссылки области сеанса.

  2. Использовать список токенов с ограниченным размером (без очистки)

    Не очищайте токены по прошедшему времени. Вместо этого ограничьте размер списка (например, 25 токенов) и сохраните самые последние сгенерированные токены.

    Это, пожалуй, самое простое решение. Когда вы добавляете элемент в список, проверьте, не превысили ли вы максимальный размер, если это так, заверните и вставьте из самого раннего индекса в списке:

    if (++putIndex > maxSize) {
        putIndex = 0;
    }
    list.put(putIndex, newElement);
    

    Здесь не требуется планировщик, и нет необходимости формировать и поддерживать набор всех пользовательских сессий.

23.04.2013

2

Я думаю, что это должно быть намного проще.

О реализации токена синхронизатора

  1. Маркеры в шаблоне маркера синхронизатора НЕ предназначены для повторного использования. Токен считается действительным только для одной отправки. Ни больше ни меньше.

  2. В любой момент времени вам нужно сохранять только один токен для сеанса.

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

  4. При отправке все, что вам нужно сделать, это проверить, совпадают ли токен в форме и сеанс. Если это так, разрешите отправку формы и сбросьте значение токена в сеансе.

  5. Теперь, если пользователь повторно отправляет ту же форму (со старым токеном), токены не будут совпадать, и вы можете обнаружить двойную отправку или устаревшую отправку.

  6. С другой стороны, если пользователь перезагрузит саму форму, обновленный токен теперь будет присутствовать в скрытом элементе формы.

Вывод - нет необходимости сохранять список токенов для пользователя. Один токен на сеанс — это как раз то, что нужно. Этот шаблон широко используется в качестве меры безопасности для предотвращения атак CSRF. Где каждая ссылка на странице может быть вызвана только один раз. Даже это можно сделать с помощью всего одного токена за сеанс. Для справки вы можете увидеть, как работает CSRF Guard V3 https://www.owasp.org/index.php/Category:OWASP_CSRFGuard_Project#Source_Code

О сеансах

  1. Объект сеанса имеет значение только до тех пор, пока поток выполнения каким-то образом привязан к паре http-запрос/ответ. Или проще говоря, пока пользователь просматривает ваш сайт.

  2. Как только пользователь ушел, сеанс (из JVM) также исчезнет. Таким образом, вы не можете использовать таймер для его сброса.

  3. Сеансы сериализуются серверами, чтобы убедиться, что они могут быть запущены, когда пользователь повторно посещает ваш сайт (jsessionid используется для идентификации того, какой сеанс должен быть десериализован для какого сеанса браузера).

  4. Существует тайм-аут, связанный с сеансом, если тайм-аут истекает, сервер запускает новый сеанс, когда пользователь повторно посещает.

Вывод - нет веской причины периодически сбрасывать сеансы пользователей - и нет способа сделать это.

Дайте мне знать, если я что-то неправильно понял, и я надеюсь, что это поможет.

23.04.2013
  • если у пользователя есть более одной вкладки, и он отправляет обе формы, это следует рассматривать как двойную отправку! 24.04.2013
  • Если вы хотите поддерживать более одного токена за сеанс, поддерживайте список конечного размера, например, 10 токенов. Когда появится необходимость в 11-м токене, удалите первый. В любом случае вы не можете и не должны думать о периодическом сбросе сессий через код. Мой ответ остается в силе. Вы не сможете запустить задание на удаление сеансов. 28.04.2013
  • @AkshaySinghal: ОП спрашивает о периодическом удалении только атрибута сеанса, а не об удалении всего сеанса пользователя, как вы могли предположить, что он имел в виду. 29.04.2013
  • Я не предполагал сбрасывать всю сессию, думаю, мой комментарий мог создать такое впечатление. Спасибо за это, я ценю, что вы попытались убедиться, что я правильно понял вопрос. Несмотря на это, попытка обновить сеанс браузера после того, как пользователь давно ушел, невозможна. Так что мой ответ на вопрос остается прежним. 29.04.2013

  • 3

    Моя идея будет примерно такой:

    Краткая версия:

    1. изменить интервал задачи на 1 минуту;
    2. добавить метод unsetAllTokensIfNeeded к SessionTokenService, который будет вызываться каждую минуту, но будет просто очищать список токенов, если это действительно необходимо (в этом случае решение принимается на основе времени).

    Подробная версия:

    Ваша запланированная задача будет запускаться каждую минуту, вызывая метод unsetAllTokensIfNeeded, реализованный SessionToken. Реализация метода проверит, когда последний раз список токенов был чистым, и вызовет unsetAllTokens, если это было час назад.

    Но для того, чтобы вызывать его в каждом отдельном сеансе с областью действия SessionTokenService, вам понадобится список существующих SessionTokenService, чего можно добиться, зарегистрировав его во время создания в службе, которая будет его очищать (здесь вы должны использовать WeakHashMap, чтобы избежать жестких ссылок, это позволит избежать сбора объектов сборщиком мусора).

    Реализация будет примерно такой:

    Токены сеанса:

    Интерфейс:

    public interface SessionTokenService extends Serializable {
        public boolean isTokenValid(String token);
        public String getLatestToken();
        public boolean unsetToken(String token);
        public void unsetAllTokens();
        public boolean unsetAllTokensIfNeeded();
    }
    

    Выполнение:

    @Service
    @Scope("session")
    public final class SessionToken implements SessionTokenService, DisposableBean, InitializingBean {
    
        private List<String> tokens;
        private Date lastCleanup = new Date();
    
    // EDIT {{{
    
        @Autowired
        private SessionTokenFlusherService flusherService;
    
        public void afterPropertiesSet() {
            flusherService.register(this);
        }
    
        public void destroy() {
            flusherService.unregister(this);
        }
    
    // }}}
    
        private static String nextToken() {
            long seed = System.currentTimeMillis(); 
            Random r = new Random();
            r.setSeed(seed);
            return Long.toString(seed) + Long.toString(Math.abs(r.nextLong()));
        }
    
        @Override
        public boolean isTokenValid(String token) {        
            return tokens == null || tokens.isEmpty() ? false : tokens.contains(token);
        }
    
        @Override
        public String getLatestToken() {
            if(tokens==null) {
                tokens=new ArrayList<String>(0);
            }
            tokens.add(nextToken());            
    
            return tokens.isEmpty() ? "" : tokens.get(tokens.size()-1);
        }
    
        @Override
        public boolean unsetToken(String token) {                
            return !StringUtils.isNotBlank(token) || tokens==null || tokens.isEmpty() ? false : tokens.remove(token);
        }
    
        @Override
        public void unsetAllTokens() {
            if(tokens!=null&&!tokens.isEmpty()) {
                tokens.clear();
                lastCleanup = new Date();
            }
        }
    
        @Override
        public void unsetAllTokensIfNeeded() {
            if (lastCleanup.getTime() < new Date().getTime() - 3600000) {
               unsetAllTokens();
            }
        }
    
    }
    

    Сброс токена сеанса:

    Интерфейс:

    public interface SessionTokenFlusherService {
        public void register(SessionToken sessionToken);
        public void unregister(SessionToken sessionToken);
    }
    

    Выполнение:

    @Service
    public class DefaultSessionTokenFlusherService implements SessionTokenFlusherService {
    
        private Map<SessionToken,Object> sessionTokens = new WeakHashMap<SessionToken,Object>();
    
        public void register(SessionToken sessionToken) {
            sessionToken.put(sessionToken, new Object());
        }
    
        public void unregister(SessionToken sessionToken) {
            sessionToken.remove(sessionToken);
        }
    
        @Scheduled(fixedDelay=60000) // each minute
        public void execute() {
            for (Entry<SessionToken, Object> e : sessionToken.entrySet()) {
                e.getKey().unsetAllTokensIfNeeded();
            }
        }
    
    }
    

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

    <task:annotation-driven executor="taskExecutor" scheduler="taskScheduler"/>
    <task:scheduler id="taskScheduler" pool-size="10"/>
    

    С моей точки зрения, это было бы простым и хорошим решением.

    ИЗМЕНИТЬ

    При таком подходе вы более или менее регистрируете SessionToken в Flusher, когда spring завершает конфигурацию bean-компонента, и удаляете его, когда spring уничтожает bean-компонент, это делается при завершении сеанса.

    23.04.2013
  • Привет, это работает как есть, но объекты, содержащиеся в WeakHashMap, остаются после выхода пользователя из системы. Со временем это может быть длинный список. Уместно ли оставить их сборщику мусора для утилизации? Так же одна мелочь которую я не знаю, на этой строчке - flusherService.register(this); внутри конструктора класса SessionToken появляется предупреждение - Leaking this in constructor. Это можно как-то подавить? 24.04.2013
  • Да, поэтому я немного изменил образец, пусть SessionToken реализует DisposableBean и InitializingBean, это зарегистрирует bean-компонент сразу после конфигурации bean-компонента spring (избегайте утечки this в сообщении конструктора) и отменит его регистрацию при завершении сеанса, надеюсь, это поможет. 24.04.2013
  • Здравствуйте, конкретная проблема решена (со ссылкой на комментарий под вопросом). Однако я все еще искал другое условие внутри метода unsetAllTokensIfNeeded(). Что происходит, так это то, что это условие if (lastCleanup.getTime() > new Date().getTime() - 3600000) оценивается как истинное навсегда, как только оно оценивается как истинное, хотя это не является серьезной проблемой. Более того, я получал исключение, указывающее, что WeakHashMap не сериализуем. Этот ответ очень полезен и в будущем, пожалуйста, не удаляйте его. 26.04.2013
  • Я просто отредактировал это, я спросил, потому что мне любопытно, было ли это то, что вам нужно, соответствует ли оно вашим требованиям... 26.04.2013
  • @JavaMentor: bean-компоненты с областью действия сеанса требуют реализации интерфейса java.io.Serializalbe. Не так ли? Относительно комментария OP - Я получил исключение, указывающее, что WeakHashMap не сериализуем. 04.05.2013
  • @Lion, служба флешера, не относится к области сеанса (это своего рода запланированная служба, которая запускается каждую минуту и ​​просто очищает SessionTokens, которые хранятся в SessionTokenService, а этот относится к области сеанса). 04.05.2013

  • 4

    Я не очень разбираюсь в кварце, но в вашей ситуации я бы сделал именно так: Spring 3.2, верно? В контексте вашего приложения:

    <task:annotation-driven executor="taskExecutor" scheduler="taskScheduler"/>
    <task:scheduler id="taskScheduler" pool-size="10"/>
    

    Вам понадобится пространство имен задач...

    В каком-то классе (например, SessionCleaner.java):

        @Scheduled(fixedRate=3600000)
        public void clearSession(){
             //clear the session    
        }
    

    Что бы я сделал, так это использовать данные сеанса в качестве компонента сеанса, управляемого Spring:

    <bean id="mySessionData" class="MySessionDataBean" scope="session">
    </bean>
    

    а затем вводить его, где мне это нужно. Тогда клан будет выглядеть так.

    class SessionCleaner{
            @Autowired
            private MySessionDataBean sessionData;
    
            @Scheduled(fixedRate=3600000)
            public void clearSession(){
                sessionData.getList().clear();//something like that    
            }
    }
    
    21.04.2013

    5

    Я мало что знаю о Quartz. Но, видя ваше исключение, я могу предположить, что планирование является частью асинхронной связи. Поэтому для достижения этой цели используются многопоточность и модель, управляемая событиями. Итак, в вашем случае вы пытаетесь создать bean-компонент preventDuplicateSubmissionService . Контейнер создает bean-компонент как часть объекта. Но когда вы обращаетесь к нему, поток, созданный Quartz как часть асинхронного планирования, пытается получить услугу. Но , для этого потока bean-компонент не создается. Может быть, вы используете scope = "request">. Итак, когда областью является запрос для каждого HTTP-запроса, создается его объект. В вашем случае, когда вы пытаетесь получить доступ к bean-компоненту, тогда bean-компонент с запросом область не создается, потому что доступ к bean-компоненту осуществляется в режиме, отличном от http, поэтому, когда поток пытается получить доступ к службе, возникает исключение. Я также перешел от той же проблемы, когда пытался получить доступ к bean-компоненту, используя многопоточность в serviceImpl.in в моем случае bean-компонент для области запроса не был создан, потому что он создается для каждого режима HTTP-запроса. Я реализовал решение, данное здесь, и оно работает для меня. Доступ к прокси-компонентам с ограниченной областью действия в потоках

    29.04.2013

    6

    Кажется, периодическое задание по удалению токенов, которое вы настроили, не запускалось в контексте определенного пользовательского сеанса или всех пользовательских сеансов.

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

    Сохраните пару уникальных токенов с отметкой времени на стороне сервера. Отправляйте токен пользователю (но не метку времени) только при представлении формы. Когда форма отправлена, найдите, когда этот токен сгенерирован - если он истек, отклоните его.

    Таким образом, вам даже не нужен таймер, чтобы удалить все токены. Использование таймера также удалит вновь созданный токен.

    29.04.2013

    7

    В исходном методе Question (который работал правильно в соответствии с расписанием) используйте приведенный ниже код...

    package quartz;
    
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.springframework.scheduling.quartz.QuartzJobBean;
    
    public final class RemoveTokens extends QuartzJobBean implements javax.sevlet.http.HttpSessionListener {
    
        static java.util.Map<String, HttpSession> httpSessionMap = new HashMap<String, HttpSession>();
    
        void sessionCreated(HttpSessionEvent se) {
            HttpSession ss = se.getSession();
            httpSessionMap.put(ss.getId(), ss);
        }
    
        void sessionDestroyed(HttpSessionEvent se) {
            HttpSession ss = se.getSession();
            httpSessionMap.remove(ss.getId());
        }
    
        @Override
        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
            Set<String> keys = httpSessionMap.keySet();
    
            for (String key : keys) {
                HttpSession ss = httpSessionMap.get(key);
                Long date = (Long) ss.getAttribute("time");
    
                if (date == null) {
                    date = ss.getCreationTime();
                }
    
                long curenttime = System.currentTimeMillis();
                long difference = curenttime - date;
    
                if (difference > (60 * 60 * 1000)) {
                    /*Greater than 1 hour*/
                    List l = ss.getAttribute("YourList");
                    l.removeAll(l);
                    ss.setAttribute("time", curenttime);
                }
            }
        }
    }
    
    29.04.2013
    Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..