spring-boot源碼分析(1)--從main方法開始

--------------------------基與spring-boot-2.0.0.BUILD-SNAPSHOT--------------------------------------java

正常來講,咱們一個spring boot 入門程序會大概這麼寫react

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

這一節咱們來分析一下SpringApplication.run()都作了什麼web

-----------------------------------------------------------------------------------------------------------spring

首先run重載了幾個方法,這個就是咱們調用run方法的入口數組

public static ConfigurableApplicationContext run(Class primarySource,
			String... args) {
       //在這裏new一個Class數組,primarySource在本例子=App.class
		return run(new Class[] { primarySource }, args);
	}
public static ConfigurableApplicationContext run(Class[] primarySources,
			String[] args) {
      //此處先根據Class數組new一個SpringApplication(),而後執行run方法
		return new SpringApplication(primarySources).run(args);
	}

剩下的run方法等下再看,先看new SpringApplication()的過程緩存

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
        //此處的resourceLoader=null
		this.resourceLoader = resourceLoader;
        //斷言sources不能爲空
		Assert.notNull(primarySources, "PrimarySources must not be null");
        //將class數組構建成一個LinkedHashSet
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        //web application 可能的枚舉類型
		this.webApplicationType = deduceWebApplication(); -- >1
        //實例化全部可用的ApplicationContextInitializer
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));-- >2
        //實例化全部可用的ApplicationListener
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));-- >3

		this.mainApplicationClass = deduceMainApplicationClass();-- >4
	}
 // 1< -- 推斷可能的webApplication類型,並返回對應的枚舉值
 private WebApplicationType deduceWebApplication() {
        
        //REACTIVE_WEB_ENVIRONMENT_CLASS=org.springframework.web.reactive.DispatcherHandler
        //MVC_WEB_ENVIRONMENT_CLASS=org.springframework.web.servlet.DispatcherServlet
		if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
				&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
            //application應該運行在reactive web application且應該啓動一個嵌入的reactive web server
			return WebApplicationType.REACTIVE;
		}
        //private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
        //"org.springframework.web.context.ConfigurableWebApplicationContext" }
		for (String className : WEB_ENVIRONMENT_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
                //application不該該不該該運行一個web application且不啓動嵌入的web server
				return WebApplicationType.NONE;
			}
		}
        //application應該運行在一個基於servlet的web application且應該啓動一個嵌入的web server
		return WebApplicationType.SERVLET;
	}
//這裏咱們再說一下ClassUtils.isPresent();
//主要是判斷className在classLoader下是否存在,if classLoader爲null
//then classLoader=Thread.currentThread().getClassLoader()
//if classLoader仍是爲null  then classLoader=ClassUtils.class.getClassLoader()
//if classLoader仍是爲null then classLoader=ClassLoader.getSystemClassLoader()
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
        try {
            forName(className, classLoader);
            return true;
        } catch (Throwable var3) {
            return false;
        }
    }
//這裏是forName的代碼,有興趣的本身瞧瞧
public static Class forName(String name, @Nullable ClassLoader classLoader) throws ClassNotFoundException, LinkageError {
        Assert.notNull(name, "Name must not be null");
        Class clazz = resolvePrimitiveClassName(name);
        if(clazz == null) {
            clazz = (Class)commonClassCache.get(name);
        }

        if(clazz != null) {
            return clazz;
        } else {
            Class ex;
            String clToUse1;
            if(name.endsWith("[]")) {
                clToUse1 = name.substring(0, name.length() - "[]".length());
                ex = forName(clToUse1, classLoader);
                return Array.newInstance(ex, 0).getClass();
            } else if(name.startsWith("[L") && name.endsWith(";")) {
                clToUse1 = name.substring("[L".length(), name.length() - 1);
                ex = forName(clToUse1, classLoader);
                return Array.newInstance(ex, 0).getClass();
            } else if(name.startsWith("[")) {
                clToUse1 = name.substring("[".length());
                ex = forName(clToUse1, classLoader);
                return Array.newInstance(ex, 0).getClass();
            } else {
                ClassLoader clToUse = classLoader;
                if(classLoader == null) {
                    clToUse = getDefaultClassLoader();
                }

                try {
                    return clToUse != null?clToUse.loadClass(name):Class.forName(name);
                } catch (ClassNotFoundException var9) {
                    int lastDotIndex = name.lastIndexOf(46);
                    if(lastDotIndex != -1) {
                        String innerClassName = name.substring(0, lastDotIndex) + '$' + name.substring(lastDotIndex + 1);

                        try {
                            return clToUse != null?clToUse.loadClass(innerClassName):Class.forName(innerClassName);
                        } catch (ClassNotFoundException var8) {
                            ;
                        }
                    }

                    throw var9;
                }
            }
        }
    }
