简体   繁体   中英

Spring dependency @PostConstruct

Say I have this dependency in a Spring @Configuration :

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

Say I want do do something in @PostConstruct that includes someClass dependency:

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

This cannot be injected:

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

causes:

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

This cannot be autowired in the same config like this:

@Autowire
private SomeClass someClass;

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

as that leads to:

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?

A config can be split (so @Bean goes to the other config) and @Import -ed by this one and it works OK. Probably other solutoins exist - eg creating a separate initialization bean or so.

Is there a way to do this within one @Configuration ?

Edit

As requested by @SotiriosDelimanolis, a sscce for the exception when using @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;
   }   
}

Try this way:

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();
    }
}

(Tested in Spring 4.3.6)

Create a nested class inside your @Configuration and put there declarations of @Autowired service and @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();
        }
    }
}

Below is your full example updated accordingly.

Notice that you don't have to add explicit reference to BaseConfig.Setup (look at the @Import annotation before Config class - it only refers to BaseConfig itself).

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;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM