整个应用程序的通用共享数据对象

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());



这是正确的方法吗?请建议是否有更好的方法来处理共享数据

编辑 一些背景知识——我需要当前登录的employeecategory,这对所有员工都是通用的。所以我在我的控制器中 autowired employeeServicecategoryService 并使用它们来获取数据。它们几乎在我的所有控制器方法中都是必需的,所以我想创建一个它们的 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 吗?

以上是关于整个应用程序的通用共享数据对象的主要内容,如果未能解决你的问题,请参考以下文章

共享 NSDateFormatter - 最佳实践?

为整个服务中使用的数据对象设计通用接口

Junit单元测试

什么是上下文

ServletContext对象

作用域和内置对象