上一篇讲到boot在启动的时候,会加载ApplicationContextInitializer和ApplicationListener这两种class的对象,那怎么加载呢。
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 = Thread.currentThread().getContextClassLoader();
// 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);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
我现在才知道,可变成的参数,是可以啥都不传的,还以为必须要有个呢。
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
这个上下文加载器的梗后面再讲,毕竟我们看jvm里面的委托加载模型,貌似没有看到过这个啥
ContextClassLoader。
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
这个是将所有满足type的classname通过classLoader加载到set里面,后面再通过
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);的方式,将这些name加载到服务里面去,然后就可以使用了。
ok,第一步,先找到名字
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
/**
在FACTORIES_RESOURCE_LOCATION下加载
* Load the fully qualified class names of factory implementations of the
* given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
* class loader.
* @param factoryClass the interface or abstract class representing the factory
* @param classLoader the ClassLoader to use for loading resources; can be
* {@code null} to use the default
* @throws IllegalArgumentException if an error occurs while loading factory names
* @see #loadFactories
*/
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//做了缓存,根据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);
}
}
如上,就是加载当前的classpath的路径META-INF/spring.factories下的所有的配置信息,我们随便的找一个看下,如下是boot下面的信息,典型的key value结构,value通过,来进行多value的配置。
PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=
org.springframework.boot.env.PropertiesPropertySourceLoader,
org.springframework.boot.env.YamlPropertySourceLoader
Run Listeners
org.springframework.boot.SpringApplicationRunListener=
org.springframework.boot.context.event.EventPublishingRunListener
Error Reporters
org.springframework.boot.SpringBootExceptionReporter=
org.springframework.boot.diagnostics.FailureAnalyzers
Application Context Initializers
org.springframework.context.ApplicationContextInitializer=
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,
org.springframework.boot.context.ContextIdApplicationContextInitializer,
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
ok,加载完成后
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
通过factoryClassName找出key是factoryClassName的所有的配置信息,那我debug下我的boot启动的时候加载的信息。
上面是加载的所有的ApplicationContextInitializer的class,
image.png
上图是加载的所有的org.springframework.context.ApplicationListener信息。
ok,name找到了,其次肯定就是loader进去了。
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, 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
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
//拿到声明的构造方法,看来必须明确的声明才行
Constructor<?> constructor = instanceClass
.getDeclaredConstructor(parameterTypes);
//反射操作,new出了一个实例,完美,加到了list里面去,就算完成了任务
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
接下来this.mainApplicationClass = deduceMainApplicationClass();
这个方法还真是个有创意的写法,反正我是第一次见,
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
手动的抛一个异常出来,找到方法是main的方法,然后加载这个main方法对应的class,
约定大于配置,main方法的名字不要乱用。
ok ApplicationContextInitializer和ApplicationListener的信息加载完了,顺便MainApplicationClass的内容也找到了,接下来就是将springapplication run起来了。
那留着第三张讲怎么run起来的吧。
反正前两步都是在准备
网友评论