介绍
类名
org.springframework.context.ApplicationContextInitializer
Spring容器刷新之前执行的回调函数
作用
向Spring容器中注册属性(SpringBoot留的一个扩展点,用于添加自定义属性值)
实现方式
方式一
1. // 实现 ApplicationContextInitializer 接口
// FirstInitializer.java
@Slf4j
@Order(1)
public class FirstInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map<String,Object> map = new HashMap<>();
map.put("first","value1");
MapPropertySource propertySource = new MapPropertySource("first",map);
environment.getPropertySources().addLast(propertySource);
log.info("first initializer done!");
}
}
2. // resource 目录下新建 META-INF文件夹,然后新建spring.factories 文件(之后讲解原因)
org.springframework.context.ApplicationContextInitializer=上边新建类FirstInitializer的全路径名
接下来采用 environment 验证:
private final Environment environment;
public InitializerController(Environment environment) {
this.environment = environment;
}
@GetMapping("/{key}")
public ResponseEntity<String> initializer(@PathVariable String key){
try {
log.info("属性:{},对应value为:{}",key,Optional.ofNullable(environment.getProperty(key)).orElse("属性不存在"));
} catch (Exception e){
e.printStackTrace();
}
return new ResponseEntity<>(HttpStatus.OK);
}
输出结果:属性:first,对应value为:value1
方式二
采用硬编码的方式,在启动类主动注册
// SecondInitializer.java
public class SecondInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map<String,Object> map = new HashMap<>();
map.put("second","value2");
MapPropertySource propertySource = new MapPropertySource("first",map);
environment.getPropertySources().addLast(propertySource);
log.info("second initializer done!");
}
}
// 启动类
public class SbCodeApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(SbCodeApplication.class);
application.addInitializers(new SecondInitializer());
application.run(args);
}
}
使用方式1的验证,同样可以获取到预期的结果:属性:second,对应value为:value2
方式三
application.yml 文件中指定 context.initializer.classes : 自定义初始化器全路径
,使用方式1的验证,同样可以获取到预期的结果:属性:third,对应value为:value3
方式对比
日志:
INFO 6364 --- [ main] c.l.s.code.Initializer.ThirdInitializer : third initializer done!
INFO 6364 --- [ main] c.l.s.code.Initializer.FirstInitializer : first initializer done!
INFO 6364 --- [ main] c.l.s.c.Initializer.SecondInitializer : second initializer done!
第三种方式,在application.yml配置文件中指定自定义初始化器,优先级最高
原理
加载机制
Debug SpringApplication.java

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// <1> 使用SpringFactoryLoader 获取所有的实现初始化器的类名
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// <2> 获取类对应的实例对象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// <3> 排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
SpringFactoriesLoader
介绍
:从classpath下多个jar包特定位置读取并初始化类,实现通用工厂类加载,实现扩展类加载
工作原理
:
// 对应以上<1>的过程
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
// 获取类名为 factoryType的所有实现类的String
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 从缓存获取
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 扫描classpath路径下所有的文件: "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();
// 将spring.factories 转为UrlResource
UrlResource resource = new UrlResource(url);
// resource 转为可处理的kv对象
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
// 处理为 <factoryTypeName,List<实现类路径>>
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
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);
}
}
// 对应以上 <2> 的过程
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;
}
通过以上<1><2><3>
获取所有实现初始化器的实例对象
调用时机
获取到实例对象以后,如何使用呢?Debug SpringApplication.run(SbCodeApplication.class,args) run
方法
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 准备上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
// 加载初始化器实现
applyInitializers(context);
listeners.contextPrepared(context);
......
}
// 循环遍历初始化器实现
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
网友评论