在JPA @ Entity里注入Bean

是否有可能使用Spring的dependency injection将bean注入JPA @ @Entity

我试图@Autowire ServletContext,但是,当服务器启动成功,我试图访问bean属性时收到一个NullPointerException。

 @Autowired @Transient ServletContext servletContext; 

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

正如你现在所意识到的那样,除非使用@Configurable和适当的AspectJ编织configuration,否则Spring不会将dependency injection到使用new运算符创build的对象中。 事实上,除非你从ApplicationContext检索它们,否则它不会将dependency injection到对象中,因为它简单地不知道它们的存在。 即使你用@Component注解你的实体,实体的实例仍然会由一个new操作执行,或者你或者一个像Hibernate这样的框架。 请记住,注释只是元数据:如果没有人解释这个元数据,它不会添加任何行为或对正在运行的程序有任何影响。

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

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

使用注释,你可以用@Component标记你的实体(或者一个更具体的原型@Repository ,它可以为DAO启用自动exception转换,并且可能会干扰JPA)。

 @Entity @Component public class MyJAPEntity { @Autowired @Transient ServletContext servletContext; ... } 

一旦你完成了你的实体,你需要configuration他们的包(或者一些祖先包),以便被Spring扫描,这样实体就可以作为bean被获取,并且它们的依赖关系会被自动连线。

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

编辑 :(什么终于工作,为什么)

  • 使ServletContext 静态 。 (删除@Autowired

     @Transient private static ServletContext servletContext; 

由于JPA正在创build一个独立的实体实例,即不使用Spring托pipebean,所以需要共享上下文

  • 添加@PostConstruct init()方法。

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

一旦Entity被实例化,并且通过引用ServletContext ,它将触发init() ,如果不是已经注入的话,它会强制注入静态属性。

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

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

引用我最后的评论来回答为什么我们不得不雇用这些诡计:

因为JPA没有使用Spring容器来实例化它的实体,所以没有什么好办法做你想做的事情。 将JPA想象成一个单独的ORM容器,它实例化和pipe理实体的生命周期(与Spring完全分离),并且仅基于实体关系进行DI。

经过很长一段时间,我偶然发现了这个答案 ,使我想到了一个优雅的解决scheme:

  • 添加到您的实体所有您需要的@Transient @Autowired字段
  • 使用这个自动编写的字段创build一个@Repository DAO: @Autowired private AutowireCapableBeanFactory autowirer;
  • 从DAO中,从DB中获取实体后,调用以下自动assembly代码: String beanName = fetchedEntity.getClass().getSimpleName(); autowirer.autowireBean(fetchedEntity); fetchedEntity = (FetchedEntity) autowirer.initializeBean(fetchedEntity, beanName); String beanName = fetchedEntity.getClass().getSimpleName(); autowirer.autowireBean(fetchedEntity); fetchedEntity = (FetchedEntity) autowirer.initializeBean(fetchedEntity, beanName);

然后,您的实体将可以像任何@Component一样访问autowired字段。