在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字段。