假設有這麼一個需求,要求在項目啓動過程當中,完成線程池的初始化,加密證書加載等功能,你會怎樣作?若是沒有想好答案,請接着往下看。今天介紹幾種在Spring Boot中進行資源初始化的方式,幫助你們解決和回答這個問題。java
定義初始化類 MyCommandLineRunnerweb
實現 CommandLineRunner 接口,並實現它的 run() 方法,在該方法中編寫初始化邏輯apache
註冊成 Bean,添加 @Component
註解tomcat
示例代碼以下:app
@Component public class MyCommandLineRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("... init resources by implements CommandLineRunner"); } }
實現了 CommandLineRunner 接口的 Component 會在全部 Spring Beans 初始化完成以後,在 SpringApplication.run() 執行以前完成。下面經過加兩行打印來驗證咱們的測試。ide
@SpringBootAppliction public class DemoApplication { public static void main(String[] args){ System.out.println("... start SpringApplication.run()"); SpringAppliction.run(DemoAppliction.class, args); System.out.println("... end SpringApplication.run()"); } }
控制檯打印結果以下。post
... start SpringApplication.run() . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.2.5.RELEASE) 2020-03-09 10:37:13.537 INFO 13456 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication on DESKTOP-9P44RJ5 with PID 13456 (D:\work\workspace\demo\target\classes started by 78787 in D:\work\workspace\demo) 2020-03-09 10:37:13.539 INFO 13456 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default 2020-03-09 10:37:14.131 INFO 13456 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2020-03-09 10:37:14.137 INFO 13456 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2020-03-09 10:37:14.141 INFO 13456 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.31] 2020-03-09 10:37:14.203 INFO 13456 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2020-03-09 10:37:14.203 INFO 13456 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 638 ms 2020-03-09 10:37:14.307 INFO 13456 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2020-03-09 10:37:14.404 INFO 13456 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2020-03-09 10:37:14.406 INFO 13456 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 1.094 seconds (JVM running for 1.754) ... init resources by implements CommandLineRunner ... end SpringApplication.run()
@Component
註解便可@Component public class MyApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments applicationArguments) throws Exception { System.out.println("...init resources by implements ApplicationRunner"); } }
能夠看到,經過實現 ApplicationRunner 接口,和經過實現 CommandLineRunner 接口均可以完成項目的初始化操做,實現相同的效果。二者以前惟一的區別是 run() 方法中自帶的形參不一樣,在 CommandLineRunner 中只是簡單的 String... args 形參,而 ApplicationRunner 中是包含了 ApplicationArguments 對象,能夠幫助獲取更豐富的項目信息。測試
public interface ApplicationArguments { String[] getSourceArgs(); Set<String> getOptionNames(); boolean containsOption(String name); List<String> getOptionValues(String name); List<String> getNonOptionArgs(); }
若是項目中既有實現了 ApplicationRunner 接口的初始化類,又有實現了 CommandLineRunner 接口的初始化類,那麼會是哪個先執行呢?測試告訴咱們,答案是實現了 ApplicationRunner 接口的初始化類先執行,我想這點卻是不須要你們過度取關注爲何,但若是須要改變兩個初始化類之間的默認執行順序,那麼使用 @Order
註解就能夠幫助咱們解決這個問題。加密
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) @Documented public @interface Order { int value() default 2147483647; }
@Component @Order(1) public class MyCommandLineRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("... init resources by implements CommandLineRunner"); } }
@Component @Order(2) public class MyApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println("... init resources by implements ApplicationRunner"); } }
最終,控制檯打印以下,經過控制檯輸出咱們發現,@Order 註解值越小,該初始化類也就越早執行。spa
....(省略部分代碼) 2020-03-09 10:41:34.858 INFO 20256 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 1.151 seconds (JVM running for 1.821) ... init resources by implements CommandLineRunner ... init resources by implements ApplicationRunner ... end SpringAppliction.run()
使用 @PostConstruct 註解一樣能夠幫助咱們完成資源的初始化操做,前提是這些初始化操做不須要依賴與其餘 Spring Bean 的初始化工做。
@Documented @Retention (RUNTIME) @Target(METHOD) public @interface PostConstruct { }
@Component public class Test { @PostConstruct public void testPostConstruct(){ System.out.println("... post construct "); } }
啓動項目,控制檯打印以下。
2020-03-09 10:49:34.229 INFO 24996 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 603 ms ... post construct 2020-03-09 10:49:34.322 INFO 24996 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2020-03-09 10:49:34.412 INFO 24996 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2020-03-09 10:49:34.414 INFO 24996 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 1.031 seconds (JVM running for 1.697) ... init resources by implements CommandLineRunner ... init resources by implements ApplicationRunner ... end SpringAppliction.run()
綜上,使用 @PostConstruct
註解進行初始化操做的順序是最快的,前提是這些操做不能依賴於其它Bean的初始化完成。經過添加 @Order
註解,咱們能夠改變同層級之間不一樣Bean的加載順序。