一、ApplicationContextInitializer接口是spring容器在执行refreshed之前的一个回调,回调ApplicationContextInitializer接口实现类中的initialize方法。容器加载时会先刷新容器,refreshed方法为容器的刷新方法,当刚加载容器的时候就会执行该方法。而在加载容器之前就会回调initialize方法。
使用步骤:
- 写一个实现类,实现CommandLineRunner接口
- 把该类纳入到spring容器中进行管理
如下程序演示:
1、自定义一个类MyApplicationContextInitializer,实现ApplicationContextInitializer接口
package com.example.demo;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
/*ConfigurableApplicationContext为接口中传入的参数类型,在initialize中可以通过传入的参数执行所需要的操作*/
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("bean的个数:" + applicationContext.getBeanDefinitionCount());
/*下面还可以用applicationContext获取bean*/
}
}
启动类为:
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(App.class);
/*把MyApplicationContextInitializer注入到容器中*/
app.addInitializers(new MyApplicationContextInitializer());
ConfigurableApplicationContext context = app.run(args);
context.close();
}
}
运行启动类,输出结果如下:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.9.RELEASE)
bean的个数:6
2017-12-15 20:13:16.875 INFO 9528 --- [ main] com.example.demo.App : Starting App on DESKTOP-98FEQG9 with PID 9528 (E:\code\springboot\springboot-5\target\classes started by *** in E:\code\springboot\springboot-5)
2017-12-15 20:13:16.879 INFO 9528 --- [ main] com.example.demo.App : No active profile set, falling back to default profiles: default
……
从输出结果可以看出,MyApplicationContextInitializer 中的initialize方法在容器之前进行调用的。
2、创建MyApplicationContextInitializer 类时,就用@Component注解把该类注入到IOC容器中,在容器加载时就不需要显示加载了。
@Component
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("bean的个数:" + applicationContext.getBeanDefinitionCount());
/*下面还可以用applicationContext获取bean*/
}
}
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(App.class);
ConfigurableApplicationContext context = app.run(args);
context.close();
}
}
运行结果同上面结果相同。
3、可以同时指定多个ApplicationContextInitializer实现类,用以在容器加载前进行回调实现类中的initialize方法。在上面示例的基础上继续实现下面的类
@Component
public class MyApplicationContextInitializer2 implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("name:" + applicationContext.getDisplayName());
/*下面还可以用applicationContext获取bean*/
}
}
启动类和上面示例相同,运行启动类,输出结果如下:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.9.RELEASE)
bean的个数:6
name:org.springframework.context.annotation.AnnotationConfigApplicationContext@5fdba6f9
……
从输出结果可以看出,程序在加载容器之前先回调了两个实现类的初始化方法。
二、当spring boot加载完容器后,但还没有执行程序自定义的方法时,也可以回调指定的方法。如果有实现CommandLineRunner或者ApplicationRunner接口,并把实现类纳入到容器中进行管理,当容器加载完后,会回调实现类中的run方法。步骤同上面示例的步骤相同。如下程序示例所示:
1、自定义类MyCommandLineRunner,实现CommandLineRunner接口,并实现接口中的方法。
package com.example.demo;
import java.util.Arrays;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
/*args为获取到的容器中启动时的参数*/
public void run(String... args) throws Exception {
System.out.println("----回调类MyCommandLineRunner中的run方法----");
System.out.println(Arrays.asList(args));
}
}
启动类为:
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(App.class);
/*可以指定args参数,在这个地方为"aa"和"bb"*/
ConfigurableApplicationContext context = app.run("aa", "bb");
context.close();
}
}
运行启动类,输出结果如下:
---MyApplicationRunner---
[aa, bb]
2、在1的实例下再自定义MyApplicationRunner类,实现ApplicationRunner接口,并实现接口中的run方法。
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("---MyApplicationRunner---");
System.out.println(Arrays.asList(args.getSourceArgs()));
}
}
运行启动类,输出结果:
---MyApplicationRunner---
[aa, bb]
----回调类MyCommandLineRunner中的run方法----
[aa, bb]
3、ApplicationRunner接口与CommandLineRunner接口实现的功能相似,都是在容器加载完,执行后续程序之前发挥功能。类似于windows的开机自启功能。但ApplicationRunner功能比CommandLineRunner的功能更全面,因为CommandLineRunner中的run方法传入的参数类型为ApplicationArguments类型的,
public interface ApplicationRunner {
/**
* Callback used to run the bean.
* @param args incoming application arguments
* @throws Exception on error
*/
void run(ApplicationArguments args) throws Exception;
}
而CommandLineRunner接口中的run方法传入的参数为string类型的,
public interface CommandLineRunner {
/**
* Callback used to run the bean.
* @param args incoming main method arguments
* @throws Exception on error
*/
void run(String... args) throws Exception;
}
由于CommandLineRunner 中的run方法的参数类型为string类型的,只能获取到容器启动时传入的参数。而ApplicationRunner 接口中的run方法传入的参数类型为ApplicationArguments类型的,该类型是对容器启动参数的封装。如下所示:在run as -> run configurations -> arguments -> program arguments中设置启动参数为:--name=lzj
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(App.class);
ConfigurableApplicationContext context = app.run(args);
ApplicationArguments argsments = context.getBean(ApplicationArguments.class);
/*getSourceArgs获取所有启动参数,返回一个字符串数组*/ System.out.println(argsments.getSourceArgs().length);
System.out.println(Arrays.asList(argsments.getSourceArgs()));
/*getOptionNames返回所有启动参数的键,然后把键放在set中进行返回*/ System.out.println(argsments.getOptionNames());
/*getOptionValues根据启动参数中的键获取对应的值,返回字符串形式*/ System.out.println(argsments.getOptionValues("name"));
context.close();
}
}
运行启动类,输出结果:
1
[--name=lzj]
[name]
[lzj]
当然也可以在程序中指定启动参数,如:
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(App.class);
ConfigurableApplicationContext context = app.run("aa", "bb");
ApplicationArguments argsments = context.getBean(ApplicationArguments.class);
System.out.println(argsments.getSourceArgs().length);
System.out.println(Arrays.asList(argsments.getSourceArgs()));
context.close();
}
}
运行启动类,输出:
2
[aa, bb]
这种情况下就不方便通过键值的形式获取参数了。此时即使在run as -> run configurations -> arguments -> program arguments中设置启动参数也管用了,因为会被程序中设置的启动参数“aa”和”bb”覆盖掉。