Injecting spring bean in a JPA Entity Listener

2019-07-07 07:41发布

问题:

Am trying to make a JPA Entity Listener aware of the spring context by marking it as @Configurable. But the injected spring beans are null. Am able to make JPA entities aware of the Spring context using the same technique. Am using Spring(core and data-jpa) as infrastructure. Any ideas on how to acheive this using JPA Entity Listeners or spring data-jpa?

@Configurable
@Scope("singleton")
public class AggregateRootListener {
    private static Logger log = LoggerFactory.getLogger(AggregateRootListener.class);

    @Autowired
    private EventHandlerHelper eventHandlerHelper;

    @PostPersist
    @PostUpdate
    public void publishEvents(BaseAggregateRoot aggregateRoot){
        log.info(aggregateRoot.getEvents().toString());
        aggregateRoot.getEvents().stream()
            .forEach(event -> {
                eventHandlerHelper.notify(event, aggregateRoot);
                log.info("Publishing " + event + " " + aggregateRoot.toString());
            });
    }
}

and the BaseAggregateRoot code

@Configurable
@Scope("prototype")
@MappedSuperclass
@EntityListeners(AggregateRootListener.class)
public abstract class  BaseAggregateRoot extends BaseDomain{
    public static enum AggregateStatus {
        ACTIVE, ARCHIVE
    }

    @EmbeddedId
    @AttributeOverrides({
          @AttributeOverride(name = "aggregateId", column = @Column(name = "ID", nullable = false))})
    protected AggregateId aggregateId;



    @Version
    private Long version;
}

回答1:

Event Listener mechanism is a JPA concept and is implemented by the JPA provider. I don't think Spring creates event listener class instances - they are rather created by the JPA provider (Hibernate, EclipseLink, etc.). Therefore, the regular Spring injection would not work with event listener class instances. The author of this post seems to have come to the same conclusion.


That said, I do use Spring managed beans in JPA event listeners. The solution I use was developed to get hold of Spring bean instances in all classes that are not managed by Spring. It involves creating the following class:

@Component
public class SpringApplicationContext implements ApplicationContextAware {
  private static ApplicationContext CONTEXT;

  public void setApplicationContext(final ApplicationContext context)
              throws BeansException {
    CONTEXT = context;
  }

  public static <T> T getBean(Class<T> clazz) { return CONTEXT.getBean(clazz); }
}

This class caches the Spring application context at initial load. The context is then used to look up Spring managed beans.

Using the class is then as simple as SpringApplicationContext.getBean(FooService.class).

All the usual Spring semantics, such as, bean lifecycle, bean scope and transitive dependencies are taken care of.