本文最后更新于:February 13, 2023 pm
SpringBoot框架中有两个非常重要的策略:开箱即用和约定优于配置。其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。
目录 今天在启动项目时,看见有SQL查询语句自动执行了。然后就 ‘网购’ 了一下,然后进行了一下整理。
启动类 这是最简单,也是最容易想到的办法。就是在启动类中进行添加代码。如下:
@SpringBootApplication public class DemoApplication { public static void main (String[] args) { SpringApplication.run(DemoApplication.class, args); System.out.println("启动自动执行..." ); } }
除此之外,SpringBoot给我们提供了两个接口来帮助我们实现这种需求,他们的执行时机为容器启动完成的时候。
ApplicationRunner
CommandLineRunner
当接口有多个实现类时,提供了@order注解实现自定义执行顺序,也可以实现Ordered接口来自定义顺序。
ApplicationRunner
实现 ApplicationRunner 接口,重写 run() 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.tothefor.mqdemo;import org.springframework.boot.ApplicationArguments;import org.springframework.boot.ApplicationRunner;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;@Component @Order(1) public class ApplicationRunnerStartService implements ApplicationRunner { @Override public void run (ApplicationArguments args) throws Exception { System.out.println("ApplicationRunner 自动执行..." ); } }
CommandLineRunner
实现 CommandLineRunner 接口,重写 run() 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.tothefor.mqdemo;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.CommandLineRunner;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;@Component @Order(10) public class CommandLineRunnerStartService implements CommandLineRunner { @Override public void run (String... args) throws Exception { System.out.println("CommandLineRunner 自动执行..." ); } }
比较
当前面两者均存在时如何执行?
同等级下,ApplicationRunner前,CommandLineRunner后。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 @Component @Order(1) public class ApplicationRunnerStartService implements ApplicationRunner { @Override public void run (ApplicationArguments args) throws Exception { System.out.println("ApplicationRunner-1 自动执行..." ); } }@Component @Order(2) public class ApplicationRunnerStartService2 implements ApplicationRunner { @Override public void run (ApplicationArguments args) throws Exception { System.out.println("ApplicationRunner-2 自动执行..." ); } }@Component @Order(1) public class CommandLineRunnerStartService implements CommandLineRunner { @Override public void run (String... args) throws Exception { System.out.println("CommandLineRunner-1 自动执行..." ); } }@Component @Order(2) public class CommandLineRunnerStartService2 implements CommandLineRunner { @Override public void run (String... args) throws Exception { System.out.println("CommandLineRunner-2 自动执行..." ); } }
输出结果:
ApplicationRunner-1 自动执行... CommandLineRunner-1 自动执行... ApplicationRunner-2 自动执行... CommandLineRunner-2 自动执行...
总结:
先按照Order排序,越小越在前。
在Order相同的情况下,ApplicationRunner 在 CommandLineRunner的前面。
InitializingBean
实现 InitializingBean 接口,重写 afterPropertiesSet() 方法。
package com.tothefor.mqdemo;import org.springframework.beans.factory.InitializingBean;import org.springframework.stereotype.Service;@Service public class UserService implements InitializingBean { @Override public void afterPropertiesSet () { System.out.println("InitializingBean 自动执行..." ); } }
三者比较 代码同上。就看输出结果:
InitializingBean 自动执行... ... ApplicationRunner-1 自动执行... CommandLineRunner-1 自动执行... ApplicationRunner-2 自动执行... CommandLineRunner-2 自动执行...
自定义配置类 首先随便写一个类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.tothefor.mqdemo;import lombok.Data;@Data public class TestPeron { private String name; private String age; public TestPeron () { System.out.println("Person NoConstructor" ); } public void customInit () { System.out.println("Person init-method" ); } public void customDestroy () { System.out.println("Person destroy-method" ); } }
这里很显然只是一个普通的java类,拥有一个无参构造和另外两个方法。 需要注意的是,这里的init和destroy两个方法名实际上是可以随意取的,不叫这个也没有问题,只不过算是一种约定俗称。 另外我们也知道,这个默认构造方法也可以不要的,因为会隐式创建,但是为了更清楚的看到init和destroy什么时候执行,我们就显示的写出来。 创建好了这个类,就可以使用@Bean注解的方式指定两个方法,让他们生效。
然后我们再写一个配置类:
package com.tothefor.mqdemo;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration public class BeanConfig { @Bean(destroyMethod = "customDestroy", initMethod = "customInit") public TestPeron lifeCycleBean () { TestPeron lifeCycleBean = new TestPeron(); return lifeCycleBean; } }
initMethod:指定了初始化方法。和对应的类中的方法名称对应。
destroyMethod:指定销毁时的方法。和对应的类中的方法名称对应。
然后启动启动类,打印如下:
Person NoConstructor Person init-method
这时并没有执行销毁方法,只有关闭时才会执行。关闭后打印:
其他
Spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中通过init-method指定,两种方式可以同时使用。
实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率要高一点,但是init-method方式消除了对spring的依赖。
如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。
综合 将上面的5种方式一起使用,打印如下:
InitializingBean 自动执行... Person NoConstructor Person init-method ApplicationRunner-1 自动执行... CommandLineRunner-1 自动执行... 启动自动执行... Person destroy-method
所以,以上可以看出执行顺序为:
InitializingBean -> 自定义配置类 -> ApplicationRunner -> CommandLineRunner -> 启动类