//2< -- 原文:Sets the {@link ApplicationContextInitializer} that will be applied to the Spring{@link ApplicationContext}
public void setInitializers(
      Collection<? extends ApplicationContextInitializer<?>> initializers) {
   this.initializers = new ArrayList<>();
   this.initializers.addAll(initializers);
}
//2<-- 3<-- 是2 也是 3
//2  type=ApplicationContextInitializer.class
//3  type=ApplicationListener.class
private  Collection getSpringFactoriesInstances(Class type) {
		return getSpringFactoriesInstances(type, new Class[] {});
	}

private  Collection getSpringFactoriesInstances(Class type,
			Class[] parameterTypes, Object... args) {
        //獲取當前上下文classLoader
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		//使用名稱並確保惟一以防止重複
		Set names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // loadName
		List instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);//createInstance
        //獲取到能夠實例化的根據OrderComparator排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
/**************************************loadName***********************************************/
//SpringFactoriesLoader.loadFactoryNames
//factoryClass=ApplicationContextInitializer.class
public static List loadFactoryNames(Class factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        //getOrDefault若是factoryClassName==null?Collections.emptyList():factoryClassName
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }
//獲取spring工廠
//private static final Map<ClassLoader,MultiValueMap<String,String>> cache = new ConcurrentReferenceHashMap();
//用這個map作緩存 以classLoader爲key,LinkedMultiValueMap爲value
private static Map> loadSpringFactories(ClassLoader classLoader) {
        MultiValueMap result = (MultiValueMap)cache.get(classLoader);
        if(result != null) {
            return result;
        } else {
            try {
                //?查找全部給定名稱的資源(spring.factories):從用來加載類的搜索路徑中查找全部具備指定名稱的資源(spring.factories)
                Enumeration ex = classLoader != null?classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result1 = new LinkedMultiValueMap();
                //spring.factories-----因爲太多 就不貼出來了,以 key=\value,\value2的方式存儲
                while(ex.hasMoreElements()) {
                    URL url = (URL)ex.nextElement();
                    UrlResource resource = new UrlResource(url);
                    //當成Properties來獲取
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry entry = (Entry)var6.next();
                        //這裏的commaDelimitedListToStringArray 是把以,分割的字符串轉成string數組
                        List factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
                        result1.addAll((String)entry.getKey(), factoryClassNames);
                    }
                }
                //放入cache中
                cache.put(classLoader, result1);
                return result1;
            } catch (IOException var9) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
            }
        }
    }

default V getOrDefault(Object key, V defaultValue) {
        V v;
        return (((v = get(key)) != null) || containsKey(key))
            ? v
            : defaultValue;
    }
/**************************************loadName***********************************************/
/***********************************createInstance*******************************************/
//獲取可以被實例化的class列表
// type=ApplicationContextInitializer.class
// parameterTypes=new Class[]{}
// classLoader=Thread.currentThread().getContextClassLoader()
//args=null
//names=獲取到的linkedHashSet()集合
private  List createSpringFactoriesInstances(Class type,
			Class[] parameterTypes, ClassLoader classLoader, Object[] args,
			Set names) {
		List instances = new ArrayList<>(names.size());
		for (String name : names) {
			try {
				Class instanceClass = ClassUtils.forName(name, classLoader);
                //斷言是不是type的subClass
				Assert.isAssignable(type, instanceClass);
				Constructor constructor = instanceClass
						.getDeclaredConstructor(parameterTypes);
                //實例化class
				T instance = (T) BeanUtils.instantiateClass(constructor, args);
                //加到list中
				instances.add(instance);
			}
			catch (Throwable ex) {
				throw new IllegalArgumentException(
						"Cannot instantiate " + type + " : " + name, ex);
			}
		}
		return instances;
	}
