领先一步
VMware 提供培训和认证,助您加速进步。
了解更多大家好!
这是我上个月加入 Interface21 以来发布的第一篇文章。我之前的博客现在已正式弃用,我将不再更新它。
那么我第一篇文章的主题是什么(除了自我介绍)? 验证逻辑。 这不会是关于如何在 Spring 框架中执行验证的演练,而是会讨论我一个特别恼人的问题:)
我特别想讨论的是,验证逻辑中到底应该包含什么。这似乎是一个显而易见的答案;“用于验证指定数据的逻辑”。好的,这确实是显而易见的,但请继续阅读 :)。如您所知,Spring 框架通过 Errors 和 Validator 接口为您提供了一个漂亮的验证抽象层。特别是,Validator 是您将业务特定验证规则应用于已填充的域对象的地方。Spring 卓越的绑定支持负责根据某些输入更新您的域模型,而验证器负责确保已填充的域模型在语义上是正确的。那么我的困扰是什么呢?我一次又一次地遇到一些应用程序,它们允许验证逻辑从验证器中流出,进入控制器(对于 Web 应用程序),甚至更糟的是进入中间层。在人们开始提出异议之前;我并不是说验证不属于中间层,我只是说 Validator 是放置验证逻辑的地方!
最常见的例子是添加一个新实体,比如一个用户。通常,验证器会执行许多“简单”检查(字段不能为空,文本字段长度超过 25 个字符等)。控制器(例如)然后会调用中间层(userService.add(user))并捕获 DuplicateKeyException(或强类型的 DuplicateUserException)异常。如果抛出此异常,控制器将填充错误对象并重新显示表单。
那么这张图有什么问题呢?很简单,事实是某些验证现在隐式地通过抛出 DuplicateKeyException 来完成,表明验证失败了!。DB(在此示例中)在插入数据之前验证数据以确保其唯一性,如果不唯一则抛出异常。
我的观点(我承认我说的很冗长 ;))是,这都是属于 Validator 的验证逻辑。将这种唯一性检查移到验证器中,它本就属于那里(!),可以带来许多好处:
注意:有一种观点认为您正在重复验证逻辑;数据库知道什么是唯一和不唯一的,那么为什么要将该逻辑复制到控制器中呢?好吧,关键是您确实在重复该逻辑,您正在使用抛出 DuplicateXXXException 来指示验证失败,所以这并不是一个真正有效的论点。
还有另一种观点认为,这不能 100% 保证捕获所有(在这种情况下)重复键。这是事实。在调用验证之后但在进行中间层调用之前,有一个很小的机会窗口,另一个进程可能会潜入并创建唯一行,但这是一个非常非常小的窗口(通常是毫秒),而且无论如何都可能会抛出 OptimisticLockingException。还要考虑数据的性质。两个不同的线程同时创建单个唯一实体的情况是极不可能的。如果确实发生了,没关系。让异常向上冒泡到容器,因为它现在确实是一个异常情况。
抱怨结束。
附注:我剩下的博客文章可能也会同样冗长 :)