Spring Boot源码-框架初始化
初始化器实战
准备工作
-
在Spring Boot工程中创建三个初始化类,如下:
当实现了Spring Boot的初始化器,会在容器初始化之前调用initializer方法。
@Order(10) public class MyInitializer1 implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext applicationContext) { System.out.println("MyInitializer1....................."); //可以在容器初始化之前注入一些环境变量,或者做一些其他事0.0 ConfigurableEnvironment environment = applicationContext.getEnvironment(); Map<String, Object> map = new HashMap<>(); map.put("key", "value"); MapPropertySource mapPropertySource = new MapPropertySource("myMapProperties", map); environment.getPropertySources().addLast(mapPropertySource); System.out.println("MyInitializer1....................."); } } @Order(5) public class MyInitializer2 implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext applicationContext) { System.out.println("MyInitializer2....................."); } } @Order(20) public class MyInitializer3 implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext applicationContext) { System.out.println("MyInitializer3....................."); } }
三种方式配置初始化器
通过spi机制配置spring.factories
-
在resource/META-INFO/spring.factories文件中配置
org.springframework.context.ApplicationContextInitializer=spring_sources.initialzed.MyInitializer
手动添加初始化器
-
手动创建SpringApplication,并添加initializer
public static void main(String[] args) { /* * 可以使用如下方式启动spring * 传入当前class,spring boot在创建容器之后会将此类注入容器 */ SpringApplication application = new SpringApplication(SpringSourceTestApp.class); //手动添加初始化器 application.addInitializers(new MyInitializer2()); application.run(args); }
yml中配置初始化器
-
配置文件配置初始化器
context: initializer: # 使用该方式配置order注解会失效,实际使用的order是0 classes: spring_sources.initialzed.MyInitializer3
验证
-
验证环境变量是否加入spring,根据Spring预留的扩展接口Aware(后面源码会详细分析)创建Context工具类
@Component public class MySpring implements ApplicationContextAware { @Getter private ApplicationContext context; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } }
-
启动类中获取自定义的环境变量
@SpringBootApplication public class SpringSourceTestApp implements ApplicationRunner { @Autowired private MySpring mySpring; public static void main(String[] args) { /* * 可以使用如下方式启动spring * 传入当前class,spring boot在创建容器之后会将此类注入容器 */ SpringApplication application = new SpringApplication(SpringSourceTestApp.class); application.addInitializers(new MyInitializer2()); application.run(args); } @Override public void run(ApplicationArguments args) throws Exception { MutablePropertySources propertySources = ((ConfigurableApplicationContext) mySpring.getContext()).getEnvironment().getPropertySources(); System.out.println("propertySources "+propertySources); System.out.println(mySpring.getContext().getEnvironment().getProperty("key")); } }
实现了ApplicationRunner接口,会在spring boot启动完成之后调用run方法(源码也会详细分析)
-
启动spring boot,查看控制台输出
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.2.4.RELEASE) MyInitializer3..................... MyInitializer2..................... MyInitializer1..................... propertySources [MapPropertySource {name='server.ports'}, ConfigurationPropertySourcesPropertySource {name='configurationProperties'}, PropertiesPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.yml]'}, MapPropertySource {name='myMapProperties'}] value
可以发现获取到的为我们加入的mapproperties,且属性也能获取到,但是发现并未按设定的顺序输出(用order排序了),而是第三个初始化器最先输出,即最先调用,后面查看源码会发现缘由。
工厂加载机制
SpringFactoriesLoader
- 实现spi机制的核心类,框架内部通用的工厂加载机制
- 从classpath下多个jar包特定位置读取文件并初始化
- 文件内容必须为kv形式,即properties类型
- key必须为抽象类/接口的全限定名,value是实现类的全限定名,多实现用,分隔
工厂加载机制源码跟踪
-
进入run方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
-
接着进入SpringApplication构造器
public SpringApplication(Class<?>... primarySources) { this(null, primarySources); } public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); //通过配置,设置初始化器initializer setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
-
进入关键方法getSpringFactoriesInstances
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { //获取classloader ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates //加载配置的初始化器 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); //通过反射创建出所有对象 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); //对初始化器根据order排序 AnnotationAwareOrderComparator.sort(instances); //返回所有初始化器 return instances; }
此时的type是ApplicationContextInitializer,
-
进入SpringFactoriesLoader.loadFactoryNames(type, classLoader)方法
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { //此时的name为 org.springframework.context.ApplicationContextInitializer String factoryTypeName = factoryType.getName(); //关键,获取key为factoryTypeName类型的初始化器,若没有则返回空list return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); }
-
进入loadSpringFactories(classLoader)方法
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { //从缓存中查找当前loader对应的初始化器,若有则返回(第一次是是没有的,该方法会被调用多次,获取不同类型的配置,如initializer,listener等) MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { /** * public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; * 因此可以看出是利用classloader加载classpath路径下的,META-INF/spring.factories文件 * */ Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); //遍历每个clas下的每个spring.factories while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); //将文件解析为properties(即k-v形式) Properties properties = PropertiesLoaderUtils.loadProperties(resource); //遍历每个key-value键值 for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); //使用工具类,将当前key对应的value用英文逗号(",")切分为数组,并依次遍历 for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { //添加到结果集,并使用factoryTypeName作为key,此时的factoryTypeName为org.springframework.context.ApplicationContextInitializer result.add(factoryTypeName, factoryImplementationName.trim()); } } } //放入缓存,下次再调用则直接返回 cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
-
-
获取到上一步返回的所有在spring.factories配置的实现类之后,进入createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);方法
-
此时的type为ApplicationContextInitializer,names为配置的所有实现类
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> instances = new ArrayList<>(names.size()); for (String name : names) { try { Class<?> instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes); T instance = (T) BeanUtils.instantiateClass(constructor, args); instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex); } } return instances; }
-
上面代码较为简单,使用反射创建ApplicationContextInitializer所有实现类的实例,并返回
-
-
接着进入 AnnotationAwareOrderComparator.sort(instances);排序方法,将所有的initializer根据order排序(可以是@Order注解也可以是继承了Ordered类)
-
最后调用set方法,将所有的初始化器实例赋值个SpringApplication
private List<ApplicationContextInitializer<?>> initializers; public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) { this.initializers = new ArrayList<>(initializers); }
-
上面的loadSpringFactories(classLoader)方法会在Spring Boot启动中多次调用,用来加载不同类型的配置
loadFactories流程图
初始化器解析
作用
- 上下文刷新即refresh方法前调用
- 用来编码设置一些属性变量,通常用在web环境中
- 可以通过order接口进行排序
初始化器被实例化的时机
配置在spring.factories中的初始化器
- 通过以上的loadFactories()方法分析,已经得知,在spring.factories配置的初始化器会在SpringApplication的构造方法中被实例化
手动加入的初始化器
-
首先回顾下手动加入的方法,在启动类中
SpringApplication application = new SpringApplication(SpringSourceTestApp.class); application.addInitializers(new MyInitializer2()); application.run(args);
-
进入SpringApplication#addInitializers方法
public void addInitializers(ApplicationContextInitializer<?>... initializers) { this.initializers.addAll(Arrays.asList(initializers)); } private List<ApplicationContextInitializer<?>> initializers;
以上可见,这种方式较为粗暴,直接往list里面添加初始化器,随后在run()调用过程中,会进入以上的loadFactories()流程,之后会对包括这儿添加的初始化器排序。
通过yml配置的初始化器
-
通过debug,查看方法调用栈的方式寻找yml配置初始化器的初始化时机
-
以之前的为例,在yml配置初始化器MyInitializer3#initialize方法中加入断点,debug启动容器查看调用栈
-
发现是在DelegatingApplicationContextInitializer#applyInitializers中调用的
-
而applyInitializers是在applyInitializerClasses中被调用的
private void applyInitializerClasses(ConfigurableApplicationContext context, List<Class<?>> initializerClasses) { Class<?> contextClass = context.getClass(); List<ApplicationContextInitializer<?>> initializers = new ArrayList<>(); for (Class<?> initializerClass : initializerClasses) { initializers.add(instantiateInitializer(contextClass, initializerClass)); } applyInitializers(context, initializers); }
-
继续查找,发现applyInitializerClasses是在DelegatingApplicationContextInitializer#initialize方法中被调用的,且DelegatingApplicationContextInitializer也实现了ApplicationContextInitializer接口
//类签名 public class DelegatingApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered private int order = 0; @Override public void initialize(ConfigurableApplicationContext context) { ConfigurableEnvironment environment = context.getEnvironment(); List<Class<?>> initializerClasses = getInitializerClasses(environment); if (!initializerClasses.isEmpty()) { applyInitializerClasses(context, initializerClasses); } }
-
因此,简单梳理下逻辑,猜测在启动阶段,首先实例化DelegatingApplicationContextInitializer,之后在调用DelegatingApplicationContextInitializer#initialize方法的时候再实例化通过yml定义的初始化器,并且调用其initialize方法
-
接下来验证是否加载了DelegatingApplicationContextInitializer初始化器,全局搜索spring.factories,在spring-bean这个模块的配置中找到了如下spring.factories的配置
# Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
-
所以,整体流程是通过加载spring.factories的时候实例化了DelegatingApplicationContextInitializer初始化器,随后再调用DelegatingApplicationContextInitializer#initialize方