Running Setup Data on Startup in Spring

转自:https://www.baeldung.com/running-setup-logic-on-startup-in-spring

1. Introduction

In this article we’ll focus on how to run logic at the startup of a Spring application.

2. Running Logic On Startup

Running logic during/after Spring application’s startup is a common scenario, but one that causes multiple problems.

In order to benefit from Inverse of Control, we naturally need to renounce partial control over the application’s flow to the container – which is why instantiation, setup logic on startup, etc needs special attention.

We can’t simply include our logic in the beans’ constructors or call methods after instantiation of any object; we are simply not in control during those processes.

Let’s look at the real-life example:

1

2

3

4

5

6

7

8

9

10

@Component

public class InvalidInitExampleBean {

 

    @Autowired

    private Environment env;

 

    public InvalidInitExampleBean() {

        env.getActiveProfiles();

    }

}

Here, we’re trying to access an autowired field in the constructor. When the constructor is called, the Spring bean is not yet fully initialized. This is problematic because calling not yet initialized fields will of course result inNullPointerExceptions.

Spring gives us a few ways of managing this situation.

2.1. The @PostConstruct Annotation

Javax’s @PostConstruct annotation can be used for annotating a method that should be run once immediately after the bean’s initialization. Keep in mind that the annotated method will be executed by Spring even if there is nothing to inject.

Here’s @PostConstruct in action:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

@Component

public class PostConstructExampleBean {

 

    private static final Logger LOG

      = Logger.getLogger(PostConstructExampleBean.class);

 

    @Autowired

    private Environment environment;

 

    @PostConstruct

    public void init() {

        LOG.info(Arrays.asList(environment.getDefaultProfiles()));

    }

}

In the example above you can see that the Environment instance was safely injected and then called in the @PostConstruct annotated method without throwing a NullPointerException.

2.2. The InitializingBean Interface

The InitializingBean approach works pretty similarly to the previous one. Instead of annotating a method, you need to implement the InitializingBean interface and the afterPropertiesSet() method.

Here you can see the previous example implemented using the InitializingBean interface:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

@Component

public class InitializingBeanExampleBean implements InitializingBean {

 

    private static final Logger LOG

      = Logger.getLogger(InitializingBeanExampleBean.class);

 

    @Autowired

    private Environment environment;

 

    @Override

    public void afterPropertiesSet() throws Exception {

        LOG.info(Arrays.asList(environment.getDefaultProfiles()));

    }

}

2.3. An ApplicationListener

This approach can be used for running logic after the Spring context has been initialized, so we are not focusing on any particular bean, but waiting for all of them to initialize.

In order to achieve this you need to create a bean that implements the ApplicationListener<ContextRefreshedEvent>interface:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

@Component

public class StartupApplicationListenerExample implements

  ApplicationListener<ContextRefreshedEvent> {

 

    private static final Logger LOG

      = Logger.getLogger(StartupApplicationListenerExample.class);

 

    public static int counter;

 

    @Override public void onApplicationEvent(ContextRefreshedEvent event) {

        LOG.info("Increment counter");

        counter++;

    }

}

The same results can be achieved by using the newly-introduced @EventListener annotation:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

@Component

public class EventListenerExampleBean {

 

    private static final Logger LOG

      = Logger.getLogger(EventListenerExampleBean.class);

 

    public static int counter;

 

    @EventListener

    public void onApplicationEvent(ContextRefreshedEvent event) {

        LOG.info("Increment counter");

        counter++;

    }

}

In this example we chose the ContextRefreshedEvent. Make sure to pick an appropriate event that suits your needs.

2.4. The @Bean initMethod attribute

The initMethod property can be used to execute a method after a bean’s initialization.

Here’s what a bean looks like:

1

2

3

4

5

6

7

8

9

10

11

public class InitMethodExampleBean {

 

    private static final Logger LOG = Logger.getLogger(InitMethodExampleBean.class);

 

    @Autowired

    private Environment environment;

 

