当前位置:   article > 正文

springboot启动源码分析1——初步初始化_bootstrapregistryinitializer

bootstrapregistryinitializer

Spirngboot启动源码分析1——初步初始化

springboot启动源码分析1——初步初始化

springboot启动源码分析2——run方法分析

springboot启动源码分析3——环境配置

springboot启动源码分析4——刷新容器

springboot启动源码分析5——后续处理内容

Spring Boot启动源码分析6——refresh的invokeBeanFactoryPostProcessors方法详解

springboot启动源码分析7——自动配置

springboot启动源码分析8——aop

概述

记录一下自己学习springboot启动源码的过程
因为内容有点多,所以分成几个部分来写,后面写完会自动修改放到这部分内容中。没上传就是还在学习中
在这里插入图片描述

​使用的版本是springboot2.7

启动springboot

springboot通过该类进行启动

public static void main(String[] args) {
        SpringApplication.run(BackgroundAdminApplication.class, args);
}
  • 1
  • 2
  • 3

接下来不断进入该方法,会出现下面这个初始化

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}	


  • 1
  • 2
  • 3
  • 4
  • 5

// 注意,在进行new SpringApplication的时候,是通过下面这个构造器,然后在进行转到另一个构造器进行的初始化,

public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
}
  • 1
  • 2
  • 3

其中在进行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();
}	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在初始化的时候,伴随着类的创建,也会设定一些默认的属性值

这个是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;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

确定Web的环境类型

推断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;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这个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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

可以看到各种配置都已经定义好的位置,有什么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;
	}	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

可以看到,通过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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在这里插入图片描述

下面证明是从自己定义的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());
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

然后在继续往下走

加载配置文件的名字

会发现是调用返回的是一个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;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

然后查看获取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;
    }   
  • 1
  • 2
  • 3
  • 4
  • 5

然后就会发现他是通过类型的名字获取从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;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

因为刚刚已经分析过了,没有对应的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;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

可以看到在try中,主要是通过name使用反射进行加载,然后在通过反射获取构造器,最后通过反射进行newInstance进行创建对象。

这里面主要使用反射进行创建对象。

因为我么的names为空,所以创建出来的对象也是空的,没有的。

最后那一步AnnotationAwareOrderComparator.sort(instances);是根据注解对实例化的对象进行重新排序排序。

就是根据@Order的优先级来进行排序,数字越低,优先级越高。

ApplicationContextInitializer的初始化

初始化语句(在new SpringApplication的时候进行的初始化。

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  • 1

可以发现跟刚刚的一样,只不过类型换了。

点进去之后还是熟悉的配方
在这里插入图片描述

在这里插入图片描述

我们点击一个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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这个就是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中的内容,然后进行床架对象。

springboot启动源码分析2——run方法分析

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/479538
推荐阅读
相关标签
  

闽ICP备14008679号