Spring Boot 2 practice: customize the startup and operation logic

Posted by russ8 on Sun, 03 Nov 2019 05:11:57 +0100

1. Preface

I don't know if you have received this kind of demand, and immediately implement some logic after the project is started. For example, cache preheating, or broadcasting after online, etc. Maybe not now but in the future. Think about your possible operation, write an interface and go online. Can I adjust it once? NO! NO! NO! This kind of junior talent. Today, I'll tell you that the operation makes your code more elegant and higher.

2. CommandLineRunner interface

 package org.springframework.boot;
 
 import org.springframework.core.Ordered;
 import org.springframework.core.annotation.Order;
 
 /**
  * Interface used to indicate that a bean should <em>run</em> when it is contained within
  * a {@link SpringApplication}. Multiple {@link CommandLineRunner} beans can be defined
  * within the same application context and can be ordered using the {@link Ordered}
  * interface or {@link Order @Order} annotation.
  * <p>
  * If you need access to {@link ApplicationArguments} instead of the raw String array
  * consider using {@link ApplicationRunner}.
  *
  * @author Dave Syer
  * @see ApplicationRunner
  */
 @FunctionalInterface
 public interface CommandLineRunner {
 
 	/**
 	 * Callback used to run the bean.
 	 * @param args incoming main method arguments
 	 * @throws Exception on error
 	 */
 	void run(String... args) throws Exception;
 
 }

The function of CommandLineRunner is to execute multiple spring beans of CommandLineRunner type defined in the same application context in the order of tags after springApplication is started. If you want to receive args as an array instead, you can use another interface instead of org.springframework.boot.ApplicationRunner.

Talk is heap show your code.

2.1 implementation of CommandLineRunner with high priority

 package cn.felord.begin;
 
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.CommandLineRunner;
 import org.springframework.core.Ordered;
 import org.springframework.stereotype.Component;
 
 /**
  * High priority specifies the priority by implementing the interface {@ link Ordered}
  *  Command line test parameters -- foo=bar --dev.name = java,springboot
  * @author Felordcn
  * @since 2019/6/17 23:06
  */
 @Slf4j
 @Component
 public class HighOrderCommandLineRunner implements CommandLineRunner , Ordered {
     @Override
     public void run(String... args) throws Exception {

       log.info("i am  highOrderRunner");
     }
 
     @Override
     public int getOrder() {
         return Ordered.HIGHEST_PRECEDENCE;
     }
 } 

2.2 implementation of CommandLineRunner with low priority:

 package cn.felord.begin;
 
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.CommandLineRunner;
 import org.springframework.core.Ordered;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Component;
 
 /**
  * Low priority: specify the priority by annotation {@ link Order}
  * 64 larger than the optimal indicates that the execution will be performed after {@ link HighOrderCommandLineRunner}
  *
  * @author Felord
  * @since 2019/6/17 23:07
  */
 @Slf4j
 @Order(Ordered.HIGHEST_PRECEDENCE + 64)
 @Component
 public class LowOrderCommandLineRunner implements CommandLineRunner {
     @Override
     public void run(String... args) throws Exception {
         log.info("i am  lowOrderRunner");
     }
 }

2.3 use ApplicationRunner to achieve the lowest priority:

 package cn.felord.begin;
 
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.ApplicationArguments;
 import org.springframework.boot.ApplicationRunner;
 import org.springframework.core.Ordered;
 import org.springframework.stereotype.Component;
 import org.springframework.util.CollectionUtils;
 
 import java.util.List;
 
 /**
  * Lowest priority implementation
  * @author Felordcn
  * @since 2019/6/18 22:13
  */
 @Slf4j
 @Component
 public class DefaultApplicationRunner implements ApplicationRunner, Ordered {
     @Override
     public void run(ApplicationArguments args) throws Exception {
 
         log.info("i am applicationRunner");
         
     }
 
     @Override
     public int getOrder() {
         return Ordered.HIGHEST_PRECEDENCE+65;
     }
 }

After starting springboot, the console prints out the execution result:

 2019-11-02 21:18:14.603  INFO 10244 --- [           main] c.f.begin.HighOrderCommandLineRunner   : i am  highOrderRunner
 2019-11-02 21:18:14.604  INFO 10244 --- [           main] c.f.begin.LowOrderCommandLineRunner    : i am  lowOrderRunner
 2019-11-02 21:18:14.604  INFO 10244 --- [           main] c.f.begin.DefaultApplicationRunner     : i am applicationRunner

3. Advanced operation - read the parameters of starting injection through the Spring Boot command line

Achieve the expected results of our opening article. So what's the difference between these two interfaces Spring officials don't have enough to eat. There should be some differences between them. According to the interface method run method, we can see that the parameters are different. In addition, we can learn how Spring Boot transfers the additional parameters to the main method by executing Java jar on the command line. The rules are as follows

  • The format of key value pair is -- K=V multiple separated by spaces
  • Values separated by spaces Open the main method configuration item in the idea development tool and configure it as follows. Other ide tools are the same. The parameter content is:

