spring 如何同时创建具有“请求”和“调度”作用域的bean?

sdnqo3pr  于 2023-08-02  发布在  Spring
关注(0)|答案(1)|浏览(105)

我有UsersHandledInRequestCache bean,用于在请求期间缓存:

  1. @Configuration
  2. public class CacheConfiguration {
  3. @Bean
  4. @RequestScope
  5. public UsersHandledInRequestCache usersHandledInRequestCache() {
  6. return new UsersHandledInRequestCache();
  7. }
  8. public static class UsersHandledInRequestCache {
  9. private final Set<Integer> cachedUsersIds = new HashSet<>();
  10. public void cache(Integer userId) {
  11. this.cachedUsersIds.add(userId);
  12. }
  13. public boolean isCached(Integer userId) {
  14. return this.cachedUsersIds.contains(userId);
  15. }
  16. }
  17. }

字符串
我有一个使用bean的服务:

  1. @Service
  2. public class UserService {
  3. @Resource(name = "usersHandledInRequestCache")
  4. private CacheConfiguration.UsersHandledInRequestCache usersHandledInRequestCache;
  5. public void doSomething(Integer userId) {
  6. if (!usersHandledInRequestCache.isCached(userId)) {
  7. // do something
  8. usersHandledInRequestCache.cache(userId);
  9. }
  10. }
  11. }


所以服务可以由控制器调用,没有问题:

  1. @RestController
  2. @RequiredArgsConstructor
  3. @RequestMapping("/users")
  4. public class UserController {
  5. private final UserService userService;
  6. @PostMapping
  7. public void doSomething(@RequestBody Collection<Integer> usersIds) {
  8. usersIds.forEach(userService::doSomething);
  9. }
  10. }


在本例中,UsersHandledInRequestCache bean为每个请求创建并且工作正常。但是我有预定的服务:

  1. @Component
  2. @RequiredArgsConstructor
  3. public class UserJob {
  4. private final UserService userService;
  5. @Scheduled(cron = "0 0 7 * * *")
  6. public void doSomething() {
  7. userService.getSomeUsersIds.forEach(userService::doSomething);
  8. }
  9. }


而当调度服务开始工作时,我得到异常,因为字段UserService#usersHandledInRequestCache无法初始化 (因为没有任何请求,方法调用是由调度服务工作发起的)
创建名为“scopedTarget. usersHandledInRequestCache”的bean时出错:作用域“请求”对于当前线程不是活动的;如果您打算从单例引用这个bean,请考虑为它定义一个作用域代理;嵌套异常为java.lang.IllegalStateException:未找到线程绑定请求:你指的是实际Web请求之外的请求属性,还是在原始接收线程之外处理请求?如果你实际上是在一个web请求中操作,并且仍然收到这个消息,那么你的代码可能是在DispatcherServlet之外运行的:在这种情况下,使用RequestContextListener或RequestContextFilter公开当前请求。
我想让bean UsersHandledInRequestCache同时具有“request”作用域和“schedule”作用域。我怎么能得到它?

pieyvz9o

pieyvz9o1#

因为spring请求范围只注册了web应用请求,所以出现上述错误。

通过RequestAttribute接口的实现,spring从HttpRequest中放入/获取请求范围信息。
如果线程是在Web请求之外启动的,则线程的变量中没有所需的属性,因此会引发异常。
我遇到了同样的问题-需要使用@Scheduled执行作业中的代码,因此它无法使用任何Session或Request作用域bean。
有几种方法可以解决这个问题,我用下面的方法解决了它:

实现RequestAttributes接口:

  1. import org.springframework.web.context.request.RequestAttributes;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. public class CustomRequestAttribute implements RequestAttributes {
  5. private Map<String, Object> requestAttributes = new HashMap<>();
  6. @Override
  7. public Object getAttribute(String name, int scope) {
  8. if (scope == RequestAttributes.SCOPE_REQUEST) {
  9. return this.requestAttributes.get(name);
  10. } else return null;
  11. }
  12. @Override
  13. public void setAttribute(String name, Object value, int scope) {
  14. if (scope == RequestAttributes.SCOPE_REQUEST) {
  15. this.requestAttributes.put(name, value);
  16. }
  17. }
  18. @Override
  19. public void removeAttribute(String name, int scope) {
  20. if (scope == RequestAttributes.SCOPE_REQUEST) {
  21. this.requestAttributes.remove(name);
  22. }
  23. }
  24. @Override
  25. public String[] getAttributeNames(int scope) {
  26. if (scope == RequestAttributes.SCOPE_REQUEST) {
  27. return this.requestAttributes.keySet().toArray(new String[0]);
  28. } else return new String[0];
  29. }
  30. @Override
  31. public void registerDestructionCallback(String name, Runnable callback, int scope) {}
  32. @Override
  33. public Object resolveReference(String key) { return null; }
  34. @Override
  35. public String getSessionId() { return null; }
  36. @Override
  37. public Object getSessionMutex() { return null; }
  38. }

字符串
现在,在代码中请求的开始,通过执行以下语句指示RequestContextHolder使用此CustomRequestAttribute,并在finally块中清除请求属性。

  1. @Scheduled(cron = "0 0 7 * * *")
  2. public void doSomething() {
  3. try {
  4. RequestContextHolder.setRequestAttributes(
  5. new CustomRequestAttribute());
  6. userService.getSomeUsersIds
  7. .forEach(userService::doSomething);
  8. } catch (Exception e) {
  9. // ...
  10. } finally {
  11. RequestContextHolder.resetRequestAttributes();
  12. }
  13. }

展开查看全部

相关问题