41

@Entity是否可以使用 Spring 的依赖注入将 bean 注入 JPA ?

我试图 @Autowire ServletContext 但是,虽然服务器确实启动成功,但在尝试访问 bean 属性时我收到了 NullPointerException。

@Autowired
@Transient
ServletContext servletContext;
4

3 回答 3

53

您可以将依赖项注入到不受 Spring 容器管理的对象中,@Configurable如下所述:http: //static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-atconfigurable .

正如您现在所意识到的,除非使用@Configurable适当的 AspectJ 编织配置,否则 Spring 不会将依赖项注入使用new运算符创建的对象中。实际上,除非您从 中检索它们,否则它不会将依赖项注入对象ApplicationContext,原因很简单,它根本不知道它们的存在。即使您使用 注释您的实体@Component,该实体的实例化仍将new由您或诸如 Hibernate 之类的框架的操作执行。请记住,注释只是元数据:如果没有人解释该元数据,它不会添加任何行为或对正在运行的程序产生任何影响。

尽管如此,我强烈建议不要将 aServletContext注入实体。实体是域模型的一部分,应该与任何交付机制分离,例如基于 Servlet 的 Web 交付层。当命令行客户端或其他不涉及 ServletContext 的东西访问该实体时,您将如何使用该实体?您应该从该 ServletContext 中提取必要的数据,并通过传统方法参数将其传递给您的实体。通过这种方法,您将获得更好的设计。

于 2013-07-16T22:40:40.783 回答
22

是的,当然可以。您只需要确保实体也注册为 Spring 托管 bean,或者以声明方式使用<bean>标签(在某些 spring-context.xml 中)或通过注释,如下所示。

使用注释,您可以使用@Component(或更具体的构造型来标记您的实体,@Repository它可以为 DAO 启用自动异常翻译,并且可能会或可能不会干扰 JPA)。

@Entity
@Component
public class MyJAPEntity {

  @Autowired
  @Transient
  ServletContext servletContext;
  ...
}

为实体完成此操作后,您需要配置它们的包(或某些祖先包)以供 Spring 扫描,以便实体作为 bean 被拾取,并且它们的依赖关系自动连接。

<beans ... xmlns:context="..." >
  ...
  <context:component-scan base-package="pkg.of.your.jpa.entities" />
<beans>

编辑:(最终起作用的原因和原因)

  • 制作ServletContext 静态。(删除@Autowired

    @Transient
    private static ServletContext servletContext;
    

因为,JPA 正在创建一个单独的实体实例,即不使用 Spring 托管 bean,所以需要共享上下文。

  • 添加一个@PostConstruct init()方法。

    @PostConstruct
    public void init() {
        log.info("Initializing ServletContext as [" +
                    MyJPAEntity.servletContext + "]");
    }
    

init()一旦实体被实例化并通过引用内部,它就会触发,如果尚未注入ServletContext,它会强制注入静态属性。

  • 移动@Autowired实例方法但在里面设置静态字段。

    @Autowired
    public void setServletContext(ServletContext servletContext) {
        MyJPAEntity.servletContext = servletContext;
    }
    

引用我在下面的最后一条评论来回答为什么我们必须使用这些恶作剧:

由于 JPA 不使用 Spring 容器来实例化其实体,因此没有什么好方法可以做您想做的事。将 JPA 视为一个单独的 ORM 容器,它实例化和管理实体的生命周期(完全独立于 Spring),并且仅基于实体关系进行 DI。

于 2013-05-10T18:58:21.180 回答
2

很长一段时间后,我偶然发现了这个 SO 答案,这让我想到了一个优雅的解决方案:

  • 将您需要的所有 @Transient @Autowired 字段添加到您的实体中
  • 使用此自动装配字段创建 @Repository DAO: @Autowired private AutowireCapableBeanFactory autowirer;
  • 在您的 DAO 中,从 DB 获取实体后,调用此自动装配代码: String beanName = fetchedEntity.getClass().getSimpleName(); autowirer.autowireBean(fetchedEntity); fetchedEntity = (FetchedEntity) autowirer.initializeBean(fetchedEntity, beanName);

然后,您的实体将能够像任何 @Component 一样访问自动装配的字段。

于 2017-11-13T22:17:30.140 回答