@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);         }     } }
  |