skip to Main Content

My problem is that I need to get an authentication token before I make a call to an API. The api must be called on start up of application. However I am having trouble in that both calls are made at the same time thus creating an error of not having the authentication token before making the api call.

I basically need the tokenUtilityClass to create the token before instantiating the Paypal class. I have tried the @Preconstruct and the @Lazy annotation but neither are working for me.

I have a boolean value of validToken which returns true once the authentication token has been created.

This is what my Springboot configuration file looks like

@Autowired
private TokenUtilityClass tokenUtilityClass;


  @Bean ResourceConfig resourceConfig() {
      return new ResourceConfig().registerClasses(Version1Api.class); }

  @PostConstruct
  public void postConstruct() {
      tokenUtilityClass.tokenTimer();
  }


@DependsOn("TokenUtilityClass")
@ConditionalOnProperty(name ="tokenUtilityClass.validToken", havingValue ="true")
@Lazy
public Paypal eventPublisherBean() {
    return new Paypal();
}

Would anyone have any ideas about initializing the Paypal class only after the authentication token has been generated.

All help would be appreciated

4

Answers


  1. You are looking for @Order annotation.

    @Component
    @Order(1)
    public class First {
    
        @PostConstruct
        public void init() {
            System.out.println("first");
        }
    }
    
    @Component
    @Order(2)
    public class Second {
    
        @PostConstruct
        public void init() {
            System.out.println("second");
        }
    }
    

    @Order works with both types of declarations: @Component/@Service/@Repository/@Controller and with @Bean

    However, if you have a clear dependency of one bean on the other bean, then use @DependsOn annotation.

    @Component("first")
    public class First {
    
        public First() { // inject dependencies here if any
            System.out.println("The very first thing")
        }
    
    
        @PostConstruct
        public void init() {
            System.out.println("first");
        }
    }
    
    @Component
    @DependsOn({"first"})
    public class Second {
    
        @PostConstruct
        public void init() {
            System.out.println("second");
        }
    }
    

    You can find more info here

    Login or Signup to reply.
  2. It looks like you are sharing the code inside class with @Configuration annotation. It seems you mark TokenUtilityClass with @Component (or similar) annotation.

    The issue with that is that @PostConstruct is connected to your Configuration class, not to TokenUtilityClass. I suggest moving @PostConstruct method to TokenUtilityClass.

    Login or Signup to reply.
  3. You can just have token refreshing functionality and make sure token exists upon creating another bean.

    @Component
    class TokenUtilityClass {
        private String freshToken;
        private Instant freshUntil;
    
        public String getToken() {
            if (freshUntil != null && Instant.now().isBefore(freshUntil)) {
                refreshToken();
            }
            return freshToken;
        }
    
        public void refreshToken() {
            // do something
        }
    }
    
    @Configuration
    class ConfigurationClass {
    
        @Bean public Paypal eventPublisherBean(TokenUtilityClass tokenUtilityClass) {
            String token = tokenUtilityClass.getToken();
            // do something with token and return your bean
            return new Paypal();
        }
    
    }
    
    Login or Signup to reply.
  4. What you declared cannot work :

    @DependsOn("TokenUtilityClass")
    @ConditionalOnProperty(name ="tokenUtilityClass.validToken", havingValue ="true")
    @Lazy
    

    because tokenUtilityClass.validToken is not a property but a bean method while
    ConditionalOnProperty expects to a property.

    Would anyone have any ideas about initializing the Paypal class only
    after the authentication token has been generated.

    Concretely, you cannot achieve it straightly with Spring because beans are instantiated as soon as these are required by other beans in their dependencies.

    But declaring just that :

    @Bean
    @Lazy
    public Paypal eventPublisherBean() {
        return new Paypal();
    }
    

    could work if that bean is never used as a eager loaded dependency of other beans but rather required at the good time for you : after the token was retrieved.
    To achieve that you have two ways :

    1) don’t use dependency injection for PayPal instance but use exclusively bean factory.
    Not tested but it sounds conform to the @Lazy javadoc :

    If this annotation is not present on a @Component or @Bean definition,
    eager initialization will occur. If present and set to true, the @Bean
    or @Component will not be initialized until referenced by another bean
    or explicitly retrieved from the enclosing BeanFactory.

    So inject BeanFactory in the bean responsible to get the token and use that factoryto initialize the bean after the token was retrieved.
    Something like that :

    @Service
    public class TokenUtility {
    
       private BeanFactory factory;
    
       public TokenUtility(BeanFactory factory){
         this.factory = factory;
       }
    
       public Token retrieveToken(){
         // call service to get the token
         //...
         // init the bean now -(it will also invoke its @PostConstruct method)
         PayPal payPal = beanFactory.getBean(PayPal.class);
       }
    
    }
    

    2) A straighter alternative to the BeanFactory is declaring the bean as a lazy dependency :

    @Service
    public class TokenUtility {
    
      @Lazy
      @Autowired
      Paypal paypal;
    
      public Token retrieveToken(){
             // call service to get the token
             //...
             // init the bean by invoking any method on that
              paypal.init();
           }
    }
    

    I created just now a very basic project (Java 11) to test that :
    https://github.com/ebundy/lazy-init-spring-example/.

    When you execute spring-boot:run you should get :

    2020-02-26 09:38:05.883  INFO 7002 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 682 ms
    TokenUtility init with status code=200
    PayPal init
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search