Spring Boot:RESTful 控制器中的多层@Services 用于许多入站请求
Posted
技术标签:
【中文标题】Spring Boot:RESTful 控制器中的多层@Services 用于许多入站请求【英文标题】:Spring Boot : Multi-layered @Services in RESTful controllers for many inbound requests 【发布时间】:2020-06-03 01:13:40 【问题描述】:不确定这是代码审查还是有关 Spring Boot 如何操作的问题,涉及具有许多入站请求的 @Service
组件。如果这是一个问题,我当然可以在 StackExchange > 代码审查中重新发布?我们在 Spring Boot 中基本上有以下模式:
List<DocumentMetadata>
对象的 RESTful 控制器
RESTful 控制器使用数据管理@Service
来处理ocumentMetadata
对象的获取和排序
数据管理@Service
使用自定义排序@Service
对这些documentMetadata
对象进行排序
叙述:本质上,一个 RESTful 端点将返回一个 List<DocumentMetadata>
对象(如果你愿意,可以列出对象列表)作为 ResponseEntity
。自定义排序实现为@Service
,并带有sort()
的方法。我的理解是 Spring Boot 将创建自定义排序 @Service
实用程序的 "singleton" 实例。我在 Spring Boot @Service
和生命周期上搜索了一些问题,并找到了以下内容。这篇文章描述了一些导致我的问题/评论的用法,Should service layer classes be singletons?,尤其是这条评论“此外,Spring 单例中的可变状态绝对没问题,你只需要知道可以对共享的操作执行哪些操作状态 - skaffman”。我还阅读了这篇帖子,Can Spring Boot application handle multiple requests simultaneously?,这似乎表明我们没问题,因为我们正在创建、排序和返回的对象是在 @GetMapping
内创建的,并传递到服务组件层,即它不是共享的。
我的问题:
这甚至是设计 RESTful 堆栈的正确方法吗?特别是我们需要的实用程序类...应该将它们作为@Service
或在数据管理@Service
层中使用的POJO
类来完成。
在 10-100 个请求同时到达 RESTful 端点的多请求情况下,自定义排序实用程序 @Service
和 sort()
方法会发生什么情况? sort()
方法是否按顺序服务每个请求?还是在不同的线程中并行?
是否每个请求都使用相同的排序方法,即自定义排序实用程序@Service
是一个“单例”那么如何使用sort()
方法?
如果 sort() 方法确实是单一的,如果两个用户同时点击该方法,一个用户是否可能最终得到另一个用户的排序列表?还是 Spring Boot 只是按顺序或在自己的线程中处理入站请求并避免任何混合。
代码 sn-ps :为简洁起见,我删除了大部分代码。
@RestController
@RequestMapping(value = "/search")
@CrossOrigin(origins = "*")
@Slf4j
public class SearchController
// @Service for Data handling...
@Autowired
private DataManagementService dataManagementService;
@GetMapping(value="/viewable-documents")
public SearchResponse getExternallyViewableDocuments(@RequestParam(required = false) Map<String, String> queryParams)
logger.debug("getViewableDocuments is called with ", queryParams);
SearchResponse searchResponse = new SearchResponse();
// ENDPOINT : Calls the Data Management handling @Service...
SearchResponse response = dataManagementService.getDocuments(queryParams);
@Service
public class DataManagementServiceImpl implements DataManagementService
// CUSTOM SORT UTILITY : Autowired with setter...
private DocumentSortUtility documentSortUtility;
@Autowired
public void setDocumentSortUtility(DocumentSortUtility setValue)
this.documentSortUtility = setValue;
@Override
public SearchResponse getDocuments(Map<String, String> request)
if (request.get("sort") != null && request.get("sort").equals("true"))
// SORT : Call the custom sort...
return serviceImpl.populateResponse(this.documentSortUtility.sort(response));
else
return serviceImpl.populateResponse(response);
@Service
public class DocumentSortUtility
public List<DocumentMetadata> sort(List<DocumentMetadata> documentMetadataList)
log.debug("Un Sorted:"+documentMetadataList);
// Do custom sort of documentMetadataList and place in retList...
log.debug("Sorted:"+retList);
return retList;
我已在代码中确认@Service
层中没有维护/管理“共享”可变对象。唯一被修改和/或创建的对象是在@Service
层的方法中完成的。
提前谢谢...
【问题讨论】:
【参考方案1】:如我所见,您的困惑基于 Spring 的 Singleton
范围以及当 @Service
类为 Singleton
时 Spring 如何处理多个请求。
Spring 将beans
创建并绑定为Singleton
对象,除非您使用@Scope
注释或与范围相关的注释定义特定范围,例如@SessionScope
。您可能已经知道这一点。
如果您对多个线程上的 Singleton
对象感到困惑,这个 answer 提供了很好的解释。那么,关于你的问题;
这甚至是设计 RESTful 堆栈的正确方法吗?具体来说 我们需要的实用程序类,应该作为@Service
还是作为POJO
在数据管理中使用的类@Service
层?
是和不是。如果您的 @Service
类是 singleton
(必须是)并且 不可变(无状态),那么这是正确的方法。这意味着,您的 @Service
类不应有任何可修改的字段。否则,您的@Service
会导致不可预知的结果。
如果在此上下文中使用了POJO
,则每次启动新线程时对象实例化都会初始化新对象,这将我们带到您的第二个问题;
在 10-100 个请求命中的多请求情况下 一次 RESTful 端点,自定义排序实用程序会发生什么@Service
和sort()
方法?sort()
方法是否为每个服务 按顺序请求?还是在不同的线程中并行?
当 10-100 或数千个或请求到达端点时,如果您使用了 POJO,它还会创建数千个额外的对象,这些对象会不必要地污染堆,因为我们可以使用一个 singleton
对象使用 @Service
注释。因为,singleton
对象是 immutable
(不会通过其方法调用保留任何更改)并在多个线程之间共享,能够同时执行而不会对结果产生任何副作用。
如果sort()
发生在内存中并返回排序后的结果,它会在线程的执行中独立执行。
是否每个请求都使用相同的排序方法,即自定义排序 实用程序@Service
是一个 "singleton" 那么sort()
方法如何 使用?
Singleton
并不意味着它的功能不能被共享,除非它们特别是synchronized
。在你的情况下,我相信这个sort()
方法不是synchronized
。因此,在每个线程上分别执行sort()
方法是没有问题的(没有差异,因为我们的singleton
对象是无状态/不可变的)。
如果sort()
方法是真正的单一方法,那么 1 个用户可能最终会 如果两个用户都点击了该方法,则与另一个用户的排序List
同时地?还是 Spring Boot 只是简单地处理入站 按顺序或在它们自己的线程中请求,并避免任何混合。
你知道,Spring boot 是基于 Java EE 的。所以,基本上它使用一个javax.servlet.ServletContext
类实例来处理这个request
和response
流,并且Java EE 中的许多其他类都参与了这个过程,由于Spring 的巧妙设计,我们看不到。但是流程是一样的。简而言之,每个request
都有单独的thread
绑定到它,并且那些request-threads
永远不会混淆。 thread
涉及整个过程,直到将响应分派给客户端。
你有没有注意到,除了特殊情况,我们不会手动创建线程?那是因为,Java EE 生态系统代表我们管理这些线程。
所以,对您的问题的回答是,不,sort()
不会执行一次,并且永远不会一个请求的结果最终出现在另一个请求的响应对象中,无论有多少千用户同时发出请求。这个answer 还提供了关于这些请求 - 响应机制如何在下面工作的精彩解释。
【讨论】:
你能please vote to undelete and reopen this question以上是关于Spring Boot:RESTful 控制器中的多层@Services 用于许多入站请求的主要内容,如果未能解决你的问题,请参考以下文章
Spring+Spring Boot+Mybatis框架注解解析
将 GZIP 压缩与 Spring Boot/MVC/JavaConfig 与 RESTful 结合使用
如何在 Spring Boot 中同时公开 SOAP Web 服务和 RESTful API?
使用 Spring Boot 的 RESTful API 中的循环依赖