SpringBoot(8-项目启动初始化数据)
|总字数:1.2k|阅读时长:4分钟|浏览量:1|
@PostConstruct
@PostConstruct 是 JSR-250 规范中的注解,用于标记一个方法在依赖注入完成后立即执行。
特点
- 在 bean 完全初始化后(依赖注入完成后)执行
- 方法可以有任意访问修饰符,但不能有参数
使用案例
1 2 3 4 5 6 7 8
| @Component public class MyComponent { @PostConstruct public void init() { System.out.println("@PostConstruct方法执行"); } }
|
InitializingBean
InitializingBean 是 Spring 框架提供的接口,包含一个 afterPropertiesSet() 方法。
特点
- 在 bean 属性设置完成后执行
- 需要实现接口,是 Spring 特有的,与框架耦合
使用案例
1 2 3 4 5 6 7 8
| @Component public class MyBean implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean的afterPropertiesSet()执行"); } }
|
CommandLineRunner
CommandLineRunner 是 Spring Boot 提供的接口,用于在应用启动后执行代码。
特点
- 在所有 bean 初始化完成后执行
- 可以访问原始的应用程序参数(String [] args)
- 可以有多个实现,通过@Order 或实现 Ordered 接口指定顺序
- 在应用上下文完全准备好之后运行
使用案例
1 2 3 4 5 6 7 8 9
| @Component @Order(1) public class MyCommandLineRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("CommandLineRunner执行,参数: " + Arrays.toString(args)); } }
|
ApplicationRunner
ApplicationRunner 与 CommandLineRunner 类似,但提供了更丰富的 ApplicationArguments 来访问参数。
特点
- 功能与 CommandLineRunner 相似
- 提供了更结构化的参数访问方式
- 同样可以有多个实现并指定顺序
- 执行时机与 CommandLineRunner 相同
使用案例
1 2 3 4 5 6 7 8 9 10 11
| @Component @Order(2) public class MyApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println("ApplicationRunner执行"); System.out.println("源参数: " + Arrays.toString(args.getSourceArgs())); System.out.println("选项参数: " + args.getOptionNames()); } }
|
执行顺序
- @PostConstruct
- InitializingBean.afterPropertiesSet()
- ApplicationRunner/CommandLineRunner(可以通过@Order 控制它们之间的顺序)
注意:@PostConstruct 和 InitializingBean.afterPropertiesSet()会根据类名称进行 ASCII 比较,根据顺序执行
使用案例
1 2 3 4 5 6 7 8
| @Component public class MyComponent { @PostConstruct public void init() { System.out.println("@PostConstruct方法执行"); } }
|
1 2 3 4 5 6 7 8
| @Component public class MyBean implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean的afterPropertiesSet()执行"); } }
|
1 2 3 4 5 6 7 8
| @Component public class AComponent { @PostConstruct public void init() { System.out.println("@PostConstruct方法执行"); } }
|
使用场景
- @PostConstruct:简单的初始化逻辑,不依赖其他 bean 的完全初始化
- InitializingBean:Spring 特有的初始化
- CommandLineRunner/ApplicationRunner: 需要在应用完全启动后执行的逻辑,特别是需要访问命令行参数时
区别
@PostConstruct 和 afterPropertiesSet
afterPropertiesSet,顾名思义「在属性设置之后」,调用该方法时,该 bean 的所有属性已经被 Spring 填充。如果我们在某些属性上使用 @Autowired(常规操作应该使用构造函数注入),那么 Spring 将在调用 afterPropertiesSet 之前将 bean 注入这些属性。但 @PostConstruct 并没有这些属性填充限制
所以 InitializingBean.afterPropertiesSet 比使用 @PostConstruct 更安全,因为如果我们依赖尚未自动注入的 @Autowired 字段,则 @PostConstruct 方法可能会遇到 NullPointerExceptions
CommandLineRunner 和 ApplicationRunner
- CommandLineRunner 和 ApplicationRunner 在容器启动的时候会执行一些内容,比如:读取配置文件、数据库连接
- CommandLineRunner 接口的 run()方法接收 String 数组作为参数,即是最原始的参数,没有做任何处理;而 ApplicationRunner 接口的 run()方法接收 ApplicationArguments 对象作为参数,是对原始参数做了进一步的封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Component @Order(value = 1) public class JDDRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println("这个是测试ApplicationRunner接口"); String strArgs = Arrays.stream(arg0.getSourceArgs()).collect(Collectors.joining("|")); System.out.println("Application started with arguments:" + strArgs); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Component public class TestCommandLineRunner implements CommandLineRunner {
@Override public void run(String... args) throws Exception { System.out.println("这个是测试CommandLineRunn接口"); String strArgs = Arrays.stream(args).collect(Collectors.joining("|")); System.out.println("Application started with arguments:" + strArgs); } }
|
注意:
- 可以使用@Order 注解或实现 Ordered 接口按一定顺序执行
- 在 ApplicationContext 容器加载完成之后,会调用 SpringApplication 类的 callRunners 方法,该方法中会获取所有实现了 ApplicationRunner 和 CommandLineRunner 的接口 bean,然后依次执行对应的 run 方法,并且是在同一个线程中执行。如果有某个实现了 ApplicationRunner 或 CommandLineRunner 的接口的 bean 的 run 方法一直循环不返回的话,后续的代码将不会被执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<>(); runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } }
|