Springboot之初始器解析
大约 4 分钟SpringJavaSpring Boot
0x1 简介
首先介绍一个关键的类,ApplicationContextInitializer,查看javadoc知道这是一个容器刷新前的一个回调函数,一般用来向容器注入属性,可以通过order进行排序。
0x2 Demo
编写自定义的初始化器,作用是注入name属性
package club.xwzzy.demo.initializer;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import java.util.HashMap;
import java.util.Map;
public class FirstApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map<String,Object> objectMap = new HashMap<>();
objectMap.put("name","xw");
MapPropertySource mapPropertySource = new MapPropertySource("first",objectMap);
environment.getPropertySources().addLast(mapPropertySource);
System.out.println("first applicationContextInitializer run");
}
}
将自定义的初始化器加载,有几种方式可以实现:
- 第一种,可以自定义SpringApplication加载
package club.xwzzy.demo;
import club.xwzzy.demo.initializer.FirstApplicationContextInitializer;
import javafx.application.Application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(DemoApplication.class);
application.addInitializers(new FirstApplicationContextInitializer());
application.run(args);
}
}
- 第二种方式,resource目录下新建META-INF文件夹,新建spring.factories文件,添加如下代码:
org.springframework.context.ApplicationContextInitializer = club.xwzzy.demo.initializer.FirstApplicationContextInitializer
- 第三种方式,在application.properties配置文件进行配置。
context.initializer.classes=club.xwzzy.demo.initializer.FirstApplicationContextInitializer
0x3 原理分析
从main函数点进去,进入SpringApplication构造器函数,如图。
getSpringFactoriesInstances方法加载并返回initializers集合,
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 = getClassLoader();
// 获取工程名称集合
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 创建实例集合并返回
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
// 根据order对实例进行排序,可使用@Order进行排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
查看SpringFactoriesLoader.loadFactoryNames方法
// 从指定类加载器获取指定类型的 类名称集合
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
查看loadSpringFactorries方法
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 尝试从缓存获取
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
//加载 META-INF/spring.factories的配置文件
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
// 循环遍历 放入缓存 并返回
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
//循环遍历
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
接下来就开始实例化,通过反射,构建实例。
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;
}
至此,初始化器解析至此基本完成。通过application.properties指定初始化器是通过DelegatingApplicationContextInitializer加载,过程与spring.factories方式加载类似。
代码如下:
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.context.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* {@link ApplicationContextInitializer} that delegates to other initializers that are
* specified under a {@literal context.initializer.classes} environment property.
*
* @author Dave Syer
* @author Phillip Webb
*/
public class DelegatingApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
// NOTE: Similar to org.springframework.web.context.ContextLoader
private static final String PROPERTY_NAME = "context.initializer.classes";
private int order = 0;
@Override
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
// 读取配置文件初始化类
List<Class<?>> initializerClasses = getInitializerClasses(environment);
if (!initializerClasses.isEmpty()) {
// 实例对象 并调用initialize方法
applyInitializerClasses(context, initializerClasses);
}
}
private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
String classNames = env.getProperty(PROPERTY_NAME);
List<Class<?>> classes = new ArrayList<>();
if (StringUtils.hasLength(classNames)) {
for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
classes.add(getInitializerClass(className));
}
}
return classes;
}
private Class<?> getInitializerClass(String className) throws LinkageError {
try {
Class<?> initializerClass = ClassUtils.forName(className,
ClassUtils.getDefaultClassLoader());
Assert.isAssignable(ApplicationContextInitializer.class, initializerClass);
return initializerClass;
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load context initializer class [" + className + "]", ex);
}
}
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);
}
private ApplicationContextInitializer<?> instantiateInitializer(Class<?> contextClass,
Class<?> initializerClass) {
Class<?> requireContextClass = GenericTypeResolver.resolveTypeArgument(
initializerClass, ApplicationContextInitializer.class);
Assert.isAssignable(requireContextClass, contextClass,
String.format(
"Could not add context initializer [%s]"
+ " as its generic parameter [%s] is not assignable "
+ "from the type of application context used by this "
+ "context loader [%s]: ",
initializerClass.getName(), requireContextClass.getName(),
contextClass.getName()));
return (ApplicationContextInitializer<?>) BeanUtils
.instantiateClass(initializerClass);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void applyInitializers(ConfigurableApplicationContext context,
List<ApplicationContextInitializer<?>> initializers) {
initializers.sort(new AnnotationAwareOrderComparator());
for (ApplicationContextInitializer initializer : initializers) {
initializer.initialize(context);
}
}
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
}
0x4 总结
- ApplicationContextInitializer是Spring的一个回调接口。在容器刷新前被调用,一般用于向容器中注入属性。
- 自定义ApplicationContextInitializer注册有3中方式 。方式一在SpringApplication中加入,通过硬编码指定;方式二是在resource目录下新建META-INF文件夹,新建spring.factories文件并在文件中指定;方式三是在application.properties中指定。
- 可以通过@Order指定执行顺序