假设有这么一个需求,要求在项目启动过程当中,完成线程池的初始化,加密证书加载等功能,你会怎样作?若是没有想好答案,请接着往下看。今天介绍几种在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的加载顺序。