Soru Bir JPA EntityListener'a Spring bağımlılığının enjekte edilmesi


deniyorum Bahar bağımlılığı enjekte Içine JPA EntityListener. İşte dinleyici sınıfım:

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


}

İşte Entity sınıfım:

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

Ancak bağımlılığım (ör. evenementPliRepository) her zaman null.

Herkes yardım edebilir mi?


25
2017-08-28 08:42


Menşei


Ayrıca bakınız stackoverflow.com/questions/8616146/... - Ralph
Aynı problemle karşı karşıyaydım ve bir çözüm buldum ve başka bir gönderide yanıtladım. stackoverflow.com/questions/22171221/... - Naresh Joshi


Cevaplar:


Vatansız çekirdeklere bağımlılıkları enjekte etmenin yol açtığı bir bağımlılık, bağımlılığı “statik” olarak tanımlamak, bir bağımlılık yöntemi oluşturmak, böylece Bahar bağımlılığı (statik bağımlılığa atayarak) enjekte edebilir.

Bağımlılığı statik olarak bildirin.

static private EvenementPliRepository evenementPliRepository;

Spring'in enjekte edebilmesi için bir yöntem oluşturun.

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

Daha fazla detay: http://blog-en.lineofsightnet.com/2012/08/dependency-injection-on-stateless-beans.html


16
2017-09-03 02:27



@SotiriosDelimanolis Bu aslında herkes için mi çalıştı? Bunu denedim, '@Outowired' dizgisini koyup çekirdekler hala boş. '@PostConstruct' yönteminin çağrıldığını doğruladım. - testing123
@SotiriosDelimanolis Elbette, bulduğum pek çok yaklaşım var ve bunu yapmanın en kolay yolu göründü. Ben sadece deli olmadığından emin olmak istedim çünkü 6 kez ayrılmıştı, bu yüzden belki de insanların başarısı olduğunu düşündüm. İnsanları bile çalışmayan bir şeyi yok etmeyin :). Diğer birçok yaklaşımdan birini deneyeceğim. Teşekkürler. - testing123
indirgemeye yardım etmek: P - Adrian Shum
@SotiriosDelimanolis, haklısınız, benim hatam, onu enjekte etmek için bir yöntem olmalı. - Juan Jimenez
Bu çalışmıyor - Naresh Joshi


Bir bahar dinleyicisini bir Entity dinleyicisine enjekte etmek için AOP kullanmanın yolunu bulmaya başladım. Araştırmadan bir buçuk gün sonra ve farklı şeyler denedikten sonra karşılaştım bağlantı belirtilen:

Yay yönetilen fasulyeleri bir JPA EntityListener sınıfına enjekte etmek mümkün değildir. Bunun nedeni, JPA dinleyici mekanizmasının vatansız bir sınıfa dayanması gerektiğinden, yöntemlerin etkili bir şekilde statik ve bağlam dışı farkında olmasıdır. ... Hiçbir AOP miktarı sizi kurtaramaz, hiçbir şey dinleyiciyi temsil eden "nesneye" hiçbir şey enjekte etmez, çünkü uygulamalar aslında örnek oluşturmaz, ancak sınıf yöntemini kullanır.

Bu noktada EclipseLink üzerinden yeniden gruplandım ve tökezledim DescriptorEventAdapter. Bu bilgiyi kullanarak Tanımlayıcı Bağdaştırıcısını genişleten bir dinleyici sınıfı oluşturdum.

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

Sınıfı kullanabilmek için, varlık sınıfımdaki @EntityListeners ek açıklamasını kullanmış olabilirdim. Ne yazık ki, bu yöntem Spring'in dinleyicimin oluşturulmasını kontrol etmesine izin vermeyecek ve sonuç olarak bağımlılık enjeksiyonuna izin vermeyecektir. Bunun yerine, şu 'init' işlevini sınıfıma ekledim:

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

Küçük bir Spring XML yapılandırması ekle

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

Şimdi, Spring'in bir varlık dinleyicisi oluşturduğu, bağımlılıklarına ihtiyaç duyulan her şeyle enjekte ettiği ve dinleyici nesnenin kendisini dinlemeyi düşündüğü varlık sınıfı ile kendini kaydettiği bir durum var.

Umarım bu yardımcı olur.


11
2018-05-08 18:23





Bu aslında eski bir soru ama alternatif bir çözüm buldum:

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

Muhtemelen en iyi değil ama AOP + dokuma ile statik değişkenlerden daha iyi değil.


10
2017-11-15 15:35



