在JPA @Entity中注入Bean

时间:2013-05-09 21:43:38

标签: spring spring-mvc jpa

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

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

@Autowired
@Transient
ServletContext servletContext;

3 个答案:

答案 0 :(得分:39)

您可以使用@Configurable将依赖项注入不受Spring容器管理的对象,如下所述: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等框架。请记住,注释只是元数据:如果没有人解释该元数据,它不会添加任何行为或对正在运行的程序产生任何影响。

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

答案 1 :(得分:20)

是的,当然可以。您只需确保实体也注册为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,所以需要共享 context

  • 添加 @PostConstruct init()方法。

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

一旦实体被实例化并且通过引用内部init(),它会激活ServletContext,如果没有注入,它会强制注入 static 属性。

  • @Autowired移至实例方法,但在其中设置静态字段。

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

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

  

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

答案 2 :(得分:1)

经过很长一段时间,我偶然发现this SO answer让我想到了一个优雅的解决方案:

  • 将您需要的所有@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一样访问自动连接的字段。