spring JPA @Entity 内的 Bean 注入
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16471636/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Bean injection inside a JPA @Entity
提问by theblang
Is it possible to inject beans to a JPA @Entityusing Spring's dependency injection?
是否可以@Entity使用 Spring 的依赖注入将 bean 注入 JPA ?
I attempted to @Autowire ServletContext but, while the server did start successfully, I received a NullPointerException when trying to access the bean property.
我尝试 @Autowire ServletContext 但是,虽然服务器确实启动成功,但我在尝试访问 bean 属性时收到了 NullPointerException。
@Autowired
@Transient
ServletContext servletContext;
回答by Spiff
You can inject dependencies into objects not managed by the Spring container using @Configurableas explained here: http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-atconfigurable.
您可以使用以下@Configurable说明将依赖项注入不受 Spring 容器管理的对象:http: //static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-atconfigurable.
As you've realized by now, unless using the @Configurableand appropriate AspectJ weaving configuration, Spring does not inject dependencies into objects created using the newoperator. In fact, it doesn't inject dependencies into objects unless you've retrieved them from the ApplicationContext, for the simple reason that it simply doesn't know about their existence. Even if you annotate your entity with @Component, instantiation of that entity will still be performed by a newoperation, either by you or a framework such as Hibernate. Remember, annotations are just metadata: if no one interprets that metadata, it does not add any behaviour or have any impact on a running program.
正如您现在已经意识到的那样,除非使用@Configurable适当的 AspectJ 编织配置,否则Spring 不会将依赖项注入到使用new操作符创建的对象中。事实上,它不会将依赖项注入对象,除非您从 中检索它们ApplicationContext,原因很简单,它根本不知道它们的存在。即使您使用 注释您的实体@Component,该实体的实例化仍将new由您或 Hibernate 等框架的操作执行。请记住,注释只是元数据:如果没有人解释该元数据,它不会添加任何行为或对正在运行的程序产生任何影响。
All that being said, I strongly advise against injecting a ServletContextinto an entity. Entities are part of your domain model and should be decoupled from any delivery mechanism, such as a Servlet-based web delivery layer. How will you use that entity when it's accessed by a command-line client or something else not involving a ServletContext? You should extract the necessary data from that ServletContext and pass it through traditional method arguments to your entity. You will achieve a much better design through this approach.
尽管如此,我强烈建议不要将 aServletContext注入实体。实体是域模型的一部分,应该与任何交付机制分离,例如基于 Servlet 的 Web 交付层。当它被命令行客户端或其他不涉及 ServletContext 的东西访问时,您将如何使用该实体?您应该从该 ServletContext 中提取必要的数据,并将其通过传统方法参数传递给您的实体。通过这种方法,您将获得更好的设计。
回答by Ravi Thapliyal
Yes, of course you can. You just need to make sure the entity is also registered as a Spring managed bean either declaratively using <bean>tags (in some spring-context.xml) or through annotations as shown below.
是的,当然可以。您只需要确保实体也注册为 Spring 管理的 bean,或者使用<bean>标记(在某些 spring-context.xml 中)声明性地或通过如下所示的注释进行注册。
Using annotations, you can either mark your entities with @Component(or a more specific stereotype @Repositorywhich enables automatic exception translation for DAOs and may or may not interfere with JPA).
使用注释,您可以使用@Component(或更具体的构造型标记您的实体,@Repository该构造型可为 DAO 启用自动异常转换,并且可能会或可能不会干扰 JPA)。
@Entity
@Component
public class MyJAPEntity {
@Autowired
@Transient
ServletContext servletContext;
...
}
Once you've done that for your entities you need to configure their package (or some ancestor package) for being scanned by Spring so that the entities get picked up as beans and their dependencies get auto wired.
为实体完成此操作后,您需要配置它们的包(或某个祖先包)以供 Spring 扫描,以便实体作为 bean 被拾取,并且它们的依赖项会自动连接。
<beans ... xmlns:context="..." >
...
<context:component-scan base-package="pkg.of.your.jpa.entities" />
<beans>
EDIT: (what finally worked and why)
编辑:(什么最终有效以及为什么)
Making the
ServletContextstatic. (remove @Autowired)@Transient private static ServletContext servletContext;
制作
ServletContext静态. (删除@Autowired)@Transient private static ServletContext servletContext;
Since, JPA is creating a separate entity instance i.e. not using the Spring managed bean, it's required for the contextto be shared.
由于 JPA 正在创建一个单独的实体实例,即不使用 Spring 管理的 bean,因此需要共享上下文。
Adding a @PostConstruct
init()method.@PostConstruct public void init() { log.info("Initializing ServletContext as [" + MyJPAEntity.servletContext + "]"); }
添加@PostConstruct
init()方法。@PostConstruct public void init() { log.info("Initializing ServletContext as [" + MyJPAEntity.servletContext + "]"); }
This fires init()once the Entity has been instantiated and by referencing ServletContextinside, it forces the injection on the staticproperty if not injected already.
init()一旦 Entity 被实例化并通过ServletContext在内部引用,它就会触发,如果尚未注入,它会强制注入静态属性。
Moving
@Autowiredto an instancemethod but setting the staticfield inside.@Autowired public void setServletContext(ServletContext servletContext) { MyJPAEntity.servletContext = servletContext; }
移动
@Autowired到一个实例方法,但在里面设置静态字段。@Autowired public void setServletContext(ServletContext servletContext) { MyJPAEntity.servletContext = servletContext; }
Quoting my last comment below to answer why do we have to employ these shenanigans:
引用我在下面的最后一条评论来回答为什么我们必须使用这些恶作剧:
There's no pretty way of doing what you want since JPA doesn't use the Spring container to instantiate its entities. Think of JPA as a separate ORM container that instantiates and manages the lifecycle of entities (completely separate from Spring) and does DI based on entity relationships only.
由于 JPA 不使用 Spring 容器来实例化其实体,因此没有什么很好的方法可以做您想做的事情。将 JPA 视为一个单独的 ORM 容器,它实例化和管理实体的生命周期(与 Spring 完全分离)并且仅基于实体关系进行 DI。
回答by xtian
After a long time I stumbled across this SO answerthat made me think of an elegant solution:
很长一段时间后,我偶然发现了这个让我想到了一个优雅解决方案的SO 答案:
- Add to your entities all the @Transient @Autowired fields you need
- Make a @Repository DAO with this autowired field:
@Autowired private AutowireCapableBeanFactory autowirer; - From your DAO, after fetching the entity from DB, call this autowiring code:
String beanName = fetchedEntity.getClass().getSimpleName(); autowirer.autowireBean(fetchedEntity); fetchedEntity = (FetchedEntity) autowirer.initializeBean(fetchedEntity, beanName);
- 将您需要的所有 @Transient @Autowired 字段添加到您的实体
- 使用这个自动装配的字段创建一个 @Repository DAO:
@Autowired private AutowireCapableBeanFactory autowirer; - 在您的 DAO 中,从 DB 获取实体后,调用此自动装配代码:
String beanName = fetchedEntity.getClass().getSimpleName(); autowirer.autowireBean(fetchedEntity); fetchedEntity = (FetchedEntity) autowirer.initializeBean(fetchedEntity, beanName);
Your entity will then be able to access the autowired fields as any @Component can.
然后,您的实体将能够像任何 @Component 一样访问自动装配的字段。

