1

I am currently working on a project where I need to audit my Entities. To do this we are using an Entity Listener configured in an xml file (This was required by the architecture of the project, no annotations were allowed) : see this documentation

We are also working using DDD (Domain Driven Design) so our application is using Domain/Application/Infrastructure layers and also WebApp (The latter allows you to start the application and do requests on it).

Here is my entity configuration :

<entity-mappings version="2.1"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd" >
    <persistence-unit-metadata>
        <persistence-unit-defaults>
            <entity-listeners>
                <entity-listener class="com.company.infra.application.service.impl.MyServiceASImpl">
                    <post-persist method-name="handlePostPersist"/>
                    <post-update method-name="handlePostUpdate"/>
                    <pre-remove method-name="handlePreRemove"/>
                </entity-listener>
            </entity-listeners>
        </persistence-unit-defaults>
    </persistence-unit-metadata>
</entity-mappings>

This is actually working. The methods located in MyServiceASImpl are correctly called. The problem is that the different services available as @Autowired are not injected correctly and I get a null pointer exception when they are used.

Here is the code of MyServiceASImpl :

package com.company.infra.application.service.impl;
// ... Different necesssary imports
@ApplicationService // This is a custom annotation
@Transactional(propagation = Propagation.REQUIRED) // To propagate exceptions
public class MyServiceASImpl implements MyServiceAS {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyServiceASImpl.class);

    @Autowired
    private com.company.domain.service.MyServiceDS mysServiceDS; // This is in the domain layer

    @Autowired
    private TransversalService transversalService;

    @Autowired
    private com.company.domain.model.myRepository myRepository; // Interface in domain layer but implementation in infrastructure layer

    // ... Other methods

    public void handlePostPersist() {
        // Calls to on of the services
    }

    public void handlePostUpdate() {
        // Calls to on of the services
    }

    public void handlePreRemove() {
        // Calls to on of the services
    }
}

I have several xml configuration to do component-scan on the necessary packages. Furthermore, if this service is called from a webservice contained in the webapp layer, the dependencies are injected correctly.

But, when the Entity Listener is triggered and class theses, all three dependencies are null.

Has anyone encountered this problem ? How can autowire the dependencies correctly ?

Don't hesitate to ask for more details. Thanks for your help.

matthieusb
  • 505
  • 1
  • 10
  • 34

2 Answers2

4

Since the entity listener is not managed by Spring, you cannot use @Autowired. So you need to use a static injection:

public class MyServiceASImpl implements MyServiceAS {    

    public void preRemove(final Object objet) {
        ApplicationContextProvider.getApplicationContext().getBean(MyRepository.class)
            .gererAuditCasPreSuppression(objet);
    }
}

Make sure to modify your xml file in order to call the methods.

You will have to right an application context provider :

package leclerc.wrf.commun;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class ApplicationContextProvider implements ApplicationContextAware {

    private static ApplicationContext context;

    protected ApplicationContextProvider() {
    }

    @Override
    public void setApplicationContext(final ApplicationContext applicationContext) {
        ApplicationContextProvider.context = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return ApplicationContextProvider.context;
    }

}
Sébi11
  • 56
  • 3
1

I'm afraid but this will not work because the entity listener is managed by JPA and the Spring dependency injection will not work.

Please have a look at the solutions mentioned here:

Injecting a Spring dependency into a JPA EntityListener

Simon Martinelli
  • 34,053
  • 5
  • 48
  • 82