Как вызвать метод после завершения инициализации компонента?

237

У меня есть случай использования, когда мне нужно вызвать (нестатический) метод в компоненте только один раз при загрузке ApplicationContext. Это нормально, если я использую MethodInvokingFactoryBean для этого? Или у нас есть лучшее решение?

Как примечание, я использую ConfigContextLoaderListener для загрузки контекста приложения в веб-приложении. И хотите, чтобы, если экземпляр E был создан, просто вызовите methodA () один раз.

Как это можно сделать красиво?

peakit
источник

Ответы:

196

Вы можете использовать что-то вроде:

<beans>
    <bean id="myBean" class="..." init-method="init"/>
</beans>

Это вызовет метод init при создании экземпляра bean-компонента.

Мерсер Трайест
источник
15
postConstruct должен быть лучше в большинстве случаев, так как мы не хотим портиться с инициализацией bean-компонента.
lwpro2
4
@ lwpro2 Что вы подразумеваете под "не хочу портить весеннюю инициализацию" здесь?
Ингве Снин Линдал
@Mercer Traieste, что я должен дать для атрибута класса здесь? Могу ли я дать класс контроллера здесь?
KJEjava48
314

Чтобы расширить предложение @PostConstruct в других ответах, на мой взгляд, это действительно лучшее решение.

  • Он сохраняет ваш код отделенным от Spring API (@PostConstruct находится в javax. *)
  • Он явно аннотирует ваш метод init как то, что нужно вызвать для инициализации bean-компонента.
  • Вам не нужно помнить, чтобы добавить атрибут init-method к определению вашего bean-компонента Spring, Spring автоматически вызовет метод (в любом случае, если вы зарегистрируете опцию annotation-config где-нибудь еще в контексте).
skaffman
источник
9
Спасибо, это работает. Обратите внимание: если вы хотите использовать Spring, вы должны включить «<context: annotation-config />» для регистрации bean-компонента CommonAnnotationBeanPostProcessor (как упоминалось выше)
khylo
2
Подходящий <context:component-scan>также работает и может быть полезен для сокращения времени запуска, если в вашем пути к классам есть большие библиотеки, отличные от Spring.
Донал Феллоуз
5
JavaDoc для PostConstruct говорит, что для каждого класса может быть аннотирован только один метод: docs.oracle.com/javaee/5/api/javax/annotation/…
Эндрю Свон
@PostConstruct не работает с менеджером транзакций, см .: forum.spring.io/forum/spring-projects/data/…
ммм
2
@PostConstruct также не будет вам очень полезен, когда создаваемый вами бин - это не ваш собственный класс, а какой-то сторонний класс
Джон Рикс,
102

Есть три различных подхода к рассмотрению, как описано в ссылке

Используйте атрибут init-method

Плюсы:

  • Не требует bean-компонента для реализации интерфейса.

Минусы:

  • Никаких немедленных указаний на то, что этот метод не требуется после создания, чтобы гарантировать, что компонент настроен правильно.

Реализация InitializingBean

Плюсы:

  • Нет необходимости указывать init-метод или включать компонентное сканирование / обработку аннотаций.
  • Подходит для bean-компонентов, поставляемых с библиотекой, где мы не хотим, чтобы приложение, использующее эту библиотеку, занималось жизненным циклом bean-компонента.

Минусы:

  • Более инвазивен, чем метод init-метода.

Используйте аннотацию жизненного цикла JSR-250 @PostConstruct

Плюсы:

  • Полезно при использовании компонентного сканирования для автоматического определения bean-компонентов.
  • Проясняет, что для инициализации должен использоваться конкретный метод. Намерение ближе к коду.

Минусы:

  • Инициализация больше не указывается централизованно в конфигурации.
  • Не забудьте включить обработку аннотаций (о которых иногда можно забыть)
Инструментарий
источник
4
Я думаю, что на самом деле это хорошая вещь, которую нужно использовать @PostConstructименно потому, что она является частью класса, которому нужен метод, вызываемый в конце обработки инициализации.
Донал Феллоуз
Если этот класс действительно нуждается в этом, и вы не можете сделать это в конструкторе, тогда я считаю, что это запах кода.
user482745
39

Вы пытались реализовать InitializingBean? Это звучит как то, что вы после.

Недостатком является то, что ваш бин становится Spring-осведомленным, но в большинстве приложений это не так уж плохо.

Джон Скит
источник
2
Есть ли причина, по которой вы бы предпочли реализацию интерфейса, а не указание init-метода в XML?
Mark
4
Это дело вкуса. Интерфейс является частью модели компонентов Spring и служит этой и только этой цели, в то время как для пользовательского именованного метода может быть не совсем очевидно, что его нужно вызывать для завершения жизненного цикла компонента. Так что это служит общению в основном. Конечно, с недостатком введенной зависимости от среды Spring. Хорошим способом между ними является использование @PostConstruct, поскольку он имеет четкую семантику, но не вводит зависимости ...
Оливер Дротбом
7
Оливер дает мне несколько хороших оправданий, но на самом деле я просто забыл о init-методе :) Еще одна причина в том, что сам тип знает, что его нужно "завершить" после того, как все свойства были установлены - это не так. принципиально то, что должно быть в конфигурации.
Джон Скит
8

Вы можете развернуть пользовательский BeanPostProcessor в контексте вашего приложения, чтобы сделать это. Или, если вы не возражаете против реализации интерфейса Spring в вашем bean-компоненте, вы можете использовать интерфейс InitializingBean или директиву init-method (та же ссылка).

Роб Х
источник
У кого-нибудь есть подробности о том, как написать BeanPostProcessor. Это звучит как раз то, что мне нужно. Ура :)
пик
Весенние корабли со множеством примеров. Просто посмотрите на API JavaDoc для BeanPostProcessor, и вы найдете ссылки на многие реализующие классы. Тогда посмотрите на исходный код для них.
Роб Х
-7

Для дальнейшего устранения любой путаницы в отношении двух подходов, т.е. использования

  1. @PostConstruct и
  2. init-method="init"

Из личного опыта я понял, что использование (1) работает только в контейнере сервлета, в то время как (2) работает в любой среде, даже в настольных приложениях. Таким образом, если вы будете использовать Spring в автономном приложении, вам придется использовать (2), чтобы выполнить «вызов этого метода после инициализации».

Ayorinde
источник
4
Технически @PostConstruct(при использовании в приложении на основе Spring) он привязан к сроку службы собственного контекста Spring. Такие контексты могут использоваться во всех видах приложений.
Донал Феллоуз
Это было поведение, которое я ожидал, но не работал для меня.
Ayorinde