Spring — ввод полей в компонент, прежде чем он получит @Autowired

#java #spring #spring-boot #autowired

#java #весна #пружинный ботинок #автозаполнение

Вопрос:

Существует ли весной механизм или прослушиватель для определения того, когда компонент, аннотированный определенной аннотацией, получает @Autowired и запускает на нем какую-либо пользовательскую логику? Что-то похожее на то, что @ConfigurationProperties уже делает, автоматически вводит поля до того, как они будут автоматически подключены.

У меня есть требование, в котором мне нужно вводить значения в поля определенных компонентов, аннотированных @ExampleAnnotation до их создания. В идеале, в этом слушателе я бы:

  1. Спросите, аннотируется ли текущий создаваемый экземпляр компонента @ExampleAnnotation
  2. Если это не так, верните. Если это так, я бы использовал отражение, чтобы получить имена полей из этого компонента и заполнить их с помощью репозитория.

Возможно ли что-то подобное?

Ответ №1:

Вы можете достичь этого с помощью следующего кода:

 @Component
class MyBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      // Every spring bean will visit here

      // Check for superclasses or interfaces
      if (bean instanceof MyBean) {
        // do your custom logic here
        bean.setXyz(abc);
        return bean;
      }
      // Or check for annotation using applicationContext
      MyAnnotation myAnnotation = this.applicationContext.findAnnotationOnBean(beanName, MyAnnotation.class);
      if (myAnnotation != null) {
        // do your custom logic here
        bean.setXyz(myAnnotation.getAbc());
        return bean;
      }
      return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
      this.applicationContext = applicationContext;
    }

    // Optional part. If you want to use Autowired inside BeanPostProcessors 
    // you can use Lazy annotation. Otherwise they may skip bean processing
    @Lazy
    @Autowired
    public MyBeanPostProcessor(MyLazyAutowiredBean myLazyAutowiredBean) {
    }
}
 

Комментарии:

1. Это выглядит многообещающе, однако this.applicationContext.findAnnotationOnBean(beanName, MyAnnotation.class); вызывает всевозможные проблемы во время сборки. Трассировка стека слишком велика, чтобы поместить ее здесь, но она связана Cannot create inner bean... с зависимостями и не выполняется. Есть ли findAnnotationOnBean какие-либо побочные эффекты, которые могут быть причиной этого? Когда я комментирую эту строку, проект строится правильно.

2. Это работает для меня. Вам не нужно использовать контекст приложения для поиска аннотаций к классам. Существуют и другие служебные классы, такие как org.springframework.core.annotation.AnnotationUtils .

3. Вероятно, вызвано какой-то магией из Spring Data JPA. Я использовал getClass().getAnnotation(MyAnnotation.class) вместо этого, и это работает. Это именно то, что я хотел. Спасибо!

Ответ №2:

Я думаю, если это похоже на ConfigurationProperties , то класс ConfigurationPropertiesBindingPostProcessor , который связывает свойства с beans, может служить примером. Он реализует BeanPostProcessor и выполняет привязку в postProcessBeforeInitialization методе. Этот метод имеет следующие Javadocs:

«Примените этот BeanPostProcessor к данному новому экземпляру bean перед любыми обратными вызовами beaninitialization (например, afterpropertiesetor от InitializingBean или пользовательский init-метод). Компонент уже будет заполнен значениями свойств.Возвращаемый экземпляр компонента может быть оболочкой вокруг оригинала «.

Комментарии:

1. Как Spring узнает, что нужно запускать ConfigurationPropertiesBindingPostProcessor всякий раз, когда он сталкивается с аннотированным классом @ConfigurationProperties ?

2. Он ConfigurationPropertiesBindingPostProcessor проверит компонент, чтобы узнать, аннотирован ли он ConfigurationProperties . Если да, то выполняется привязка. Что касается того, как он зарегистрирован, Javadoc of BeanPostProcessor говорит, что «an ApplicationContext может автоматически BeanPostProcessor определять bean-компоненты в своих определениях bean и применять эти постпроцессоры к любым впоследствии созданным bean-компонентам» . Его также можно зарегистрировать с помощью EnableConfigurationProperties . В этом руководстве показан пример.

Ответ №3:

Одним из возможных решений является написание пользовательского сеттера и аннотирование его с помощью @Autowired, например:

 @Autowired
public void setExample(Example example)
{

    // Do your stuff here.

    this.example = example;
}
 

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