Springboot初始化过程(1.5.9.RELEASE)(一)

自带的启动类

@SpringBootApplication
public class SpringbootDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootDemoApplication.class, args);
    }
}

只有⼀个@SpringBootApplication 注解 和 调⽤了SpringApplication#run⽅法.那么我们先来解析SpringApplication的run⽅法 。

解析

1.⾸先调⽤了org.springframework.boot.SpringApplication#run(Object, String...) ⽅法.代码如下:

public static ConfigurableApplicationContext run(Object source, String... args) {
		return run(new Object[] { source }, args);
	}

接着调⽤如下代码:

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
    return new SpringApplication(sources).run(args);
}

可以发现 ⾸先初始化了SpringApplication,然后调⽤其实例⽅法:run。


2.在 SpringApplication 的构造器中,调⽤了 initialize ⽅法.
 

public SpringApplication(Object... sources) {
    initialize(sources);
}

3.SpringApplication#initialize⽅法代码如下:

private void initialize(Object[] sources) {
		if (sources != null && sources.length > 0) {
			this.sources.addAll(Arrays.asList(sources));
		}
		this.webEnvironment = deduceWebEnvironment();
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

可以看到做了如下5件事:
1. 如果sources⻓度⼤于0的话,加⼊到SpringApplication的sources中,该sources是⼀个LinkedHashSet.
2. 调⽤deduceWebEnvironment⽅法判断是否是web环境
3. 设置initializers.
4. 设置Listeners.
5. 设置mainApplicationClass.

4. deduceWebEnvironment代码如下:

private boolean deduceWebEnvironment() {
		for (String className : WEB_ENVIRONMENT_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return false;
			}
		}
		return true;
	}

 WEB_ENVIRONMENT_CLASSES:

private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };

可以发现会调⽤ClassUtils类的isPresent⽅法,检查classpath中是否存在javax.servlet.Servlet类和
org.springframework.web.context.ConfigurableWebApplicationContext类,如果存在的话,返回true.否则返回false。

特别说明下ClassUtils.isPresent(String className, ClassLoader classLoader):类是否存在

public static boolean isPresent(String className, ClassLoader classLoader) {
        try {
            forName(className, classLoader);
            return true;
        } catch (Throwable var3) {
            return false;
        }
    }

具体可以看下面文章:Spring源码学习(四)——ClassUtils.forName()

 5. 在设置Initializers时⾸先调⽤getSpringFactoriesInstances⽅法加载ApplicationContextInitializer.然后直接赋值给initializers.代码如下:

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}

转⽽调⽤如下代码:

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// Use names and ensure unique to protect against duplicates
        // 使⽤Set保存names来避免重复元素
		Set<String> names = new LinkedHashSet<String>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 根据names来进⾏实例化
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
        // 对实例进⾏排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

该⽅法逻辑如下:
  1. ⾸先获得ClassLoader.
  2. 调⽤SpringFactoriesLoader#loadFactoryNames进⾏加载,然后放⼊到LinkedHashSet进⾏去重.
  3. 调⽤createSpringFactoriesInstances进⾏初始化
  4. 排序
其中SpringFactoriesLoader#loadFactoryNames代码如下:

ublic static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        // 获取包含包名的工厂类名称
        String factoryClassName = factoryClass.getName();

        try {
            // 找到的每个 META-INF/spring.factories 文件都是一个 Properties 文件,将其内容加载到一个 Properties 对象然后处理其中的每个属性
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            ArrayList result = new ArrayList();

            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                String factoryClassNames = properties.getProperty(factoryClassName);
                // 将逗号分割的属性值逐个取出,然后放到 结果result 中去
                result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
            }

            return result;
        } catch (IOException var8) {
            throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
        }
    }

逻辑如下:
  1. 获得factoryClassName,对于当前来说factoryClassName =org.springframework.context.ApplicationContextInitializer.
  2. 通过传⼊的classLoader加载META-INF/spring.factories⽂件.
  3. 通过调⽤PropertiesLoaderUtils#loadProperties将其转为Properties.
  4. 获得factoryClassName对应的值进⾏返回。
对于当前来说,由于我们只加⼊了spring-boot-starter-web的依赖,因此会加载如下的配置:
1. 在spring-boot/META-INF/spring.factories中.org.springframework.context.ApplicationContextInitializer值如下:

org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer


2. 在spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories中:

org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

因此会加载6个。
 

 SpringApplication#createSpringFactoriesInstances⽅法如下:

private <T> List<T> createSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
			Set<String> names) {
		List<T> instances = new ArrayList<T>(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;
	}

逻辑如下:遍历传⼊的names,也就是之前通过SpringFactoriesLoader加载的类名.通过遍历,依次调⽤其构造器进⾏初始化.加⼊到
instances.然后进⾏返回。

对于当前场景来说:
ConfigurationWarningsApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer
初始化没有做任何事.
ContextIdApplicationContextInitializer在初始化时.会获得spring boot的应⽤名.搜索路径如下:
1. spring.application.name
2. vcap.application.name
3. spring.config.name
4. 如果都没有配置的话,返回application.
代码如下:
 

private static final String NAME_PATTERN = "${spring.application.name:${vcap.application.name:${s
pring.config.name:application}}}";
public ContextIdApplicationContextInitializer() {
    this(NAME_PATTERN);
}
public ContextIdApplicationContextInitializer(String name) {
    this.name = name;
}

 

6. 设置SpringApplication#setListeners时,还是同样的套路.调⽤getSpringFactoriesInstances加载META-INF/spring.factories中配置的org.springframework.context.ApplicationListener. 对于当前来说.加载的类如下:

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.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener

这些类在构造器中都没有做任何事。

 

7. 调⽤SpringApplication#deduceMainApplicationClass⽅法.获得应⽤的启动类.该⽅法通过获取当前⽅法调⽤栈,找到main函数的
类.代码如下:

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;
	}
//StackTraceElement表示StackTrace(堆栈轨迹)中的一个方法对象,属性包括方法的类名、方法名、文件名以及调用的行数。
public final class StackTraceElement implements java.io.Serializable {
    // Normally initialized by VM (public constructor added in 1.5) 
    private String declaringClass;
    private String methodName;
    private String fileName;
    private int  lineNumber;
}

 

流程图如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值