2012-08-28 198 views
20

我想注入Spring的依赖JPA EntityListener。这里是我的监听器类:注入的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); 
    } 


} 

这里是我的实体类:

@RooJavaBean 
@RooToString 
@RooJpaActiveRecord 
@EntityListeners(PliListener.class) 
public class Pli implements Serializable{ 
... 

然而,我的依赖(即evenementPliRepository总是空

任何人都可以请帮忙吗?

+0

参见http://stackoverflow.com/questions/8616146/eventlisteners-using- hibernate-4-0-with-spring-3-1-0-release – Ralph

+0

我也遇到同样的问题,并找到了解决方案,并在另一篇文章中回答了它http://stackoverflow.com/questions/22171221/how- to-inject-entitymanager-in-entitylisteners/42222592#42222592 –

回答

2

我相信这是因为这个监听bean不受Spring的控制。 Spring没有实例化它,Spring怎么知道如何找到这个bean并进行注入?

我还没有尝试过,但似乎你可以使用AspectJ Weaver和Spring的可配置注释来让Spring控制非Spring实例化的bean。

http://static.springsource.org/spring/docs/3.1.2.RELEASE/spring-framework-reference/html/aop.html#aop-using-aspectj

+0

你好,谢谢你阿德里安。我已将''添加到我的配置中。我在我的POM中有aop * .jars。我不认为我错过了任何东西。仍然'evenementPliRepository'是** null **。任何其他想法,我可能会失踪? – balteo

+0

你有经纪人吗?引用自ref:'进一步说,在某些环境中,这种支持可以加载时编织,而不需要修改应用服务器的启动脚本,这需要添加-javaagent:path/to/aspectjweaver.jar或在本节后面)-javaagent:path/to/org.springframework.instrument- {version} .jar(以前称为spring-agent.jar)。“但说实话,我之前没有真正尝试过这个功能,可能这是我不知道的其他限制 –

+0

我要检查。我也意识到,因为我使用Spring Roo,所以Roo已经设置了“@ Configurable”。还有什么让我觉得,我没有得到关于依赖性问题的警告...... – balteo

13

一个黑客注入的无状态bean依赖,是定义为依赖“静态”,创建一个setter方法,使Spring能够注入的依赖(将其分配到静态依赖)。

声明依赖关系为静态。

static private EvenementPliRepository evenementPliRepository; 

创建一个方法,以便Spring可以注入它。

@Autowired 
public void init(EvenementPliRepository evenementPliRepository) 
{ 
    MyListenerClass.evenementPliRepository = evenementPliRepository; 
    logger.info("Initializing with dependency ["+ evenementPliRepository +"]"); 
} 

更多细节在:http://blog-en.lineofsightnet.com/2012/08/dependency-injection-on-stateless-beans.html

+0

@SotiriosDelimanolis这实际上适用于任何人吗?我已经尝试了这一点,并将“@Autowired”放置在setter上,并且bean仍然为空。我已验证'@PostConstruct'方法正在被调用。 – testing123

+0

@SotiriosDelimanolis当然,我发现了很多方法,而且这似乎是最容易做到的。我只是想确保我没有疯狂,因为它已经提高了6倍,所以我想也许人们已经成功了。不要赞扬一些甚至没有工作的人:)。我只会尝试其他方法之一。谢谢。 – testing123

+0

有助于downvote它:P –

11

我开始下井使用AOP到一个Spring bean注入到实体监听的路径。一天的研究和尝试不同的事物半后,我遇到了这个link其中指出:

这是不可能注入Spring管理豆成一个JPA EntityListener类。这是因为JPA侦听器机制应该基于无状态类,所以这些方法实际上是静态的,并且是非上下文感知的。 ...没有多少AOP会节省你,没有任何东西被注入到表示监听器的'对象'中,因为这些实现实际上并不创建实例,而是使用类方法。

在这一点上,我重新集结并偶然发现了EclipseLink DescriptorEventAdapter。使用这些信息,我创建了一个扩展了描述符适配器的侦听器类。

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 
    } 
} 

为了使用类我可以用我的实体类的@EntityListeners注解。不幸的是,这种方法不允许Spring控制我的侦听器的创建,因此不允许依赖注入。相反,我增加了以下“初始化”功能,我的课:

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(); 
     } 
    } 
} 

加少许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> 

现在我们这里春创造一个实体监听的情况下,与任何依赖注入其是需要的,并且侦听器对象将自己注册到它想要侦听的实体类。

我希望这会有所帮助。

8

那么这个解决方案呢?

@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; 

} 

然后监听...

@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); 
    } 
} 

和辅助...

/** 
    * 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; 
     } 

    } 

为我工作。

来源: http://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/

+0

如果EntityListener用于ApplicationContext本身的初始化,那么这个解决方案有一个问题;那么你必须确保'AutowireHelper' bean在使用它的代码之前被初始化。 – holmis83

+0

Works,但只有在config xml或Java Spring配置中注册AutowireHelper之后: @Bean public AutowireHelper autowireHelper(){ return AutowireHelper.getInstance(); } – ashes

7

这其实是一个老问题,但我找到了一个替代的解决方案:

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)); 
    } 
} 

可能不是最好的之一,但不是静态变量更好的W/O AOP +编织。

+0

如何将该侦听器附加到实际实体而不使用xml? – Antoniossss

+0

在相关实体上添加'@EntityListeners(MyEntityListener.class)'。 –

+0

不能做 - 模型在单独的JAR中。我的意思是如何在运行时不使用XML。 – Antoniossss

3

我测试了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; 
    } 
} 

然后把这种从实体监听器是这样的:

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; 
    } 
}