Весенняя зависимость @PostConstruct

Скажем, у меня есть эта зависимость в Spring @Configuration:

@Bean
public SomeClass someClass(SomeClass1 someClass1, SomeClass2 someClass2, ...) {
  return new SomeClass(someClass1, someClass2, ...);
}

Скажем, я хочу сделать что-то в @PostConstruct, которое включает зависимость someClass:

@PostConstruct
public void init() {
  someClass.doSomething();
}

Это не может быть введено:

@PostConstruct
public void init(SomeClass someClass) {
  someClass.doSomething();
}

причины:

Caused by: java.lang.IllegalStateException: Lifecycle method annotation requires a no-arg method: ...

Это не может быть автоматически подключено в той же конфигурации, например:

@Autowire
private SomeClass someClass;

@Bean
public SomeClass someClass(SomeClass1 someClass1, SomeClass2 someClass2, ...) {
  return new SomeClass(someClass1, someClass2, ...);
}

так как это приводит к:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'globalBus': Requested bean is currently in creation: Is there an unresolvable circular reference?

Конфиг можно разделить (так что @Bean переходит к другому конфигу) и @Import отредактировать этим, и он работает нормально. Вероятно, существуют и другие растворы - например. создание отдельного bean-компонента инициализации или около того.

Есть ли способ сделать это в пределах одного @Configuration?

Изменить

По запросу @SotiriosDelimanolis, sscce для исключения при использовании @Autowired:

public class ConfigPostConstructDependenciesPrb {
   public static void main(String[] args) {
      AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
      ctx.getBean(Service.class);
      ctx.close();
   }

   public static class Service {
      private final Dependency dependency;

      public Service(Dependency dependency) {
         this.dependency = dependency;
      }

      public void work() {
         System.out.println(dependency.getNum());
      }

      @Override
      public String toString() {
         StringBuilder sb = new StringBuilder();
         sb.append("Service [dependency=");
         sb.append(dependency);
         sb.append("]");
         return sb.toString();
      }
   }

   public static class Dependency {
      private final int num;

      public Dependency(int num) {
         this.num = num;
      }

      public int getNum() {
         return this.num;
      }

      @Override
      public String toString() {
         StringBuilder sb = new StringBuilder();
         sb.append("SomeClass1 [num=");
         sb.append(num);
         sb.append("]");
         return sb.toString();
      }
   }

   @Configuration
   public static class BaseConfig {
      @Autowired
      private Service service;

      @Bean
      public Dependency dependency() {
         return new Dependency(42);
      }

      @Bean
      public Service service(Dependency dependency) {
         return new Service(dependency);
      }

      @PostConstruct
      public void init() {
         service.work();
      }
   }

   @Configuration
   @Import(BaseConfig.class)
   public static class Config {
      @Autowired
      private Service service;
   }   
}

person levant pied    schedule 25.06.2015    source источник
comment
Почему вы не можете/не Inject этого?   -  person user489041    schedule 25.06.2015
comment
Является ли SomeClass внутренним классом?   -  person Thunderforge    schedule 25.06.2015
comment
@PostConstruct предназначен для ваших bean-компонентов, а не для ваших классов конфигурации. Как сказал пользователь489041, вы должны просто ввести его в свой компонент, если он там используется.   -  person hyness    schedule 25.06.2015
comment
Вы должны делать @PostConstruct в классе @Configuration? Что это за способ установки?   -  person Sotirios Delimanolis    schedule 25.06.2015
comment
Кроме того, в зависимости от того, как выглядит остальная часть вашей конфигурации, ваше решение @Autowired должно работать. Поскольку это не так, вам мешают другие вещи. Просьба уточнить.   -  person Sotirios Delimanolis    schedule 25.06.2015
comment
user489041 - Не Autowired == Inject? / Thunderforge - нет / hyness - см. stackoverflow.com/questions/24548004 / @SotiriosDelimanolis - да, нужно регистрировать слушателей в конце концов проводной. Я попытаюсь опубликовать sscce   -  person levant pied    schedule 26.06.2015
comment
попробуйте использовать SpringBeanAutowiringSupport()   -  person Tiago Medici    schedule 30.07.2021


Ответы (2)


Попробуйте так:

public class ConfigPostConstructDependenciesPrb  {

