spring一站式开发
在最近的(核心)Spring框架培训课程中,有人问我:“(Java)Spring开发人员应该知道的一件事是什么?” 这个问题使我措手不及。 是的,(核心)Spring框架确实涵盖了很多领域(例如,bean,配置,面向方面的编程,事务)。 我很难只指出一件事。 最后,我提到了我们为期3天的培训课程所涵盖的所有内容。
如果(Java)Spring开发人员应该知道一件事,那应该是什么?
当我进一步思考这个问题时,我开始思考最重要的一个问题。 我最后想到了Spring如何最重要地使用方面将行为添加到托管对象(通常称为bean)中。 这就是Spring框架如何支持事务,安全性,范围,基于Java的配置等。 我在这篇文章中分享我的想法。
我最后想到了Spring如何最重要地使用方面将行为添加到托管对象(通常称为bean)中。
ORM和延迟加载异常
大多数使用某种形式的ORM的开发人员都遇到了一个异常,该异常表明无法加载子实体(例如LazyInitializationException
)。
一些遇到此问题的开发人员将使用“打开的视图中的会话”( OSIV )模式来保持会话打开并防止发生此异常。 但是我觉得这太过分了。 更糟糕的是,一些开发人员认为“开放会话可见”模式是唯一的解决方案。 造成这种误解的潜在根本原因可能是,开发人员可能不了解有效使用Spring Framework来保持ORM会话打开时间更长的知识。
对于JPA ,“打开视图中的实体管理器”模式将在请求开始时创建一个实体管理器,将其绑定到请求线程,并在响应完成时将其关闭。
那么,如果不是OSIV模式,哪种解决方案更好?
简短的答案是使用Spring框架在需要的时间内保持会话打开(例如@Transactional
)。 请继续阅读,因为我会提供更长的答案。
服务和存储库
在分层体系结构中,典型的设计模式是定义域或应用程序服务 (通常定义为接口)以提供业务功能(例如,开始使用购物车,向该购物车添加商品,搜索产品)。 域和应用程序服务的实现通常会将域实体的检索/持久性委派给存储库。
存储库 (或数据访问对象)也被定义为检索/持久化域实体(即提供ORM和CRUD访问)的接口。 自然地,存储库实现使用ORM库(例如JPA / Hibernate,myBATIS)来检索和保留域实体。 这样,它使用ORM框架的类连接到持久性存储,检索/持久化实体并关闭连接(在Hibernate中称为会话)。 此时,没有延迟加载失败的问题。
当服务使用资料库中检索域实体,并希望加载的子实体(库方法返回之后 )发生的懒加载失败问题。 到存储库返回域实体时,ORM会话将关闭。 因此,尝试访问/加载域服务中的子实体会导致异常。
下面的代码段说明了当订单实体的子项目由存储库返回后被延迟加载时,如何发生延迟加载异常。
@Entity
public class Order {@OneToMany // defaults to FetchType.LAZYprivate List<OrderItem> items;…public List<OrderItem> getItems() {…}
}public class SomeApplicationServiceImpl implements SomeApplicationService {private OrderRepository orderRepository;…@Overridepublic void method1(…) {…order = orderRepository.findById(...);order.getItems(); // <-- Lazy loading exception occurs!…}…
}public class OrderRepositoryImpl implements OrderRepository {@PersistenceContextprivate EntityManager em;…@Overridepublic Order findById(...) {...}…
}
存储库实现将JPA明确用于其ORM(如使用EntityManager
)。
在这一点上,一些开发人员可能选择使用紧急获取来防止延迟初始化异常。 告诉ORM急切地获取订单实体的子项将起作用。 但是有时候,我们不需要加载子项。 急切地加载它可能是不必要的开销。 仅在需要时加载它会很棒。
为了防止延迟初始化异常(而不是被迫急于获取),我们需要保持ORM会话打开,直到调用服务方法返回。 在Spring中,可以像@Transactional
一样简单地注释服务方法以保持会话打开。 我发现这种方法比使用“在视图中打开会话”模式(或被迫使用紧急获取)更好,因为它仅在我们希望的持续时间内保持会话打开。
public class SomeApplicationServiceImpl implements SomeApplicationService {private OrderRepository orderRepository;…@Override@Transactional // <-- open the session (if it's not yet open)public void method1(…) {…order = orderRepository.findById(...);order.getItems(); // <-- Lazy loading exception should not happen…}…
}
表示层中的域实体
即使将ORM会话在服务层中(在存储库实现对象之外)保持打开状态,当我们将域实体暴露给表示层时,仍然可能发生惰性初始化异常。 同样,由于这个原因,一些开发人员更喜欢OSIV方法,因为它还可以防止表示层中的延迟初始化异常。
但是,为什么要在表示层中公开域实体?
根据经验,我曾与那些希望在表示层中公开域实体的团队合作。 这通常会导致贫血领域模型 ,因为表示层框架需要一种将输入值绑定到对象的方法。 这迫使域实体具有getter和setter方法以及零参数构造函数。 具有getter和setter将使不变式难以执行。 对于简单域,这是可行的。 但是对于更复杂的领域,更丰富的领域模型将是首选,因为它更易于实施不变式。
在更丰富的域模型中,表示表示层输入/输出值的对象实际上是数据传输对象(DTO)。 它们代表在域层中执行的输入(或命令)。 考虑到这一点,我更喜欢使用DTO并维护更丰富的域模型。 因此,我并没有真正在表示层遇到惰性初始化异常。
向受管对象添加行为的方面
Spring会拦截对这些@Transactional
注释方法的调用,以确保ORM会话处于打开状态。
事务(或只是保持ORM会话保持打开状态)并不是使用方面提供的唯一行为。 有安全性,范围,基于Java的配置等。 知道Spring框架使用方面来添加行为是我们让Spring管理我们开发的POJO的主要原因之一。
结论
你去。 对我来说,这是Spring Framework开发人员在使用内核时应了解的最重要的一件事。 现在,我已经对什么是最重要的事情发表了意见,您呢? 在处理Core Spring时,您认为最重要的一件事是。 干杯!
翻译自: https://www.javacodegeeks.com/2016/02/one-thing-good-spring-developers-know.html
spring一站式开发