整个应用程序的通用共享数据对象
Posted
技术标签:
【中文标题】整个应用程序的通用共享数据对象【英文标题】:Common shared data objects for entire application 【发布时间】:2019-06-01 13:56:12 【问题描述】:我有一些在 Spring Boot 应用程序中通用的数据对象 - 一个是登录的 employee
对象,另一个是 category
。我创建了一个 @Component
类,其中包含这些静态变量。这样我什至不必自动装配它们。它们可以像CurrentContext.employee
一样直接在控制器中使用。
@Component
public final class CurrentContext
public static Category currentCategory;
public static Employee employee;
@Autowired
private CategoryService categoryService;
@Autowired
private EmployeeService employeeService;
@EventListener
public void onApplicationEvent(ContextRefreshedEvent event)
currentCategory = categoryService.getCategory();
@EventListener
public void onLoginSuccess(InteractiveAuthenticationSuccessEvent event)
employee = employeeService.getEmployeeByUserId(((MyUserDetails) event.getAuthentication().getPrincipal()).getUserId());
这是正确的方法吗?请建议是否有更好的方法来处理共享数据
编辑
一些背景知识——我需要当前登录的employee
和category
,这对所有员工都是通用的。所以我在我的控制器中 autowired
employeeService
和 categoryService
并使用它们来获取数据。它们几乎在我的所有控制器方法中都是必需的,所以我想创建一个它们的 bean,以便我直接在我的控制器中使用它们并节省频繁的数据库调用。
【问题讨论】:
employee
对于每个登录用户应该不同吗?如果是,并且您以这种方式这样做,那么您总是会得到相同的员工.....
同意评论。您是否开发单用户应用程序?
正如您正确提到的肯,这对我来说是一个重大失败,因为同一员工会为每个用户加载。大卫,这是一个多用户应用程序。那么我该如何解决它,以便每个用户都加载他们的详细信息?
【参考方案1】:
通常,我们只将与横切关注点相关的依赖项(即跨整个应用程序的依赖项,例如 security 、 logging 、 transaction stuff 、 time provider 等)放在静态字段中。
通过以静态方式访问这些依赖项,我们不需要通过方法参数/构造函数将它们从对象传递到对象,这将使 API 更加干净而没有这种噪音(顺便说一句。这被称为 @987654322 @ 在 .NET 世界中)。
您的Employee
对象很可能属于这种类型,因此可以以静态方式访问它。但是由于它们的范围是每个会话,你不能简单地将它放在一个类的静态字段中。如果是,那么您总是在所有会话中获得相同的员工。相反,您必须以某种方式将其存储在会话范围的对象中(例如HttpSession
)。然后在开始处理网络请求时,从会话中获取它,然后将其放入封装在“ContextHolder”对象中的ThreadLocal
中。然后,您以静态方式访问该“ContextHolder”。
听起来很复杂很吓人?别担心,因为 Spring Security 已经为你实现了这些东西。您需要做的是customize Authentication#getPrincipal()
or extend default Authentication
包含您的Employee
。然后使用SecurityContextHolder.getContext().getAuthentication()
获取它
对于您的currentCategory
,如果它们不是横切关注点并且是应用程序范围,那么制作一个单例 bean 来获取它的值是一个更好的 OOP 设计。
@Component
public final class CurrentCategoryProvider
@Autowired
private CategoryService categoryService;
public Category getCurrentCategory()
//or cache the value to the an internal properties depending on your requirements
return categoryService.getCategory();
然后您将CurrentCategoryProvider
注入需要访问currentCategory
的bean。
【讨论】:
谢谢@ken-chan。如果我尝试从SecurityContextHolder.getContext().getAuthentication()
获取employee
,我没有得到“完整”对象,它将包含其他对象的代理,例如Project
,它是员工的一部分。 Eager fetch 可能会解决这个问题,但我正在考虑一个更简单的解决方案。我在想如果我可以创建一个将在身份验证成功事件上触发的员工 bean,它应该很好。我已经对我的原始问题添加了一些更新。抱歉,我是 Spring 新手。
是的。您必须急切地获取所有必需的对象,这是非常正常的。顺便说一句,如果您在身份验证成功事件上创建了一个雇主 bean,那么您将回到那个问题。每个登录用户登录后会取回他们自己的员工 bean 吗?以上是关于整个应用程序的通用共享数据对象的主要内容,如果未能解决你的问题,请参考以下文章