Инъекция бобов внутри JPA @Entity

Можно ли вводить бобы в JPA @Entity с помощью инъекции зависимостей Spring?

Я попытался @Autowire ServletContext, но, хотя сервер действительно запустился, я получил NullPointerException при попытке получить доступ к свойству компонента.

@Autowired
@Transient
ServletContext servletContext;

3 ответа

Решение

Вы можете внедрить зависимости в объекты, не управляемые контейнером Spring, используя @Configurable как объяснено здесь: http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html.

Как вы уже поняли, если не используете @Configurable и соответствующей конфигурации AspectJ, Spring не внедряет зависимости в объекты, созданные с использованием new оператор. Фактически, он не вводит зависимости в объекты, если вы не получили их из ApplicationContextПо той простой причине, что он просто не знает об их существовании. Даже если вы аннотируете свою сущность @Componentсоздание экземпляра этого объекта все еще будет выполняться new операция, либо вами, либо фреймворк, такой как Hibernate. Помните, аннотации - это просто метаданные: если никто не интерпретирует эти метаданные, это не добавляет никакого поведения и не влияет на работающую программу.

Все это, как говорится, я настоятельно рекомендую не вводить ServletContext в сущность. Объекты являются частью вашей доменной модели и должны быть отделены от любого механизма доставки, такого как уровень веб-доставки на основе сервлетов. Как вы будете использовать эту сущность, когда к ней обращается клиент из командной строки или что-то еще, не связанное с ServletContext? Вы должны извлечь необходимые данные из этого ServletContext и передать их через аргументы традиционного метода вашей сущности. Благодаря этому подходу вы достигнете гораздо лучшего дизайна.

Да, конечно ты можешь. Вам просто нужно убедиться, что сущность также зарегистрирована как управляемый компонент Spring либо декларативно с использованием <bean> теги (в некоторых spring-context.xml) или аннотации, как показано ниже.

Используя аннотации, вы можете пометить свои объекты как @Component (или более конкретный стереотип @Repository который включает автоматический перевод исключений для DAO и может или не может мешать JPA).

@Entity
@Component
public class MyJAPEntity {

  @Autowired
  @Transient
  ServletContext servletContext;
  ...
}

После того, как вы сделали это для своих сущностей, вам нужно настроить их пакет (или некоторый пакет предков) для сканирования Spring, чтобы сущности выбирались как бины, а их зависимости автоматически подключались.

<beans ... xmlns:context="..." >
  ...
  <context:component-scan base-package="pkg.of.your.jpa.entities" />
<beans>

РЕДАКТИРОВАТЬ: (что, наконец, сработало и почему)

  • Делая ServletContext статический(удалить @Autowired)

    @Transient
    private static ServletContext servletContext;
    

Поскольку JPA создает отдельный экземпляр сущности, т. Е. Не использует управляемый bean-компонент Spring, он необходим для совместного использования контекста.

  • Добавление @PostConstruct init() метод.

    @PostConstruct
    public void init() {
        log.info("Initializing ServletContext as [" +
                    MyJPAEntity.servletContext + "]");
    }
    

Это пожары init() после того, как сущность была создана и путем ссылки на ServletContext внутри он вызывает инъекцию статического свойства, если оно еще не введено.

  • перемещение @Autowired к методу экземпляра, но установив статическое поле внутри.

    @Autowired
    public void setServletContext(ServletContext servletContext) {
        MyJPAEntity.servletContext = servletContext;
    }
    

Цитируя мой последний комментарий ниже, чтобы ответить, почему мы должны использовать эти махинации:

Нет хорошего способа сделать то, что вы хотите, поскольку JPA не использует контейнер Spring для создания экземпляров своих сущностей. Думайте о JPA как об отдельном контейнере ORM, который создает экземпляр жизненного цикла сущностей (полностью отделен от Spring) и управляет им, а также выполняет DI на основе только взаимосвязей сущностей.

После долгого времени я наткнулся на этот SO ответ, который заставил меня задуматься об элегантном решении:

  • Добавьте к своим сущностям все необходимые поля @Transient @Autowired
  • Создайте @Repository DAO с этим автоматическим полем: @Autowired private AutowireCapableBeanFactory autowirer;
  • Из вашего DAO, после извлечения сущности из БД, вызовите этот код автоматического подключения: String beanName = fetchedEntity.getClass().getSimpleName(); autowirer.autowireBean(fetchedEntity); fetchedEntity = (FetchedEntity) autowirer.initializeBean(fetchedEntity, beanName);

После этого ваша сущность сможет получить доступ к полям с автопроводкой, как любой @Component.

Другие вопросы по тегам