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?
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
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.
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.
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/
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;
}
}
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.
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.