Spring JPA@实体内的Bean注入
是否可以使用Spring的依赖项注入将bean注入JPASpring JPA@实体内的Bean注入,spring,spring-mvc,jpa,Spring,Spring Mvc,Jpa,是否可以使用Spring的依赖项注入将bean注入JPA@实体 我尝试@Autowire ServletContext,但当服务器成功启动时,我在尝试访问bean属性时收到一个NullPointerException @Autowired @Transient ServletContext servletContext; 是的,当然可以。您只需要确保实体也注册为Spring管理的bean,可以使用标记(在某些Spring context.xml中)声明性地注册,也可以通过如下所示的注释注册 使
@实体
我尝试@Autowire ServletContext,但当服务器成功启动时,我在尝试访问bean属性时收到一个NullPointerException
@Autowired
@Transient
ServletContext servletContext;
是的,当然可以。您只需要确保实体也注册为Spring管理的bean,可以使用
标记(在某些Spring context.xml中)声明性地注册,也可以通过如下所示的注释注册
使用注释,您可以使用@Component
(或更具体的原型@Repository
,该原型支持DAO的自动异常转换,并且可能会干扰JPA,也可能不会干扰JPA)
一旦您为您的实体完成了这项工作,您就需要配置它们的包(或某个祖先包)以供Spring扫描,这样实体就可以作为bean被提取,它们的依赖关系就可以自动连接
<beans ... xmlns:context="..." >
...
<context:component-scan base-package="pkg.of.your.jpa.entities" />
<beans>
由于JPA正在创建一个单独的实体实例,即不使用Spring托管bean,因此需要共享上下文
- 添加@PostConstruct
方法init()
@PostConstruct public void init() { log.info("Initializing ServletContext as [" + MyJPAEntity.servletContext + "]"); }
ServletContext
触发init()
- 将
@Autowired
移动到实例方法,但在其中设置静态字段
@Autowired
public void setServletContext(ServletContext servletContext) {
MyJPAEntity.servletContext = servletContext;
}
下面引用我的最后一句话来回答为什么我们要使用这些恶作剧:
因为JPA不使用Spring容器来实例化它的实体,所以没有很好的方法来做您想要做的事情。将JPA视为一个单独的ORM容器,它实例化和管理实体的生命周期(与Spring完全分离),并且只基于实体关系进行DI
您可以使用@Configurable
将依赖项注入到Spring容器未管理的对象中,如下所述:
正如您现在已经意识到的,除非使用@Configurable
和适当的AspectJ编织配置,否则Spring不会将依赖项注入到使用new
操作符创建的对象中。事实上,它不会将依赖项注入到对象中,除非您从ApplicationContext
检索到它们,原因很简单,它根本不知道它们的存在。即使您使用@组件
对实体进行注释,该实体的实例化仍将由新建
操作执行,可以由您执行,也可以由诸如Hibernate之类的框架执行。请记住,注释只是元数据:如果没有人解释元数据,它不会添加任何行为,也不会对正在运行的程序产生任何影响
尽管如此,我强烈建议不要将ServletContext
注入实体。实体是域模型的一部分,应该与任何交付机制(如基于Servlet的web交付层)分离。当命令行客户端或其他不涉及ServletContext的对象访问该实体时,您将如何使用该实体?您应该从该ServletContext提取必要的数据,并通过传统的方法参数将其传递给实体。通过这种方法,您将获得更好的设计。经过很长一段时间,我无意中发现了一个让我想到了一个优雅的解决方案:
- 将所需的所有@Transient@Autowired字段添加到实体中
- 使用此自动连接字段创建@Repository DAO:
@自动连线专用自动连线功能BeanFactory自动连线器;
- 从DAO中,从DB获取实体后,调用以下自动连接代码:
字符串beanName=fetchedEntity.getClass().getSimpleName();
autowire.autowireBean(fetchedEntity);
fetchedEntity=(fetchedEntity)autowire.initializeBean(fetchedEntity,beanName);
然后,您的实体将能够像任何@Component一样访问自动连接字段。不幸的是,在将@Component
添加到实体和
添加到app config.xml
后,servletContext
仍然为空。请将其设置为静态<代码>私有静态ServletContext ServletContext
如果它仍然是null
则通过定义@PostConstruct public void init(){log.info(“将ServletContext初始化为[“+MyJPAEntity.ServletContext+”]);}
希望这会有所帮助。将它设置为静态后仍然是null。我想我对@PostConstruct
的功能感到困惑。@PostConstruct
一旦实体被实例化,就会触发init(),并通过引用其中的servletContext,强制为创建的实例注入(现在是静态的)属性<代码>静态I/CODE >以确保它是共享的,因为JPA正在创建一个单独的实体实例,即不使用Spring托管bean。当您必须在JPA实体中注入其他bean时,我认为它是代码气味,我认为最好重新考虑重新设计实体及其周围的(包私有)类,这两个问题的答案都是一个有用的SO线程的完美例子(我两个都投了赞成票)。虽然@RaviThapliyal下面的答案提供了一种实现结果的方法,但您的答案提供了背景知识(对我来说,这是一种“啊哈”——帮助我将过去几周读到的关于Spring的DI的所有理论信息转化为实际应用知识的体验)。在我看来,这样的帖子实在是太多了,因为它们确实教会了你一些东西(而且往往不会为你赢得太多的声誉)。@sthzg当我情绪低落,需要重新找回信心时,我会回到这里,阅读你的评论。谢谢。在将EntityManager注入EntityListener时,我也遇到了同样的问题,并找到了解决方案
@PostConstruct
public void init() {
log.info("Initializing ServletContext as [" +
MyJPAEntity.servletContext + "]");
}
@Autowired
public void setServletContext(ServletContext servletContext) {
MyJPAEntity.servletContext = servletContext;
}