Инъекция бобов внутри 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.