bal*_*teo 43 spring dependency-injection jpa spring-roo entitylisteners
我试图将Spring依赖注入到JPA EntityListener中.这是我的听众课程:
@Configurable(autowire = Autowire.BY_TYPE, dependencyCheck = true)
public class PliListener {
@Autowired
private EvenementPliRepository evenementPliRepository;
@PostPersist
void onPostPersist(Pli pli) {
EvenementPli ev = new EvenementPli();
ev.setPli(pli);
ev.setDateCreation(new Date());
ev.setType(TypeEvenement.creation);
ev.setMessage("Création d'un pli");
System.out.println("evenementPliRepository: " + evenementPliRepository);
evenementPliRepository.save(ev);
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的Entity类:
@RooJavaBean
@RooToString
@RooJpaActiveRecord
@EntityListeners(PliListener.class)
public class Pli implements Serializable{
...
Run Code Online (Sandbox Code Playgroud)
但是,我的依赖(即evenementPliRepository)始终为null.
有人可以帮忙吗?
Jua*_*nez 30
注入依赖于无状态bean的hack是将依赖项定义为"static",创建一个setter方法,以便Spring可以注入依赖项(将其分配给静态依赖项).
将依赖项声明为static.
static private EvenementPliRepository evenementPliRepository;
Run Code Online (Sandbox Code Playgroud)
创建一个方法,以便Spring可以注入它.
@Autowired
public void init(EvenementPliRepository evenementPliRepository)
{
MyListenerClass.evenementPliRepository = evenementPliRepository;
logger.info("Initializing with dependency ["+ evenementPliRepository +"]");
}
Run Code Online (Sandbox Code Playgroud)
更多详情请访问:http://blog-en.lineofsightnet.com/2012/08/dependency-injection-on-stateless-beans.html
Lud*_*ume 21
这实际上是一个老问题,但我找到了另一种解决方案:
public class MyEntityListener {
@Autowired
private ApplicationEventPublisher publisher;
@PostPersist
public void postPersist(MyEntity target) {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
publisher.publishEvent(new OnCreatedEvent<>(this, target));
}
@PostUpdate
public void postUpdate(MyEntity target) {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
publisher.publishEvent(new OnUpdatedEvent<>(this, target));
}
@PostRemove
public void postDelete(MyEntity target) {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
publisher.publishEvent(new OnDeletedEvent<>(this, target));
}
}
Run Code Online (Sandbox Code Playgroud)
可能不是最好的,但比没有AOP +编织的静态变量更好.
chu*_*edw 14
那个解决方案怎么样?
@MappedSuperclass
@EntityListeners(AbstractEntityListener.class)
public abstract class AbstractEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Long id;
@Column(name = "creation_date")
private Date creationDate;
@Column(name = "modification_date")
private Date modificationDate;
}
Run Code Online (Sandbox Code Playgroud)
那么听众......
@Component
public class AbstractEntityListener {
@Autowired
private DateTimeService dateTimeService;
@PreUpdate
public void preUpdate(AbstractEntity abstractEntity) {
AutowireHelper.autowire(this, this.dateTimeService);
abstractEntity.setModificationDate(this.dateTimeService.getCurrentDate());
}
@PrePersist
public void prePersist(AbstractEntity abstractEntity) {
AutowireHelper.autowire(this, this.dateTimeService);
Date currentDate = this.dateTimeService.getCurrentDate();
abstractEntity.setCreationDate(currentDate);
abstractEntity.setModificationDate(currentDate);
}
}
Run Code Online (Sandbox Code Playgroud)
帮帮人......
/**
* Helper class which is able to autowire a specified class. It holds a static reference to the {@link org
* .springframework.context.ApplicationContext}.
*/
public final class AutowireHelper implements ApplicationContextAware {
private static final AutowireHelper INSTANCE = new AutowireHelper();
private static ApplicationContext applicationContext;
private AutowireHelper() {
}
/**
* Tries to autowire the specified instance of the class if one of the specified beans which need to be autowired
* are null.
*
* @param classToAutowire the instance of the class which holds @Autowire annotations
* @param beansToAutowireInClass the beans which have the @Autowire annotation in the specified {#classToAutowire}
*/
public static void autowire(Object classToAutowire, Object... beansToAutowireInClass) {
for (Object bean : beansToAutowireInClass) {
if (bean == null) {
applicationContext.getAutowireCapableBeanFactory().autowireBean(classToAutowire);
}
}
}
@Override
public void setApplicationContext(final ApplicationContext applicationContext) {
AutowireHelper.applicationContext = applicationContext;
}
/**
* @return the singleton instance.
*/
public static AutowireHelper getInstance() {
return INSTANCE;
}
}
Run Code Online (Sandbox Code Playgroud)
适合我.
资料来源:http: //guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/
小智 14
从 Spring V5.1(和 Hibernate V5.3)开始,它应该开箱即用,因为 Spring 注册为这些类的提供者。查看SpringBeanContainer 的文档
jha*_*ley 13
我开始沿着使用AOP将spring bean注入Entity监听器的道路走下去.经过一天半的研究和尝试不同的事情,我遇到了这个链接,其中说:
无法将spring托管bean注入JPA EntityListener类.这是因为JPA侦听器机制应该基于无状态类,因此这些方法实际上是静态的,并且非上下文感知....没有任何AOP会保存你,没有任何东西被注入到代表监听器的'对象'中,因为实现实际上并不创建实例,而是使用类方法.
此时,我重新组合并偶然发现了EclipseLink DescriptorEventAdapter.使用这些信息,我创建了一个扩展Descriptor Adapter的监听器类.
public class EntityListener extends DescriptorEventAdapter {
private String injectedValue;
public void setInjectedValue(String value){
this.injectedValue = value;
}
@Override
public void aboutToInsert(DescriptorEvent event) {
// Do what you need here
}
}
Run Code Online (Sandbox Code Playgroud)
为了使用该类,我可以在我的实体类上使用@EntityListeners注释.不幸的是,这种方法不允许Spring控制我的监听器的创建,因此不允许依赖注入.相反,我将以下'init'函数添加到我的类:
public void init() {
JpaEntityManager entityManager = null;
try {
// Create an entity manager for use in this function
entityManager = (JpaEntityManager) entityManagerFactory.createEntityManager();
// Use the entity manager to get a ClassDescriptor for the Entity class
ClassDescriptor desc =
entityManager.getSession().getClassDescriptor(<EntityClass>.class);
// Add this class as a listener to the class descriptor
desc.getEventManager().addListener(this);
} finally {
if (entityManager != null) {
// Cleanup the entity manager
entityManager.close();
}
}
}
Run Code Online (Sandbox Code Playgroud)
添加一点Spring XML配置
<!-- Define listener object -->
<bean id="entityListener" class="EntityListener " init-method="init">
<property name="injectedValue" value="Hello World"/>
<property name="entityManagerFactory" ref="emf"/>
</bean>
Run Code Online (Sandbox Code Playgroud)
现在我们有一种情况,Spring创建一个实体监听器,为它注入所需的任何依赖关系,并且监听器对象将自己注册到它想要监听的实体类.
我希望这有帮助.
我用@Component注释对侦听器进行注释,然后创建了一个非静态的setter来分配注入的Spring bean,它运行良好
我的代码如下:
@Component
public class EntityListener {
private static MyService service;
@Autowired
public void setMyService (MyService service) {
this.service=service;
}
@PreUpdate
public void onPreUpdate() {
service.doThings()
}
@PrePersist
public void onPersist() {
...
}
}
Run Code Online (Sandbox Code Playgroud)
我测试了https://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/中建议的方法并且工作了.不是很干净,但做的工作.对我来说稍微修改过的AutowireHelper类看起来像这样:
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class AutowireHelper implements ApplicationContextAware {
private static ApplicationContext applicationContext;
private AutowireHelper() {
}
public static void autowire(Object classToAutowire) {
AutowireHelper.applicationContext.getAutowireCapableBeanFactory().autowireBean(classToAutowire);
}
@Override
public void setApplicationContext(final ApplicationContext applicationContext) {
AutowireHelper.applicationContext = applicationContext;
}
}
Run Code Online (Sandbox Code Playgroud)
然后从实体监听器中调用它,如下所示:
public class MyEntityAccessListener {
@Autowired
private MyService myService;
@PostLoad
public void postLoad(Object target) {
AutowireHelper.autowire(this);
myService.doThings();
...
}
public void setMyService(MyService myService) {
this.myService = myService;
}
}
Run Code Online (Sandbox Code Playgroud)
JPA 侦听器的问题在于:
它们不是由 Spring 管理的(所以没有注入)
它们是(或可能)在Spring 的应用程序上下文准备好之前创建的(因此我们不能在构造函数调用中注入 bean)
我处理这个问题的解决方法:
1)创建Listener具有公共静态LISTENERS字段的类:
public abstract class Listener {
// for encapsulation purposes we have private modifiable and public non-modifiable lists
private static final List<Listener> PRIVATE_LISTENERS = new ArrayList<>();
public static final List<Listener> LISTENERS = Collections.unmodifiableList(PRIVATE_LISTENERS);
protected Listener() {
PRIVATE_LISTENERS.add(this);
}
}
Run Code Online (Sandbox Code Playgroud)
2) 我们要添加的所有 JPA 侦听器Listener.LISTENERS都必须扩展此类:
public class MyListener extends Listener {
@PrePersist
public void onPersist() {
...
}
...
}
Run Code Online (Sandbox Code Playgroud)
3) 现在我们可以在 Spring 的 Application Context 准备好之后获取所有监听器并注入 bean
@Component
public class ListenerInjector {
@Autowired
private ApplicationContext context;
@EventListener(ContextRefreshedEvent.class)
public void contextRefreshed() {
Listener.LISTENERS.forEach(listener -> context.getAutowireCapableBeanFactory().autowireBean(listener));
}
}
Run Code Online (Sandbox Code Playgroud)
尝试使用ObjectFactory这样的
@Configurable
public class YourEntityListener {
@Autowired
private ObjectFactory<YourBean> yourBeanProvider;
@PrePersist
public void beforePersist(Object target) {
YourBean yourBean = yourBeanProvider.getObject();
// do somthing with yourBean here
}
}
Run Code Online (Sandbox Code Playgroud)
我org.springframework.data.jpa.domain.support.AuditingEntityListener从 spring-data-jpa 中找到了这个解决方案。
演示:https : //github.com/eclipseAce/inject-into-entity-listener
基于 Paulo Merson 的答案,这里是如何使用JpaBaseConfiguration. 以下是两个步骤:
步骤 1:将监听器定义为 Spring 组件。请注意,自动装配通过构造函数注入进行。
@Component
public class PliListener {
private EvenementPliRepository evenementPliRepository;
public PliListener(EvenementPliRepository repo) {
this.evenementPliRepository = repo;
}
@PrePersist
public void touchForCreate(Object target) {
// ...
}
@PostPersist
void onPostPersist(Object target) {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
第 2 步:设置SpringBeanContainer,这会在侦听器中启用自动装配。SpringBeanContainer JavaDoc可能值得一看。
@Configuration
public class JpaConfig extends JpaBaseConfiguration {
@Autowired
private ConfigurableListableBeanFactory beanFactory;
protected JpaConfig(DataSource dataSource, JpaProperties properties,
ObjectProvider<JtaTransactionManager> jtaTransactionManager) {
super(dataSource, properties, jtaTransactionManager);
}
@Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
@Override
protected Map<String, Object> getVendorProperties() {
Map<String, Object> props = new HashMap<>();
// configure use of SpringBeanContainer
props.put(org.hibernate.cfg.AvailableSettings.BEAN_CONTAINER,
new SpringBeanContainer(beanFactory));
return props;
}
}
Run Code Online (Sandbox Code Playgroud)
@Lazy 应该可以解决问题
@Component
public class MyEntityListener {
@Lazy
@Autowired
private ApplicationEventPublisher publisher;
}
Run Code Online (Sandbox Code Playgroud)
我相信这是因为这个监听器 bean 不受 Spring 的控制。Spring没有实例化它,Spring如何知道如何找到该bean并进行注入?
我还没有尝试过,但似乎您可以利用 AspectJ Weaver 和 Spring 的 Configurable 注释来让 Spring 控制非 Spring 实例化的 bean。
| 归档时间: |
|
| 查看次数: |
35168 次 |
| 最近记录: |