    public static void main(String[] args) {
        try {
            AnnotationConfigApplicationContext ctx =
                new AnnotationConfigApplicationContext(BaseConfig.class);

            ctx.registerShutdownHook();
            ctx.getBean(Service.class);
            ctx.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

}

@Configuration
class BaseConfig {

    @Autowired
    private Service service;

    @Bean
    public Dependency dependency() {
        return new Dependency(42);
    }

    @Bean
    public Service service(Dependency dependency) {
        return new Service(dependency);
    }

    @PostConstruct
    public void init() {
        this.service.work();
    }
}

class Dependency {

    private int num;

    public Dependency() {

    }

    public Dependency(int num) {
        this.num = num;
    }

    public int getNum() {
        return this.num;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("SomeClass1 [num=");
        sb.append(num);
        sb.append("]");
        return sb.toString();
    }
}

class Service {

    private Dependency dependency;

    public Service() {

    }

    public Service(Dependency dependency) {
        this.dependency = dependency;
    }

    public void work() {
        System.out.println(dependency.getNum());
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Service [dependency=");
        sb.append(dependency);
        sb.append("]");
        return sb.toString();
    }
}
person maframaran    schedule 25.06.2015
comment
Какая здесь разница по сравнению с тем, что я опубликовал, удаление класса Config? Если да, то это ничего не решает. - person levant pied; 30.06.2015
comment
Посмотрите на модификаторы, мои классы не статичны. Я доказал этот пример, и он работает. - person maframaran; 01.07.2015
comment
Статичны ли классы или нет, ничего не меняется (не стесняйтесь проверить себя, чтобы подтвердить). Вы изменили код таким образом, что меняется инициализация — вы уменьшили количество конфигов с 2 до 1 и убрали импорт. Будет ли это работать, если вы импортируете конфиги, как я (т. е. Config импортирует BaseConfig, а вы инициализируете с помощью Config)? - person levant pied; 01.07.2015
comment
Покажите мне, в чем ошибка при тестировании этого кода, потому что я проверил свой код перед публикацией, и он работает. Может быть, у вас есть другая ошибка. - person maframaran; 02.07.2015
comment
Я согласен, это работает, но это не то же самое. Вы удалили Config класс и используете только BaseConfig. Если вы попытаетесь использовать несколько конфигураций (которые мне нужны), это сломается. Подробности смотрите в моем предыдущем комментарии. - person levant pied; 07.07.2015
comment
Проблема в вашем коде: Spring Loader пытается внедрить службу при создании bean-компонента (в то же время). Чтобы решить эту проблему и создать сервисный компонент перед внедрением, необходимо прервать жизненный цикл Spring и создать компоненты. Попробуйте реализовать этот интерфейс BeanFactoryPostProcessor и зарегистрируйте bean-компоненты в методе с именем postProcessBeanFactory. Это решит вашу проблему. - person maframaran; 07.07.2015

(Проверено весной 4.3.6)

Создайте вложенный класс внутри вашего @Configuration и поместите туда объявления @Autowired service и @PostConstruct init():

@Configuration
public static class BaseConfig {

    //...

    @Bean
    public Service service(Dependency dependency) {
        return new Service(dependency);
    }

    @Configuration
    public static class Setup {

        @Autowired
        private Service service;

        @PostConstruct
        public void init() {
            service.work();
        }
    }
}

Ниже приведен ваш полный пример, обновленный соответствующим образом.

Обратите внимание, что вам не нужно добавлять явную ссылку на BaseConfig.Setup (посмотрите на аннотацию @Import перед классом Config — она относится только к самому BaseConfig).

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;

import javax.annotation.PostConstruct;

public class ConfigPostConstructDependenciesPrb {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
        ctx.getBean(Service.class);
        ctx.close();
    }

    public static class Service {
        private final Dependency dependency;

        public Service(Dependency dependency) {
            this.dependency = dependency;
        }

        public void work() {
            System.out.println(dependency.getNum());
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("Service [dependency=");
            sb.append(dependency);
            sb.append("]");
            return sb.toString();
        }
    }

    public static class Dependency {
        private final int num;

        public Dependency(int num) {
            this.num = num;
        }

        public int getNum() {
            return this.num;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("SomeClass1 [num=");
            sb.append(num);
            sb.append("]");
            return sb.toString();
        }
    }

    @Configuration
    public static class BaseConfig {
        @Bean
        public Dependency dependency() {
            return new Dependency(42);
        }

        @Bean
        public Service service(Dependency dependency) {
            return new Service(dependency);
        }

        @Configuration
        public static class Setup {
            @Autowired
            private Service service;

            @PostConstruct
            public void init() {
                service.work();
            }
        }
    }

    @Configuration
    @Import(BaseConfig.class)
    public static class Config {
        @Autowired
        private Service service;
    }
}
person user3078523    schedule 26.09.2018