Injection D'une dépendance de ressort dans un EntityListener

je suis en train pour injecter un Printemps dépendance dans un JPA EntityListener. Voici mon auditeur de la classe:

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


}

voici ma classe Entity:

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

cependant, ma dépendance (i.e. evenementPliRepository) est toujours nul.

quelqu'un Peut s'il vous plaît aider?

27
demandé sur balteo 2012-08-28 12:42:05

7 réponses

un hack pour injecter des dépendances sur des haricots apatrides, est de définir la dépendance comme "statique", créer une méthode de setter de sorte que le ressort puisse injecter la dépendance (en l'assignant à la dépendance statique).

Déclarer la dépendance comme statique.

static private EvenementPliRepository evenementPliRepository;

créer une méthode pour que Spring puisse l'injecter.

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

plus de détails à: http://blog-en.lineofsightnet.com/2012/08/dependency-injection-on-stateless-beans.html

17
répondu Juan Jimenez 2016-03-08 02:11:56

j'ai commencé à utiliser L'AOP pour injecter un haricot dans un auditeur D'entité. Après un jour et demi de recherche et d'essayer différentes choses, je suis tombé sur cela lien qui a déclaré:

il n'est pas possible d'injecter des fèves gérées par ressort dans une classe EntityListener de la JPA. C'est parce que le mécanisme d'écoute de JPA devrait être basé sur une classe apatride, de sorte que les méthodes sont effectivement statiques, et non contextuels conscients. ... Aucun montant de AOP will sauvez - vous, rien n'est injecté à l ‘ "objet" représentant l'auditeur, car les implémentations ne créent pas réellement d'instances, mais utilisent la méthode de classe.

a ce point je me suis regroupé et suis tombé sur L'Éclipselink DescriptorEventAdapter. En utilisant cette information j'ai créé une classe d'écoute qui a étendu l'Adaptateur de descripteur.

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

pour utiliser la classe j'aurais pu utiliser l'annotation @EntityListeners sur mon entity classe. Malheureusement, cette méthode ne permettrait pas au printemps de contrôler la création de mon auditeur et, par conséquent, ne permettrait pas l'injection de dépendances. Au lieu de cela, j'ai ajouté la fonction 'init' suivante à ma classe:

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

Ajouter un petit ressort de configuration XML

<!-- Define listener object -->
<bean id="entityListener" class="EntityListener " init-method="init">
    <property name="injectedValue" value="Hello World"/>
    <property name="entityManagerFactory" ref="emf"/>
</bean>  

maintenant nous avons une situation où le printemps crée un écouteur d'entity, l'injecte avec toutes les dépendances nécessaires, et l'objet écouteur s'enregistre avec la classe entity à laquelle il a l'intention de l'écouter.

j'espère que cette aide.

12
répondu jhadley 2013-05-08 18:23:38

C'est en fait une vieille question, mais j'ai trouvé une autre solution :

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

probablement pas le meilleur mais meilleur que les variables statiques avec AOP + tissage.

10
répondu Ludovic Guillaume 2017-01-03 16:14:03

et qu'en est-il de cette solution?

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

}

Puis l'Auditeur...

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

Et l'aide...

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

    }

Fonctionne pour moi.

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

9
répondu chuckedw 2015-10-09 14:57:06

j'ai testé l'approche proposée dans https://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/ et travaillé. Pas très propre, mais fait le travail. La classe autowirehelper légèrement modifiée pour moi ressemblait à ceci:

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

alors appelé ceci de l'écouteur d'entity comme ceci:

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;
    }
}
5
répondu Naymesh Mistry 2017-08-10 05:44:54

je crois que c'est parce que cet auditeur haricot n'est pas sous le contrôle de Printemps. Le printemps ne l'instancie pas, comment le printemps peut-il savoir trouver ce haricot et faire l'injection?

Je n'ai pas essayé sur cela, mais il semble que vous pouvez faire usage D'un tisserand AspectJ avec l'annotation Configurable de Spring pour avoir le contrôle de Spring non-Spring-instantiated haricot.

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

2
répondu Adrian Shum 2012-08-28 09:14:47

une autre option:

Créer un service pour faire de AplicationContext accessible:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import lombok.Setter;

@Service
class ContextWrapper {

    @Setter
    private static ApplicationContext context;

    @Autowired
    public ContextWrapper(ApplicationContext ac) {
        setContext(ac);
    }

    public static ApplicationContext getContext() {
        return context;
    }

}

Utilisation:

...    
public class AuditListener {

    private static final String AUDIT_REPOSITORY = "AuditRepository";

    @PrePersist
    public void beforePersist(Object object){
        //TODO:
    }

    @PreUpdate
    public void beforeUpdate(Object object){
        //TODO:
    }

    @PreRemove
    public void beforeDelete(Object object) {
        getRepo().save(getAuditElement("DEL",object));
    }

    private Audit getAuditElement(String Operation,Object object){

        Audit audit = new Audit();
        audit.setActor("test");
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        audit.setDate(timestamp);

        return audit;
    }

    private AuditRepository getRepo(){
        return ContextWrapper.getContext().getBean(AUDIT_REPOSITORY, AuditRepository.class);
    }
}

cette classe est créée en tant qu'auditeur à partir de jpa:

...
@Entity
@EntityListeners(AuditListener.class)
@NamedQuery(name="Customer.findAll", query="SELECT c FROM Customer c")
public class Customer implements Serializable {
    private static final long serialVersionUID = 1L;
...

depuis que L'auditeur n'est pas sous le contrôle de Spring, il ne peut pas accéder au bean de contexte. J'ai essayé plusieurs options (@Configurable (...)) et aucun n'a marché, sauf à créer une classe statique de l'accès au contexte. Déjà dans ce dilemme, je pense que c'est une option élégante.

0
répondu leon cio 2018-07-20 10:37:35