--foo=bar --dev.name = java springboot

The HighOrderCommandLineRunner prints the following args parameters:

 package cn.felord.begin;
 
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.CommandLineRunner;
 import org.springframework.core.Ordered;
 import org.springframework.stereotype.Component;
 
 /**
  * High priority specifies the priority by implementing the interface {@ link Ordered}
  *  Command line test parameters -- foo=bar --dev.name = java,springboot
  * @author dax
  * @since 2019/6/17 23:06
  */
 @Slf4j
 @Component
 public class HighOrderCommandLineRunner implements CommandLineRunner , Ordered {
     @Override
     public void run(String... args) throws Exception {
 
         for (String arg : args) {
             System.out.println("arg = " + arg);
         }
 
       log.info("i am  highOrderRunner");
     }
 
     @Override
     public int getOrder() {
         return Ordered.HIGHEST_PRECEDENCE;
     }
 }

Then let's explore the application arguments of DefaultApplicationRunner:

 package cn.felord.begin;
 
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.ApplicationArguments;
 import org.springframework.boot.ApplicationRunner;
 import org.springframework.core.Ordered;
 import org.springframework.stereotype.Component;
 import org.springframework.util.CollectionUtils;
 
 import java.util.List;
 
 /**
  * @author Felord
  * @since 2019/6/18 22:13
  */
 @Slf4j
 @Component
 public class DefaultApplicationRunner implements ApplicationRunner, Ordered {
     @Override
     public void run(ApplicationArguments args) throws Exception {
 
         log.info("i am applicationRunner");
 
         args.getOptionNames().forEach(System.out::println);
         System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");
         String[] sourceArgs = args.getSourceArgs();
 
         if (sourceArgs!=null){
             for (String sourceArg : sourceArgs) {
                 System.out.println("sourceArg = " + sourceArg);
             }
         }
 
 
         System.out.println("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
         List<String> foo = args.getOptionValues("foo");
         if (!CollectionUtils.isEmpty(foo)){
             foo.forEach(System.out::println);
         }
 
         System.out.println("++++++++++++");
         List<String> nonOptionArgs = args.getNonOptionArgs();
         System.out.println("nonOptionArgs.size() = " + nonOptionArgs.size());
         nonOptionArgs.forEach(System.out::println);
     }
 
     @Override
     public int getOrder() {
         return Ordered.HIGHEST_PRECEDENCE+65;
     }
 }

Restarting the Spring Boot console prints out the results:

 
 arg = --foo=bar
 arg = --dev.name=Little farmer
 arg = java
 arg = springboot
 2019-11-02 21:18:14.603  INFO 10244 --- [           main] c.f.begin.HighOrderCommandLineRunner   : i am  highOrderRunner
 2019-11-02 21:18:14.604  INFO 10244 --- [           main] c.f.begin.LowOrderCommandLineRunner    : i am  lowOrderRunner
 2019-11-02 21:18:14.604  INFO 10244 --- [           main] c.f.begin.DefaultApplicationRunner     : i am applicationRunner
 dev.name
 foo
 >>>>>>>>>>>>>>>>>>>>>>>>>>
 sourceArg = --foo=bar
 sourceArg = --dev.name=Little farmer
 sourceArg = java
 sourceArg = springboot
 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 bar
 ++++++++++++
 nonOptionArgs.size() = 2
 java
 springboot

We found that we can use these two interfaces to read the Spring Boot command line parameters In fact, we can also use @ Value annotation to read it. We will not explain it here. If you are interested, you can try it yourself The difference between application runner and command line runner is clear from the console.

4. Differences between applicationrunner and CommandLineRunner

From the log above, we know that arg = is printed for the args array of CommandLineRunner. Only the above parameters are parsed into the original array with the rule of space. The application runner is more refined. You can know from printing that ApplicationArguments provides some very useful parameter parsing methods:

  • args.getOptionNames() is to get the K in the key value pair -- K=V
  • args.getOptionValues("foo") is used to get the value V of the key value pair through K
  • args.getSourceArgs() is equivalent to args array of CommandLineRunner
  • args.getNonOptionArgs() is the worst way to get a single dog

Note that the problem of null pointers is handled when parsing ApplicationArguments.

5. summary

Today we talk about CommandLineRunner and ApplicationRunner. It solves the problem of how to execute some logic when Spring Boot starts and how to arrange the priority order of multiple startup logic. At the same time, we take a step further and read the Spring Boot entry parameters through these two methods. Furthermore, the subtle differences between the two interfaces are clarified. I hope it will help you.

Pay attention to the public address: Felordcn for more information

Personal blog: https://felord.cn

Topics: Programming Java Spring SpringBoot Lombok