Ve bu dinleyiciyi xml kullanmadan gerçek varlığa nasıl eklerim? - Antoniossss
Eklemek @EntityListeners(MyEntityListener.class) ilgili varlığa - Ludovic Guillaume
Yapamıyor - model ayrı JAR'da. Demek istediğim, çalışma zamanı üzerinde XML olmadan bunu yapmaktır. - Antoniossss
Bu zaten XML'siz bir çözümdür. Bunu varlıkları içeren kavanozunuza eklemelisiniz. Ya da belki paylaşılan bir kavanoz oluşturun. Modelin kaynaklarına erişiminiz yoksa, dinlemek istediğiniz her sınıfı genişletmeniz gerekir. - Ludovic Guillaume
Bunu denedim ama benim için çalışmadı. "ProcessInjectionBasedOnCurrentContext ()" yönteminden hata ayıklama mesajına bakın: Geçerli Web UygulamasıKontext, MyEntityListener'ın işlenmesinde kullanılamaz: Bu sınıfın bir Spring web uygulamasında oluşturulduğundan emin olun. Enjeksiyon olmadan devam ediyor. - Naymesh Mistry


Peki ya bu çözüm?

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

}

Sonra Dinleyici ...

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

Ve yardımcısı ...

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

    }

Benim için çalışıyor.

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


9
2017-10-09 14:57



EntityListener, ApplicationContext'in başlatılmasında kullanılırsa, bu çözüm ile bir getcha var; o zaman emin olmalısınız AutowireHelper fasulye, onu kullanan koddan önce başlatılır. - holmis83
Çalışmalar, ancak yalnızca AutowireHelper'ı config xml veya Java Spring yapılandırmasına kaydettikten sonra: @Bean public AutowireHelper autowireHelper () {return AutowireHelper.getInstance (); } - ashes


Önerilen yaklaşımı test ettim https://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/ ve çalıştı. Çok temiz değil ama iş yapıyor. Benim için biraz değiştirilmiş AutowireHelper sınıfı şöyle görünüyordu:

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

Daha sonra bunu böyle bir varlık dinleyicisi olarak adlandırın:

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
2017-08-10 05:30





İnanıyorum ki bu dinleyici fasulyesi ilkbaharın kontrolü altında değil. İlkbahar bunu ortaya çıkarmıyor, Bahar o fasülyeyi nasıl bulacağını ve enjeksiyonu nasıl yapacağını nereden biliyor?

Bunu denemedim, fakat Spring'in yapılandırılmamış açıklamaları ile Spring kontrol non-Spring-örneklenmiş fasulyeleri elde etmek için AspectJ Weaver'dan faydalanabilirsiniz.

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


2
2017-08-28 09:14



Merhaba, teşekkürler Adrian. ekledim <context:spring-configured/> yapılandırmam için. POM'umda * .jar var. Hiçbir şey kaçırdığımı sanmıyorum. yine evenementPliRepositoryolduğu boş. Ne eksik olduğum hakkında başka bir fikrin var mı? - balteo
Ajan başladı mı? ref alıntı: Further, in certain environments, this support enables load-time weaving without making any modifications to the application server's launch script that will be needed to add -javaagent:path/to/aspectjweaver.jar or (as we describe later in this section) -javaagent:path/to/org.springframework.instrument-{version}.jar (previously named spring-agent.jar).  Ama dürüst olmak gerekirse, bu özelliği daha önce denemedim, muhtemelen farkında olmadığım başka kısıtlamalar var. - Adrian Shum
Bunu kontrol edeceğim. Ben de Spring Roo kullandığım için bunu anlıyorum. @Configurable zaten Roo tarafından kuruldu. Beni de vurmak, bağımlılık sorunları hakkında hiçbir uyarı almamam ... - balteo
Bununla ilgili hiçbir zaman hata yapamazsınız, çünkü dediğim gibi, bu nesne örneği Spring tarafından yönetilmiyor. DI ile ilgili açıklamalara sahip olmak hiçbir şey ifade etmiyor. Kimse hiçbir şey yapmamak için bu açıklamalara bakmayacak. - Adrian Shum


Başka seçenek:

AplicationContext'i erişilebilir yapmak için bir hizmet oluşturun:

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

}

Kullan:

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

Bu sınıf jpa'dan bir dinleyici olarak oluşturulur:

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

Dinleyici, Spring'in kontrolü altında olmadığı için, bağlam fasulyesine erişemez. Birden çok seçeneği (@Configurable (...)) denedim ve hiçbiri, içeriğe statik erişim sağlayan bir sınıf oluşturmak dışında çalıştı. Zaten bu ikilemde, bunun zarif bir seçenek olduğunu düşünüyorum.


0
2017-07-20 10:01