您的位置:首页 > 编程语言 > Java开发

spring-boot启动源码学习-1

2018-07-05 17:59 405 查看

spring-boot启动源码分析-启动初始化

主要对spring-boot的启动流程中的启动初始化进行学习,学习spring boot的启动流程中的相关知识,如果有分析不对的,欢迎指正

1.相关的环境配置

开发工具IDEA

JDK1.8

spring-boot 版本 1.5.10

2.启动的入口

spring boot的启动时通过一个main 函数启动的。例如

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


@SpringBootApplication 是一个组合注解,其中包含EnableAutoConfiguration(自动注入配置文件),ComponentScan(扫描)

2.进入run方法,看下里面什么内容

//1.点击run以后进入如下方法中
public static ConfigurableApplicationContext run(Object source, String... args) {
return run(new Object[] { source }, args);//继续跟踪
}
//2.这个才是spring boot干活的地方,其中sources就是在main()中传递过来的
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}

3.启动时的初始化

在前面我们看到,spring boot 真正启动的时候 是通过**new SpringApplication(sources).run(args)**进行的。那么在new SpringApplication()中都干了什么呢,点击进入

//1.进入SpringApplication的构造函数中
public SpringApplication(Object... sources) {
initialize(sources);//初始化相关内容,继续向下看
}
//2.初始化内容,对这里面的四个主要的步骤分别分析
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = deduceWebEnvironment();//判断运行环境是否是web环境
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));//获取ApplicationContextInitializer
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//获取ApplicationListener
this.mainApplicationClass = deduceMainApplicationClass();// 获取mainApplicationClass
}

3.1 判断运行环境是否是web环境

进入deduceWebEnvironment()方法

//1.该方法主要有个一个变量值 WEB_ENVIRONMENT_CLASSES,该变量值是什么呢。(我直接贴出来)
//WEB_ENVIRONMENT_CLASSES={ "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" }
private boolean deduceWebEnvironment() {
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return false;
}
}
return true;
}
//2.进入ClassUtils.isPresent()方法
public static boolean isPresent(String className, ClassLoader classLoader) {
try {
forName(className, classLoader);
return true;
} catch (Throwable var3) {
return false;
}
}
//3.可看出,就是通过反射查找你的工程中是否还有javax.servlet.Servlet 和 ConfigurableWebApplicationContext这两个类来判断你当前的工程环境

3.2 获取ApplicationContextInitializer

进入getSpringFactoriesInstances(Class<T> type)方法中查看

//1.该方法没做什么实质的工作,继续向下看(可以学习下java中的泛型,也可以学习下spring 怎么对方法进行重载的)
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
//2.做事情的在这个方法里面
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<String> names = new LinkedHashSet<String>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));//查找type类型的class name,为下面创建实例做准备
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);//通过上面获取的class name反射创建实例
AnnotationAwareOrderComparator.sort(instances);//进行排序
return instances;
}


我们下面看下spring中怎么根据type 查找相关的类名

//1.进入loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader)
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));//获取META-INF/spring.factories所在的路径
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));//获取路径下对应的属性值
String factoryClassNames = properties.getProperty(factoryClassName);//获取我们自己想要的class name
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}

至于怎么获取spring.factories下面的路径,怎么通过路径进行解析,这里就不在分析了

通过class name 反射出相关的实例

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;
}

都是通过反射获取相应的实例

3.3 获取ApplicationListener

获取ApplicationListener和上面的获取ApplicationContextInitializer一样。这里就不做描述了

//1.和3.2 中的内容差不多,所以不做分析了
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

3.4 获取mainApplicationClass

这个方法没什么好看的

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;
}

4.到此spring boot启动中有关初始的化的内容已经分析完毕,下篇对run方法进行分析

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Spring Spring Boot