簡體   English   中英

Spring 依賴注入到 JPA 實體監聽器

[英]Spring Dependency Injection into JPA entity listener

我需要將 Spring 依賴項注入到 JPA 實體偵聽器中。 我知道我可以使用 @Configurable 和 Spring 的 AspectJ weaver 作為 javaagent 來解決這個問題,但這似乎是一個 hacky 解決方案。 還有其他方法可以完成我想做的事情嗎?

由於 Hibernate 5.3 org.hibernate.resource.beans.container.spi.BeanContainer和 Spring 5.1 org.springframework.orm.hibernate5.SpringBeanContainer你不再需要額外的自動裝配工作了。 https://github.com/spring-projects/spring-framework/issues/20852 中查看此功能的詳細信息

只需使用 @Component 注釋您的 EntityListener 類,然后執行任何自動裝配,如下所示:

@Component
public class MyEntityListener{

  private MySpringBean bean;

  @Autowired
  public MyEntityListener(MySpringBean bean){
    this.bean = bean;
  }

  @PrePersist
  public void prePersist(final Object entity) {
    ...
  }

}

在 Spring Boot 中,LocalContainerEntityManagerFactoryBean 的配置在 org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration 中自動完成。

在 Spring Boot 之外,您必須將 SpringBeanContainer 注冊到 Hibernate:

LocalContainerEntityManagerFactoryBean emfb = ...
 emfb.getJpaPropertyMap().put(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory));

另一個技巧是使用靜態方法實現一個實用程序類,它可以幫助您在任何地方使用 Spring bean,而不僅僅是在托管類中:

@Component
public final class BeanUtil {

    private static ApplicationContext context;

    private BeanUtil(ApplicationContext context) {
        BeanUtil.context = context;
    }

    public static <T> T getBean(Class<T> clazz) throws BeansException {

        Assert.state(context != null, "Spring context in the BeanUtil is not been initialized yet!");
        return context.getBean(clazz);
    }
}

你可以試試這個解決方案

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


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

/**
 * @return the singleton instance.
 */
public static AutowireHelper getInstance() {
    return INSTANCE;
}

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

}

進而

 @Autowired
SomeService thatToAutowire;

  AutowireHelper.autowire(this, this.thatToAutowire);//this in the method

這是 Kotlin 中的解決方案(Spring Boot 2.3.9,Hibernate 5.4.29.Final)。 第一部分類似於馬蒂亞斯的回答。 但是,即使它是 Spring Boot 應用程序,也需要第二部分。

Bean 聲明

@Component
class EntityXyzListener(val mySpringBean: MySpringBean) {

    @PostLoad
    fun afterLoad(entityXyz: EntityXyz) {
        // Injected bean is available here. (In my case the bean is a 
        // domain service that I make available to the entity.)
        entityXyz.mySpringBean= mySpringBean
    }

}

數據源配置

我的 Spring Boot 應用程序中已經有了這個數據源@Configuration 我只需要添加的代碼行放BEAN_CONTAINER在屬性jpaPropertyMap

@Resource
lateinit var context: AbstractApplicationContext

@Primary
@Bean
@Qualifier("appDatasource")
@ConfigurationProperties(prefix = "spring.datasource")
fun myAppDatasource(): DataSource {
    return DataSourceBuilder.create().build()
}

@Primary
@Bean(name = ["myAppEntityManagerFactory"])
fun entityManagerFactoryBean(builder: EntityManagerFactoryBuilder): LocalContainerEntityManagerFactoryBean {
    val localContainerEntityManagerFactoryBean =
            builder
                    .dataSource(myAppDatasource())
                    .packages("com.mydomain.myapp")
                    .persistenceUnit("myAppPersistenceUnit")
                    .build()
    // the line below was the long-sought solution :^)
    localContainerEntityManagerFactoryBean.jpaPropertyMap.put(
            AvailableSettings.BEAN_CONTAINER, SpringBeanContainer(context.beanFactory))
    return localContainerEntityManagerFactoryBean
}

稍微擴展以上回復:自 Hibernate 5.3 org.hibernate.resource.beans.container.spi.BeanContainer 和 Spring 5.1。 例如,您可以使用它來發布處理加載的域實體。 而不是使用方面。 參見: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/orm/hibernate5/SpringBeanContainer.html

在你的配置中:

@Bean
    LocalContainerEntityManagerFactoryBean customCartEntityManagerFactory(DataSource customCartDataSource, EntityManagerFactoryBuilder builder, ConfigurableListableBeanFactory beanFactory) {
        var mf = builder
                .dataSource(customCartDataSource)
                .packages("com.my.domain")
                .build();
        mf.getJpaPropertyMap().put(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory));
        return mf;
    }

在您的實體 bean 中:

@EntityListeners(MyEntityListener.class)

聽眾,請注意沒有 @Component 裝飾。

@Slf4j
public class MyEntityListener implements BeanFactoryAware, InitializingBean {

    private final BeanConfigurerSupport beanConfigurerSupport = new BeanConfigurerSupport();


    public CustomCartEntityListener() {
        log.info("MyEntityListener created");
    }

    @PostLoad
    public void postLoad(MyEntity entity) {
        beanConfigurerSupport.configureBean(entity);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanConfigurerSupport.setBeanWiringInfoResolver(new AnnotationBeanWiringInfoResolver());
        this.beanConfigurerSupport.setBeanFactory(beanFactory);
    }

    @Override
    public void afterPropertiesSet() {
        this.beanConfigurerSupport.afterPropertiesSet();
        log.info("MyEntityListener initialized");
    }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM