【问题标题】:Accessing Spring Beans inside AttributeConverter class在 AttributeConverter 类中访问 Spring Bean
【发布时间】:2018-04-23 11:37:35
【问题描述】:

我正在开发一个 Spring Data JPA 应用程序,并且我创建了一个 AttributeConverter 类,以便将对象的 ArrayList 保存为数据库列中的 JSON。在这个类中,我需要使用一个我定义为 Spring Bean 的类。

由于AttributeConverter 类由 Hibernate 管理,它似乎在创建任何 Spring bean 之前就已实例化,因此 DI 似乎不起作用(AttributeConverter 类中的 Spring Bean 是 null,并且我收到了NullPointer 异常)。所以目前我正在创建该 bean 的另一个实例,以便能够在 AttributeConverter 类中使用它(这违背了 DI 的目的)。

我还尝试创建一个 Util 类(用 @Component 注释),它实现了 ApplicationContextAware,它提供了一个提供 SpringBean (cxt.getBean(BeanClass.class)) 的方法。但这也是在 AttributeConverter 之后实例化的。

有没有办法解决这个问题?

谢谢。

【问题讨论】:

  • @tom01 不,不是。您提供的链接是关于在创建对象时控制的对象,在 AttributeConverter 的情况下是不可能的。
  • 该链接显示了如何自动装配未由 Spring 容器初始化的实例,在这种情况下有什么不同?当然,只需在适当的应用程序生命周期点获取对 Hibernate 和 AutowireCapableBeanFactory 创建的 AttributeConverter 实例的引用。

标签: spring hibernate jpa


【解决方案1】:

您可以使用静态属性在 AttributeConverter 中注入 bean(@Component, @Service, @Repository)

设置:

  1. 在您的 AttributeConverter 中设置以下注释:@Component、@Converter 和 @Configurable
  2. 定义要使用静态修饰符访问自动装配的字段
  3. 创建一个 init 方法以自动装配存储库
  4. 实现接口AttributeConverter中定义的方法

基本上,代码应该是这样的......

//Step 1
@Component
@Converter
@Configurable
public class MyAttributeConverter implements AttributeConverter<X,Y> {
    //Where: X = the type of the entity attribute and Y = the type of the database column

    //Step 2
    private static MyRepository myRepository;

    //Step 3
    @Autowired
    public void initMyRepository(MyRepository myRepository){
        MyAttributeConverter.myRepository = myRepository;
    }

    //Step 4
    Y convertToDatabaseColumn(X attribute){//TODO implement method}
    X convertToEntityAttribute(Y dbData){//TODO implement method}
}

希望对你有帮助!!!

【讨论】:

  • 它有效,但我仍在寻找一种干净的方法来做到这一点(没有静态字段)。
【解决方案2】:

使用 JPA 2.2、Spring 5.1( SPR-16305) 和 Hibernate 5.3.0 (HHH-12135) 您不再需要使用可变静态属性 hack,并且可以像在常规 spring 托管 bean 上一样使用依赖注入(注意不再需要注释):

public class MyAttributeConverter implements AttributeConverter<X,Y> {

    private final MySpringBean bean;

    public MyAttributeConverter(MySpringBean bean) {
        this.bean = bean;
    }

    public Y convertToDatabaseColumn(X attribute) {
      ...
    }

    public X convertToEntityAttribute(Y dbData) {
      ...
    }
}

【讨论】:

  • 成功了,谢谢!这是我必须做的:升级到 Spring Boot 2.1(因为它使用 Spring 5.1),以及 Spring Cloud 版本 Greenwich.RELEASE(如果你想省略父 pom)。将 spring.jpa.database-platform 添加到我的 application.properties,将时区添加到 DB 连接 URL,并从 AttibuteConverter 中删除注释(不确定是否严格需要)。
  • 它适用于 Spring Boot 2.1,除非我将 @Converter 注释添加到MyAttributeConverter
  • 您必须使用@Converter 注释该字段。我的意思是,它不适用于@Converter(autoApply = false)(至少在我的测试中没有,它没有......)
  • 对我不起作用,我更新到 spring-boot-starter-parent 2.5.5。我的转换器看起来完全一样。但是应用程序无法启动并出现错误消息NoSuchMethodException - 抱怨没有默认构造函数
【解决方案3】:

一般来说,我猜 Ipandzic 的回答是正确的提示。然而,他如何描述它对我不起作用。在 Spring 环境中,我的参数构造函数看起来也有点奇怪。我玩了一下,能够使用以下形式的 AttributeConverter(实际上,您不需要 @Converter-annotation 或 AttributeConverter-class 本身的任何其他内容):

import javax.persistence.AttributeConverter;
import org.springframework.beans.factory.annotation.Autowired;

public class MyConverter implements AttributeConverter<String, String> {
    @Autowired
    private MyBean mybean;

    public String convertToDatabaseColumn(String value) {
        return myBean.changeValue(value);
    }

    public String convertToEntityAttribute(String dbValue) {
        return myBean.undoChange(dbValue);
    }
}

但是创建这个类并升级到 Spring-Boot 2.1(包括 Spring 5.1、Hibernate 5.3 和 JPA 2.2)对我来说并没有成功。问题是,我使用LocalContainerEntityManagerFactoryBean 来配置我的持久存储,它不会为AttributeConverters 启用依赖注入。阅读 Ipandzic 发布的第一个链接建议您必须以某种方式将LocalSessionFactoryBuilder 发挥作用。这就是我最终得到以下配置的原因:

//...

@Bean
public LocalSessionFactoryBean entityManagerFactory(DataSource dataSource, Environment env) {
    LocalSessionFactoryBean factory = new LocalSessionFactoryBean();
    factory.setDataSource(dataSource);

    // somehow tell the factory where you entitiy-definitions are, this is just
    // one possibility of doing so:
    String entityPackage = JpaMarkerModel.class.getPackage().getName();
    log.info("EntityManager will scan for entities in package [{}].", entityPackage);
    factory.setPackagesToScan(entityPackage);

    return factory;
}

@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(entityManagerFactory);
    return transactionManager;
}

//...

这个“答案”只是对 Ipandzi 的补充,但也许它可以帮助一些人比我更快地解决他们的问题。

【讨论】:

  • 更喜欢使用@EnableEntityScan 注释。
  • 这与手头的问题有关吗?为什么我更喜欢使用该注释?
  • 有一种方法可以通过为 Hibernate 设置 bean 工厂来继续使用 LocalContainerEntityManagerFactoryBeangithub.com/spring-projects/spring-framework/issues/…
  • 我遇到了类似的问题,升级了所有依赖项,但仍然没有自动装配。提到LocalContainerEntityManagerFactoryBean 是一个线索——我们在应用程序上下文/配置中明确创建它,因此使用了非 Spring BeanContainer。 Github问题的链接为我提供了答案:LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean(); emfb.getJpaPropertyMap().put(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory));
猜你喜欢
  • 2011-07-27
  • 2013-04-11
  • 1970-01-01
  • 2016-08-19
  • 1970-01-01
  • 1970-01-01
  • 2017-03-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多