出于此问题中提到的原因,我正在生成随机令牌,这些令牌放置在 a 中java.util.List
,并且List
保存在会话范围内。
经过一些谷歌搜索后,我决定每小时删除List
会话中包含的所有元素(令牌)。
我可以考虑使用Quartz API,但这样做似乎无法操纵用户的会话。我在 Spring 中使用 Quartz API(1.8.6、2.x 与我正在使用的 Spring 3.2 不兼容)尝试的内容如下所示。
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();
}
这个bean在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 是否支持完成此任务?