//4< --
private Class<?> deduceMainApplicationClass() {
   try {
      //StackTraceElement用棧的方式保存了方法的調用信息
      //獲取當前調用棧,匹配main並找到所在的類,並將clone()後的類返回
      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;
}

到這裏  ConfigurableApplicationContext就構造完成了

接下來看看它的run方法

public ConfigurableApplicationContext run(String... args) {
    //StopWatch是spring的一個util工具,主要是用來記錄程序的運行時間
   StopWatch stopWatch = new StopWatch();
    //記錄程序的開始時間
   stopWatch.start();
   ConfigurableApplicationContext context = null;
    //SpringBootExceptionReporter用來支持報告關於啓動的錯誤
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   configureHeadlessProperty();//-- >1
   SpringApplicationRunListeners listeners = getRunListeners(args);//-- >2
   //開啓監聽器 此處啓動的是spring.factories的org.springframework.boot.context.event.EventPublishingRunListener 
   //能夠經過查看源碼知道這個類都作了什麼,就是在run方法的不一樣階段發送不一樣的事件
   //這裏用到一個觀察者模式
   listeners.starting();
   try {
    //應用參數:這裏個人args是null
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);//-- >3
      configureIgnoreBeanInfo(environment);//-- >4
       //打印Banner 這裏分OFF LOG CONSOLE 分別是不打印、Log、System,out
      Banner printedBanner = printBanner(environment);
      //建立ApplicationContext、
      context = createApplicationContext();// -- > 5
      //實例化SpringBootExceptionReporter.class 並指定ConfigurableApplicationContext參數的構造器
      exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);// -- > 6
      refreshContext(context);
      afterRefresh(context, applicationArguments);
    //中止監聽
      listeners.finished(context, null);
    //中止並記錄時間
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
      return context;
   }
   catch (Throwable ex) {
      handleRunFailure(context, listeners, exceptionReporters, ex);
      throw new IllegalStateException(ex);
   }
}
//1 < --
private void configureHeadlessProperty() {
    //設置指定鍵值對的系統屬性
    //SYSTEM_PROPERTY_JAVA_AWT_HEADLESS="java_awt_headless",默認值爲"true"
   System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
         SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
//2 < --
private SpringApplicationRunListeners getRunListeners(String[] args) {
   Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    //實例化可用的SpringApplicationRunListener(指定了types的構造器)
   return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
         SpringApplicationRunListener.class, types, this, args));
}
//3 < --
private ConfigurableEnvironment prepareEnvironment(
      SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
   // 建立並配置環境
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   listeners.environmentPrepared(environment);
   bindToSpringApplication(environment);
   if (isWebEnvironment(environment)
         && this.webApplicationType == WebApplicationType.NONE) {
      environment = convertToStandardEnvironment(environment);
   }
   ConfigurationPropertySources.attach(environment);
   return environment;
}
//返回對應的環境     剛纔已經初始化過webApplicationType 
private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		if (this.webApplicationType == WebApplicationType.SERVLET) {
			return new StandardServletEnvironment();
		}
		return new StandardEnvironment();
	}
//4 < --
//從這裏咱們能夠看出spring boot的環境主要是property與profiles
protected void configureEnvironment(ConfigurableEnvironment environment,
      String[] args) {
   configurePropertySources(environment, args);
   configureProfiles(environment, args);
}
protected void configurePropertySources(ConfigurableEnvironment environment,
			String[] args) {
		MutablePropertySources sources = environment.getPropertySources();
        //判斷defaultProperties(map)是否爲空
		if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
            //MutablePropertySources內部維護一個list<PropertySource<?>>集合,先removeIfPresent()而後把defaultProperties加到最後
			sources.addLast(
					new MapPropertySource("defaultProperties", this.defaultProperties));
		}
        //判斷addCommandLineProperties ==true 和 args的長度  此處咱們的args=0
		if (this.addCommandLineProperties && args.length > 0) {
            //name=commandLineArgs
			String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
			if (sources.contains(name)) {//若是改列表中包括了
				PropertySource source = sources.get(name);//獲取到這個PropertySource資源
				CompositePropertySource composite = new CompositePropertySource(name);
				composite.addPropertySource(new SimpleCommandLinePropertySource(
						name + "-" + args.hashCode(), args));
				composite.addPropertySource(source);
				sources.replace(name, composite);
			}
			else {
                //若是不存在就加到最前面,這樣子能夠起到一個做用是
                //若是經過main()作的配置 會最優執行
				sources.addFirst(new SimpleCommandLinePropertySource(args));
			}
		}
	}
	protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
        //spring.active.profile的值
		environment.getActiveProfiles(); // ensure they are initialized
		// But these ones should go first (last wins in a property key clash)
		Set profiles = new LinkedHashSet<>(this.additionalProfiles);
		profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
        //將additionalProfiles和getActiveProfiles的值加到一塊兒
        //設置到環境中
		environment.setActiveProfiles(profiles.toArray(new String[profiles.size()]));

	}
//5 < -- 建立ApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
   Class<?> contextClass = this.applicationContextClass;
   if (contextClass == null) {
      try {
         switch (this.webApplicationType) {
         case SERVLET:
//DEFAULT_WEB_CONTEXT_CLASS="org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
            contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
            break;
//DEFAULT_REACTIVE_WEB_CONTEXT_CLASS="org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext"
         case REACTIVE:
            contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
            break;
//DEFAULT_CONTEXT_CLASS=org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext"
         default:
            contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
         }
      }
      catch (ClassNotFoundException ex) {
         throw new IllegalStateException(
               "Unable create a default ApplicationContext, "
                     + "please specify an ApplicationContextClass",
               ex);
      }
   }
   return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
//6 < --
private void prepareContext(ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
   context.setEnvironment(environment);
   postProcessApplicationContext(context);
   applyInitializers(context);
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }

   // Add boot specific singleton beans
   context.getBeanFactory().registerSingleton("springApplicationArguments",
         applicationArguments);
   if (printedBanner != null) {
      context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
   }

   // Load the sources
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   load(context, sources.toArray(new Object[sources.size()]));
   listeners.contextLoaded(context);
}

至此:run方法結束app

相關文章
相關標籤/搜索