@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
这段代码就是Spring boot 应用的启动入口,通过静态main启动应用程序。短短几行代码却已经是一个完成的web应用程序了!
通过调用SpringApplication的静态run方法加载程序内容
public static ConfigurableApplicationContext run(Object source, String... args) { return run(new Object[] { source }, args); } public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
// new 对象 return new SpringApplication(sources).run(args); }
run 方法是多态方法,通过调用自身的run方法 new SpringApplication 对象并执行该对象的run方法。
public SpringApplication(Object... sources) { initialize(sources); } public SpringApplication(ResourceLoader resourceLoader, Object... sources) { this.resourceLoader = resourceLoader; initialize(sources); }
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(); }
SpringApplication 构造函数调用initialize 方法初始化sources、webEnvironment、initializers、listeners、mainApplicationClass;
sources 很简单, 就是main参数中的args参数。initialize 方法通过集合转换讲args 存放到set 集合sources 中。
webEnvironment :webEnvironment 是一个Boolean 变量,代表当前程序是否是web 程序。赋值过程:
private boolean deduceWebEnvironment() { for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return false; } } return true; }
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" };
WEB_ENVIRONMENT_CLASSES是一个数组、存放着两个类"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"的名称.
decuceWebEnvironment 方法通过遍历WEB_ENVIRONMENT_CLASSES 数组,判定当前classpath下是否存在包含的类,存在返回true值,代表web应用程序。isPresent()方法通过
forName(className,classLoader)进行类加载,加载成功返回一个true值,反之,返回false。所以webEnvironment的值为true;
setinitializers:初始化initializers变量,该变量是一个ApplicationContentInitializer类型的集合。
private List<ApplicationContextInitializer<?>> initializers;
// 设置
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
通过getSpringFactoriesInstances(ApplicationContextInitializer.class)) 方法得到一个ApplicationContextInializer 类型集合,然后添加到initializers集合中去。getSpringFactoriesInstances实现:
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();
// 获取到初始化Spring Factories的名字 Set<String> names = new LinkedHashSet<String>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//根据名字创建对应对象 List<T> instances = createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);
//排序 AnnotationAwareOrderComparator.sort(instances);
return instances; }
首选获取当前线程,根据传入的class type和类加载器,调用SpringFactoriesLoader.loadFactoryNames方法,获取到配置文件中的定义的Application Context Initializers类名称
loadFactoryNames方法,通过classLoader.getResources或getSystemResources方法,从资源文件spring.factories获取到一个一个enumeration 类型的URL。通过遍历URL,获取到property资源,然后获取定义的Intializers名称,并经过转换加入了集合中返回
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
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)); 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); 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 文件中定义的4个Initializer、分别是:
# Application Context Initializers
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
ConfigurationWarningsApplicationContextInitializer的作用是用来报告Spring容器的一些常见的错误配置
ContextIdApplicationContextInitializer的作用是设置Spring应用上下文的ID,会参照环境属性。
DelegatingApplicationContextInitializer的作用是使用环境属性context.initializer.classes指定的初始化器(initializers)进行初始化工作,如果没有指定则什么都不做。
ServerPortInfoApplicationContextInitializer的作用是将内置servlet容器实际使用的监听端口写入到Environment环境属性中。这样属性local.server.port就可以直接通过@Value注入到测试中,或者通过环境属性Environment获取。
获取到names名字后,调用createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names)方法,根据名字,将4个Initalizer分别创建并加入到集合中返回;创建方法:
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、通过ClassUtils.forName方法获取到类对象、然后通过instantiateClass()方法创建实例,然后加入到集合中返回!然后返回到setInitializers、将创建好的ApplicationContextInitializer加入到SpringApplication定义好的initializers中了
public void setInitializers( Collection<? extends ApplicationContextInitializer<?>> initializers) { this.initializers = new ArrayList<ApplicationContextInitializer<?>>(); this.initializers.addAll(initializers);
}
设置listeners
private List<ApplicationListener<?>> listeners; //springApplication 定义的监听list变量
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //设置监听
//实现方法:通过getSpringFactoriesInstrances(ApplicationListener.class)获取到listeners,并加入到集合中listeners
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<ApplicationListener<?>>(); this.listeners.addAll(listeners); }
getSpringFactoriesInstrances方法跟前面的设置setInitializers所使用的方法是一样的,只不过传入的参数不一样,传入监听器AplicationListener.class。所以创建的结果是一个存放listener的集合。spring.factorries中定义的Listener如下:
每个Listener分别处理不同的功能,暂时不说明具体处理内容。
# 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.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener
mainApplicationClass设置
private void initialize(Object[] sources) { ... // 为成员变量mainApplicationClass赋值 this.mainApplicationClass = deduceMainApplicationClass(); } 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; }
mainApplicationClass 通过获取运行当前调用栈,然后找到main方法,然后通过forName获取类对象,最后复制给mainApplicationClass。到这里,SpringApplication 对象初始化过程完成了。获取到了SpringApplication 对象,接着调用run方法。run方法的内容更多,接着下一篇再探索吧。。。。
=================================================================================================================================
原文:https://www.cnblogs.com/xiaotongye/p/12743015.html