    public void init() {

        LOG.info(Arrays.asList(environment.getDefaultProfiles()));

    }

}

You can notice that there are no special interfaces implemented nor any special annotations used.

Then, we can define the bean using the @Bean annotation:

1

2

3

4

@Bean(initMethod="init")

public InitMethodExampleBean exBean() {

    return new InitMethodExampleBean();

}

And this is how a bean definition looks in an XML config:

1

2

3

4

<bean id="initMethodExampleBean"

  class="org.baeldung.startup.InitMethodExampleBean"

  init-method="init">

</bean>

2.5. Constructor Injection

If you are injecting fields using Constructor Injection, you can simply include your logic in a constructor:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

@Component

public class LogicInConstructorExampleBean {

 

    private static final Logger LOG

      = Logger.getLogger(LogicInConstructorExampleBean.class);

 

    private final Environment environment;

 

    @Autowired

    public LogicInConstructorExampleBean(Environment environment) {

        this.environment = environment;

        LOG.info(Arrays.asList(environment.getDefaultProfiles()));

    }

}

2.6. Spring Boot CommandLineRunner

Spring boot provides a CommanLineRunner interface with a callback run() method which can be invoked at application startup after the Spring application context is instantiated.

Let’s look at an example:

1

2

3

4

5

6

7

8

9

10

11

12

13

@Component

public class CommandLineAppStartupRunner implements CommandLineRunner {

    private static final Logger LOG =

      LoggerFactory.getLogger(CommandLineAppStartupRunner.class);

 

    public static int counter;

 

    @Override

    public void run(String...args) throws Exception {

        LOG.info("Increment counter");

        counter++;

    }

}

Full source code can be found on Github.

Note: As mentioned in the documentation, multiple CommandLineRunner beans can be defined within the same application context and can be ordered using the @Ordered interface or @Order annotation.

2.7. Spring Boot ApplicationRunner

Similar to CommandLineRunner, Spring boot also provides an ApplicationRunner interface with a run() method to be invoked at application startup. However, instead of raw String arguments passed to the callback method, we have an instance of the ApplicationArguments class.

The ApplicationArguments interface has methods to get argument values that are options and plain argument values. An argument that is prefixed with – – is an option argument.

Let’s look at an example:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

@Component

public class AppStartupRunner implements ApplicationRunner {

    private static final Logger LOG =

      LoggerFactory.getLogger(AppStartupRunner.class);

 

    public static int counter;

 

    @Override

    public void run(ApplicationArguments args) throws Exception {

        LOG.info("Application started with option names : {}",

          args.getOptionNames());

        LOG.info("Increment counter");

        counter++;

    }

}

Full source code can be found on Github.

3. Combining Mechanisms

In order to achieve full control over your beans, you might want to combine the above mechanisms together.

The order of execution is as follows:

  1. The constructor
  2. the @PostConstruct annotated methods
  3. the InitializingBean’s afterPropertiesSet() method
  4. the initialization method specified as init-method in XML

Let’s create a Spring bean that combines all mechanisms:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

@Component

@Scope(value = "prototype")

public class AllStrategiesExampleBean implements InitializingBean {

 

    private static final Logger LOG

      = Logger.getLogger(AllStrategiesExampleBean.class);

 

    public AllStrategiesExampleBean() {

        LOG.info("Constructor");

    }

 

    @Override

    public void afterPropertiesSet() throws Exception {

        LOG.info("InitializingBean");

    }

 

    @PostConstruct

    public void postConstruct() {

        LOG.info("PostConstruct");

    }

 

    public void init() {

        LOG.info("init-method");

    }

}

If you try to instantiate this bean, you will be able to see logs that match the order specified above:

1

2

3

4

[main] INFO o.b.startup.AllStrategiesExampleBean - Constructor

[main] INFO o.b.startup.AllStrategiesExampleBean - PostConstruct

[main] INFO o.b.startup.AllStrategiesExampleBean - InitializingBean

[main] INFO o.b.startup.AllStrategiesExampleBean - init-method

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值