72

Spring 依存関係JPA EntityListenerに注入しようとしています。これが私のリスナークラスです:

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


}

ここに私のエンティティクラスがあります:

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

ただし、私の依存関係 (つまりevenementPliRepository)は常に nullです。

誰でも助けてもらえますか?

4

14 に答える 14

43

ステートレス Bean に依存関係を注入するためのハックは、依存関係を「静的」として定義し、setter メソッドを作成して、Spring が依存関係を注入できるようにすることです (静的依存関係に割り当てます)。

依存関係を静的として宣言します。

static private EvenementPliRepository evenementPliRepository;

Spring が注入できるようにメソッドを作成します。

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

詳細: http://blog-en.lineofsightnet.com/2012/08/dependency-injection-on-stateless-beans.html

于 2012-09-03T02:27:09.303 に答える
29

これは実際には古い質問ですが、別の解決策を見つけました:

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

おそらく最良のものではありませんが、AOP + ウィービングなしの静的変数よりは優れています。

于 2016-11-15T15:35:10.447 に答える
18

Spring V5.1 (および Hibernate V5.3) 以降、Spring はこれらのクラスのプロバイダーとして登録されるため、そのまま使用できるはずです。SpringBeanContainerのドキュメントを参照してください

于 2019-08-27T08:59:47.407 に答える
14

AOP を使用して Spring Bean を Entity リスナーに注入する道をたどり始めました。1日半の調査とさまざまなことを試した後、次のリンクに出会いました。

Spring Managed Bean を JPA EntityListener クラスに注入することはできません。これは、JPA リスナー メカニズムがステートレス クラスに基づく必要があるためです。そのため、メソッドは事実上静的であり、コンテキストを認識しません。...実装は実際にはインスタンスを作成せず、クラスメソッドを使用するため、AOPの量はあなたを救いません。リスナーを表す「オブジェクト」には何も注入されません。

この時点で、私は再編成して EclipseLink DescriptorEventAdapterに出くわしました。この情報を使用して、Descriptor Adapter を拡張するリスナー クラスを作成しました。

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

クラスを使用するには、エンティティ クラスで @EntityListeners アノテーションを使用できます。残念ながら、この方法では、Spring がリスナーの作成を制御できず、その結果、依存性注入が許可されません。代わりに、次の「init」関数をクラスに追加しました。

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

Spring XML 構成を少し追加する

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

ここで、Spring がエンティティ リスナーを作成し、それに必要な依存関係を注入し、リスナー オブジェクトがリッスンするエンティティ クラスに自身を登録する状況があります。

これが役立つことを願っています。

于 2013-05-08T18:23:38.320 に答える
8

https://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/で提案されているアプローチをテストし、機能しました。あまりきれいではありませんが、仕事はします。AutowireHelper クラスを少し変更すると、次のようになります。

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

次に、エンティティ リスナーから次のように呼び出します。

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;
    }
}
于 2017-08-10T05:30:37.267 に答える
3

これは、このリスナー Bean が Spring の制御下にないためだと思います。Spring はそれをインスタンス化していません。Spring はその Bean を見つけて注入を行う方法をどのように知ることができますか?

私はそれを試していませんが、AspectJ Weaver と Spring の Configurable アノテーションを使用して、Spring でインスタンス化されていない Bean を Spring で制御できるようです。

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

于 2012-08-28T09:14:47.180 に答える
1

Paulo Merson の回答に基づいて、を利用して SpringBeanContainer を設定する方法のバリエーションを次に示しJpaBaseConfigurationます。両方の手順を次に示します。

ステップ 1 : リスナーを Spring コンポーネントとして定義します。自動配線は、コンストラクターの挿入を通じて機能することに注意してください。

@Component
public class PliListener {

    private EvenementPliRepository evenementPliRepository;

    public PliListener(EvenementPliRepository repo) {
        this.evenementPliRepository = repo;
    }

    @PrePersist
    public void touchForCreate(Object target) {
        // ...
    }

    @PostPersist
    void onPostPersist(Object target) {
        // ...
    }
}

ステップ 2 : を設定してSpringBeanContainer、リスナーでオートワイヤーを有効にします。SpringBeanContainer JavaDocは一見の価値があるかもしれません。

@Configuration
public class JpaConfig extends JpaBaseConfiguration {
    
    @Autowired
    private ConfigurableListableBeanFactory beanFactory;

    protected JpaConfig(DataSource dataSource, JpaProperties properties,
            ObjectProvider<JtaTransactionManager> jtaTransactionManager) {
        super(dataSource, properties, jtaTransactionManager);
    }

    @Override
    protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
        return new HibernateJpaVendorAdapter();
    }

    @Override
    protected Map<String, Object> getVendorProperties() {
        Map<String, Object> props = new HashMap<>();

        // configure use of SpringBeanContainer
        props.put(org.hibernate.cfg.AvailableSettings.BEAN_CONTAINER, 
            new SpringBeanContainer(beanFactory));
        return props;
    }

}
于 2021-08-19T20:12:25.997 に答える