Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 - e.g. 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;
   }   
}
like image 312
levant pied Avatar asked Jun 25 '15 20:06

levant pied


People also ask

What does @PostConstruct do in spring?

When we annotate a method in Spring Bean with @PostConstruct annotation, it gets executed after the spring bean is initialized. We can have only one method annotated with @PostConstruct annotation. This annotation is part of Common Annotations API and it's part of JDK module javax.

Is @PostConstruct deprecated?

In jdk9 @PostConstruct and @PreDestroy are in java. xml. ws. annotation which is deprecated and scheduled for removal.

What is the use of PreDestroy annotation?

The PreDestroy annotation is used on methods as a callback notification to signal that the instance is in the process of being removed by the container. The method annotated with PreDestroy is typically used to release resources that it has been holding.

Can we have multiple PostConstruct?

In a single class, it allows to have more than one @PostConstruct annotated method, and also the order of execution is random.


1 Answers

(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;
    }
}
like image 109
user3078523 Avatar answered Oct 21 '22 23:10

user3078523