赞
踩
记录一下自己学习springboot启动源码的过程
因为内容有点多,所以分成几个部分来写,后面写完会自动修改放到这部分内容中。没上传就是还在学习中
使用的版本是springboot2.7
springboot通过该类进行启动
public static void main(String[] args) {
SpringApplication.run(BackgroundAdminApplication.class, args);
}
接下来不断进入该方法,会出现下面这个初始化
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
// 注意,在进行new SpringApplication的时候,是通过下面这个构造器,然后在进行转到另一个构造器进行的初始化,
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
其中在进行new SpringApplication(primarySource)的时候就已经在进行部分的初始化了,初始化代码如下
@SuppressWarnings({ "unchecked", "rawtypes" }) public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; //primarySource不能为null,这里的primarySource是上面那个构造器传下来的第二个参数primarySources。 Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //根据Class的类型来进行推断WebApplication的类型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); //从spring.factories中获取对应参数类型的,这里的类型是BootstrapRegistryInitialize类型。 this.bootstrapRegistryInitializers = new ArrayList<>( getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
在初始化的时候,伴随着类的创建,也会设定一些默认的属性值
这个是SpringApplication的属性值和进行默认的初始化的初始值
public static final String BANNER_LOCATION_PROPERTY_VALUE = SpringApplicationBannerPrinter.DEFAULT_BANNER_LOCATION; /** * Banner location property key. */ public static final String BANNER_LOCATION_PROPERTY = SpringApplicationBannerPrinter.BANNER_LOCATION_PROPERTY; private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless"; private static final Log logger = LogFactory.getLog(SpringApplication.class); static final SpringApplicationShutdownHook shutdownHook = new SpringApplicationShutdownHook(); private Set<Class<?>> primarySources; private Set<String> sources = new LinkedHashSet<>(); private Class<?> mainApplicationClass; private Banner.Mode bannerMode = Banner.Mode.CONSOLE; private boolean logStartupInfo = true; private boolean addCommandLineProperties = true; private boolean addConversionService = true; private Banner banner; private ResourceLoader resourceLoader; private BeanNameGenerator beanNameGenerator; private ConfigurableEnvironment environment; private WebApplicationType webApplicationType; private boolean headless = true; private boolean registerShutdownHook = true; private List<ApplicationContextInitializer<?>> initializers; private List<ApplicationListener<?>> listeners; private Map<String, Object> defaultProperties; private List<BootstrapRegistryInitializer> bootstrapRegistryInitializers; private Set<String> additionalProfiles = Collections.emptySet(); private boolean allowBeanDefinitionOverriding; private boolean allowCircularReferences; private boolean isCustomEnvironment = false; private boolean lazyInitialization = false; private String environmentPrefix; private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT; private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
推断Web的环境类型。该主要是通过字节码进行判断,这里面使用到了一个ClassUtils的工具类。如果不是reactive的类型SERVLET的类型
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
这个WEBFULX_INDICATOR_CLASS的类型
reactive类型
servlet类型
这里设置的Web类型,后面会根据该web类型进行相对应的初始化。
下面的这个
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
这里讲一下,这个ApplicationContextInitializer是spring的组件的spring-context的一个接口,主要调用在于spring ioc容器刷新(下面会有讲到)的时候进行一次回调。你可以自定义自己的ApplicationContextInitializer然后实现该接口,在通过自己定义的配置spring.factories来定义自己想要加载的自定义的Listener等等,就可以加载到容器中了。
这里讲一下,这个spring.factories默认是在springboot的jar包下面的META-INF下面的一个配置文件
默认会加载的内容,即spring.factories的内容,因为这里面的内容很多,所以就只是展示一小部分,剩下的大家可以自己去看一看。
org.springframework.boot.logging.LoggingSystemFactory=\ org.springframework.boot.logging.logback.LogbackLoggingSystem.Factory,\ org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.Factory,\ org.springframework.boot.logging.java.JavaLoggingSystem.Factory # PropertySource Loaders org.springframework.boot.env.PropertySourceLoader=\ org.springframework.boot.env.PropertiesPropertySourceLoader,\ org.springframework.boot.env.YamlPropertySourceLoader # ConfigData Location Resolvers org.springframework.boot.context.config.ConfigDataLocationResolver=\ org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\ org.springframework.boot.context.config.StandardConfigDataLocationResolver # ConfigData Loaders org.springframework.boot.context.config.ConfigDataLoader=\ org.springframework.boot.context.config.ConfigTreeConfigDataLoader,\ org.springframework.boot.context.config.StandardConfigDataLoader
可以看到各种配置都已经定义好的位置,有什么DataLoader、日志等等。这些都会通过后面有调用getSpringFactoriesInstances(type, new Class<?>[] {});来进行初始化,这里的type就是你要获取的类型,然后在根据该类型的名字进行初始化。
这里讲一下获取的方式和创建这些类的方式,因为很多都是一样的,所以讲一种类型就行了。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
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);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
可以看到,通过loadFctoryNames(type,classLoader)来获取所要创建类型的名字,从哪里获取?就是上面讲到的spring.factories中进行获取。
下面这个是根据类型获取名字,可以看到传进来的是BootstrapRegirstryInitializer,所以获取的名字是spring.factories中的内容。
如果你自己自定义了META-INF/spring.factories,他同样会获取其中的内容
下面是我自己定义了一个自己的spring.factories
往下我们证明一下是从这些spring.factories中进行获取所对应的内容的。
org.springframework.boot.SpringApplicationRunListener=\
com.wsh.springbootbackgroundadmin.initializeConfig.MyApplicationListenerRunner
org.springframework.context.ApplicationListener=\
com.wsh.springbootbackgroundadmin.initializeConfig.MyApplicationListener
org.springframework.context.ApplicationContextInitializer=\
com.wsh.springbootbackgroundadmin.initializeConfig.MyApplicationInitialize
下面证明是从自己定义的spring.factories中进行获取所要内容
上面那一个代码中的,获取names的loadFactoryNames(type,classLoader)点进去
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
//在这里进行返回的时候会进行loadFactories,我们在点进去看看。
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
然后在继续往下走
会发现是调用返回的是一个Map
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { Map<String, List<String>> result = cache.get(classLoader); if (result != null) { return result; } result = new HashMap<>(); try { Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue()); for (String factoryImplementationName : factoryImplementationNames) { result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()) .add(factoryImplementationName.trim()); } } } // Replace all lists with unmodifiable lists containing unique elements result.replaceAll((factoryType, implementations) -> implementations.stream().distinct() .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))); cache.put(classLoader, result); } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } return result; }
然后查看获取url的路径
会发现他是从META-INF/spring.factories中来进行获取的,
然后通过这些内容加载到内存中,然后依次进行获取,
可以看到这个entrySet就是我们在spring.factories中定义的部分内容,然后依次遍历
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-312DHLTA-1671185520075)(C:\Users\wang sheng hui\AppData\Roaming\Typora\typora-user-images\image-20221216163010337.png)]
当我们进行第二次遍历,遍历第二个url的时候,获取的内容就是从spring-boot的jar包中扫描spring.factories中获取相对应的内容了
如下
然后按照刚才的逻辑,加载到resource中,然后在依次进行取出
然后result就进行完全的添加他们的名字和对应的值到了result中,结构是使用的Map,然后在返回map就行了。
这个时候那些spring.factories中的内容已经完全加载进去了,这个时候在调用上面哪一个方法
点进去
@Override
public V getOrDefault(Object key, V defaultValue) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? defaultValue : e.value;
}
然后就会发现他是通过类型的名字获取从spring.factories中加载到map中的名字对应的值,否则就会返回默认值,这里的传入的默认值是null。
因为配置文件中没有对应的BootstrapRegistratyInitializer,所以返回的是null。
在回到刚刚的getSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, Object… args)方法
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//接下来分析这一步,刚刚上面那一部分是通过传入相对应的累心,然后获取spring.factories中对应类型的类,然后在这一步进行创建
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
因为刚刚已经分析过了,没有对应的BootstrapRegistratyInitializer,所以返回的是一个空的names
下面是进入到createSpringFactoriesInstance方法中
@SuppressWarnings("unchecked") 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; }
可以看到在try中,主要是通过name使用反射进行加载,然后在通过反射获取构造器,最后通过反射进行newInstance进行创建对象。
这里面主要使用反射进行创建对象。
因为我么的names为空,所以创建出来的对象也是空的,没有的。
最后那一步AnnotationAwareOrderComparator.sort(instances);是根据注解对实例化的对象进行重新排序排序。
就是根据@Order的优先级来进行排序,数字越低,优先级越高。
初始化语句(在new SpringApplication的时候进行的初始化。
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
可以发现跟刚刚的一样,只不过类型换了。
点进去之后还是熟悉的配方
我们点击一个Listener的内容来进行查看
初始化完成之后的内容
会发现自定义的myApplicationListener和spring-boot中的META-INF/spring.factories的配置内容都在其中
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener
这个就是springboot的初步初始化
ContextCloserApplicationListener,
org.springframework.boot.context.FileEncodingApplicationListener,
org.springframework.boot.context.config.AnsiOutputApplicationListener,
org.springframework.boot.context.config.DelegatingApplicationListener,
org.springframework.boot.context.logging.LoggingApplicationListener,
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener
这个就是springboot的初步初始化
就是配置web的环境类型,然后加载配置文件spring.factories中的内容,然后进行床架对象。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。