对于每一个使用过SpringBoot/SpringCloud的程序员而言,下面这段代码可能大家都不陌生:
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
SpringApplication是springboot驱动spring应用上下文的引导类,用于启动和引导springBoot应用程序。
从SpringApplication.run(TestApplication.class, args)开始追踪,第一步我们会来到这个地方:
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified source using default settings.
* @param primarySource the primary source to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args);
}
然后下一步跳转到这个地方:
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified sources using default settings and user supplied arguments.
* @param primarySources the primary sources to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
按照注释来说,这个方法接收的两个参数分别是:
第一个参数 primarySource
:加载的主要资源类
第二个参数 args
:传递给应用的应用参数
继续向下,我们可以看到,这一步返回了一个 new SpringApplication(primarySources).run(args)
所以说,我们先来看下SpringApplication实例化的过程:
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #SpringApplication(ResourceLoader, Class...)
* @see #setSources(Set)
*/
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
这一步需要注意的是,这个地方传递了一个 null
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = deduceWebApplicationType();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
然后便到了这个构造器。可以看出,这个构造器大概分为了七步完成初始化:
//第一步,初始化资源加载器为 null
this.resourceLoader = resourceLoader;
//第二步,使用断言,确保资源类不能为null
Assert.notNull(primarySources, "PrimarySources must not be null");
//第三步,构建一个主要资源类的LinkedHashSet
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//第四步,判断当前web应用的基本类型,判断具体环境
this.webApplicationType = deduceWebApplicationType();
我们来详细看下deduceWebApplicationType()这个方法:
private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private WebApplicationType deduceWebApplicationType() {
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
这段代码,就是推断应用的类型 ,创建的是一个 SERVLET 应用还是 REACTIVE应用或者是 NONE。
这里简单看一下ClassUtils.isPresent这个方法
/**
* Determine whether the {@link Class} identified by the supplied name is present
* and can be loaded. Will return {@code false} if either the class or
* one of its dependencies is not present or cannot be loaded.
* @param className the name of the class to check
* @param classLoader the class loader to use
* (may be {@code null} which indicates the default class loader)
* @return whether the specified class is present
*/
public static boolean isPresent(String className, @Nullable ClassLoader classLoader){
try {
forName(className, classLoader);
return true;
}
catch (Throwable ex) {
// Class or one of its dependencies is not present...
return false;
}
}
/**
* Replacement for {@code Class.forName()} that also returns Class instances
* for primitives (e.g. "int") and array class names (e.g. "String[]").
* Furthermore, it is also capable of resolving inner class names in Java source
* style (e.g. "java.lang.Thread.State" instead of "java.lang.Thread$State").
* @param name the name of the Class
* @param classLoader the class loader to use
* (may be {@code null}, which indicates the default class loader)
* @return a class instance for the supplied name
* @throws ClassNotFoundException if the class was not found
* @throws LinkageError if the class file could not be loaded
* @see Class#forName(String, boolean, ClassLoader)
*/
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 = commonClassCache.get(name);
}
if (clazz != null) {
return clazz;
}
// "java.lang.String[]" style arrays
if (name.endsWith(ARRAY_SUFFIX)) {
String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
Class<?> elementClass = forName(elementClassName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}
// "[Ljava.lang.String;" style arrays
if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
Class<?> elementClass = forName(elementName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}
// "[[I" or "[[Ljava.lang.String;" style arrays
if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
Class<?> elementClass = forName(elementName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}
ClassLoader clToUse = classLoader;
if (clToUse == null) {
clToUse = getDefaultClassLoader();
}
try {
return (clToUse != null ? clToUse.loadClass(name) : Class.forName(name));
}
catch (ClassNotFoundException ex) {
int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
if (lastDotIndex != -1) {
String innerClassName =
name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
try {
return (clToUse != null ? clToUse.loadClass(innerClassName) : Class.forName(innerClassName));
}
catch (ClassNotFoundException ex2) {
// Swallow - let original exception get through
}
}
throw ex;
}
}
这边简单谈一下java的类加载机制:
java类加载过程可以分为三个步骤:
- 加载
- 链接
- 初始化
类加载器就是来实现类加载的功能的。java为我们提供了三种不同类型的类加载器,BootstrapClassLoader、ExtensionClassLoader和AppClassLoader,三种类加载器分别针对核心类、扩展类和classPath下以及我们自定义的jar进行加载。类加载器根据双亲委派模型进行工作,即当一个类被加载时,先交由父加载器进行,如果父加载器无法找到这个类,再交给子加载器进行加载。当然,我们可以自定义子加载器进行个性化加载。
————https://blog.csdn.net/qq_21399231/article/details/80751489
这边再简单说个概念:
对于类中静态代码块的初始化工作,是在链接阶段中的第二个小阶段准备阶段执行的。
假设你定义了一个静态变量a:
private static int a = 10;
在链接阶段,JVM在准备阶段便将a设置为他的初始值,也就是 0;
然后,在链接阶段执行完成之后,在初始化阶段,a才被设置为 10;
关于具体的类加载机制,可以参见博客内其他文章。
上文 forName 方法中,我们可以看到这么一块:
ClassLoader clToUse = classLoader;
if (clToUse == null) {
clToUse = getDefaultClassLoader();
}
/**
* Return the default ClassLoader to use: typically the thread context
* ClassLoader, if available; the ClassLoader that loaded the ClassUtils
* class will be used as fallback.
* <p>Call this method if you intend to use the thread context ClassLoader
* in a scenario where you clearly prefer a non-null ClassLoader reference:
* for example, for class path resource loading (but not necessarily for
* {@code Class.forName}, which accepts a {@code null} ClassLoader
* reference as well).
* @return the default ClassLoader (only {@code null} if even the system
* ClassLoader isn't accessible)
* @see Thread#getContextClassLoader()
* @see ClassLoader#getSystemClassLoader()
*/
@Nullable
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
}
catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back...
}
if (cl == null) {
// No thread context class loader -> use class loader of this class.
cl = ClassUtils.class.getClassLoader();
if (cl == null) {
// getClassLoader() returning null indicates the bootstrap ClassLoader
try {
cl = ClassLoader.getSystemClassLoader();
}
catch (Throwable ex) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
return cl;
}
这就很明了了,如果输入的类加载器为空,那么系统便从当前线程上下文中获取默认的类加载器,如果不进行设置,那么返回的便是默认的类加载器app ClassLoader()。
//第五步,初始化应用上下文
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
先来看下这个:
private List<ApplicationContextInitializer<?>> initializers;
/**
* Sets the {@link ApplicationContextInitializer} that will be applied to the Spring
* {@link ApplicationContext}.
* @param initializers the initializers to set
*/
public void setInitializers(
Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>();
this.initializers.addAll(initializers);
}
这里面实例化了一个数组,用于存放 initializers
这边便涉及到了一个 ApplicationContextInitializer
ApplicationContextInitializer 是一个接口,用于ConfigurableApplicationContext通过调用refresh函数来初始化Spring容器之前的回调函数,通常在web应用中,设计在初始化Spring容器之前调用。例如依赖于容器ConfigurableApplicationContext中的Enviroment来记录一些配置信息或者使一些配置文件生效;
这里边涉及到了两个东西:Spring Factories 和 SPI 原则 。
抛开这两个东西先不管,我们继续看 getSpringFactoriesInstances(ApplicationContextInitializer.class) 这一块代码。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,Class<?>[],parameterTypes, Object... args) {
//调用当前线程默认的classloader
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//使用当前classloader 从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer集合
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//创建相关实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
@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 {
//使用ClassUtils.forName 指定classLoader获取对应class对象
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;
}
SpringFactoriesLoader工厂加载机制是Spring内部提供的一个约定俗成的加载方式,与java spi类似,只需要在模块的META-INF/spring.factories文件,这个Properties格式的文件中的key是接口、注解、或抽象类的全名,value是以逗号 “ , “ 分隔的实现类,使用SpringFactoriesLoader来实现相应的实现类注入Spirng容器中。
这里简单说下Class.forName(className)和 ClassLoader.loadClass(className) 的区别:
Class.forName(className)方法,内部实际调用的方法是 Class.forName(className,true,classloader);
第2个boolean参数表示类是否需要初始化, Class.forName(className)默认是需要初始化。
一旦初始化,就会触发目标对象的 static块代码执行,static参数也也会被再次初始化。
ClassLoader.loadClass(className)方法,内部实际调用的方法是 ClassLoader.loadClass(className,false);
第2个 boolean参数,表示目标对象是否进行链接,false表示不进行链接,由上面介绍可以,
不进行链接意味着不进行包括初始化等一些列步骤,那么静态块和静态对象就不会得到执行
//第六步,设置监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
先看下这一段
private List<ApplicationListener<?>> listeners;
/**
* Sets the {@link ApplicationListener}s that will be applied to the SpringApplication
* and registered with the {@link ApplicationContext}.
* @param listeners the listeners to set
*/
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<>();
this.listeners.addAll(listeners);
}
这一段代码的意思就是构建一个 List<ApplicationListener<?>> listeners,这么个东西。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
/**
* Class to be extended by all application events. Abstract as it
* doesn't make sense for generic events to be published directly.
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
public abstract class ApplicationEvent extends EventObject {
/** use serialVersionUID from Spring 1.2 for interoperability */
private static final long serialVersionUID = 7099057708183571937L;
/** System time when the event happened */
private final long timestamp;
/**
* Create a new ApplicationEvent.
* @param source the object on which the event initially occurred (never {@code null})
*/
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
/**
* Return the system time in milliseconds when the event happened.
*/
public final long getTimestamp() {
return this.timestamp;
}
}
ApplicationContext事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。
参见:https://www.jianshu.com/p/0269a8390d5f
//第七步,推断主入口应用类
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;
}
在完成SpringApplication对象的构建和初始化之后,就开始执行run方法的逻辑了。具体源代码如下文所示:
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
//统计应用启动时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//初始化spring跟容器
ConfigurableApplicationContext context = null;
//自定义SpringApplication启动错误的回调接口
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//设置系统属性
configureHeadlessProperty();
//创建所有 Spring 运行监听器并发布应用启动事件
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//装配参数和环境
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//发布ApplicationEnvironmentPreparedEvent
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
//创建ApplicationContext,并装配
context = this.createApplicationContext();
this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
//发布ApplicationPreparedEvent
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
//发布ApplicationStartedEvent
listeners.started(context);
//执行Spring中@Bean下的一些操作,如静态方法等
this.callRunners(context, applicationArguments);
} catch (Throwable var9) {
this.handleRunFailure(context, listeners, exceptionReporters, var9);
throw new IllegalStateException(var9);
}
//发布ApplicationReadyEvent
listeners.running(context);
return context;
}
参考文章:https://blog.csdn.net/hzhahsz/article/details/83960139
先看下configureHeadlessProperty:
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
private boolean headless = true;
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
这个方法的作用是配置了一个名为java.awt.headless的系统属性,具体功能就是让应用程序没有显示器也能启动。
SpringApplicationRunListeners listeners = getRunListeners(args);
这段代码的作用是获取监听器,具体源代码如下:
private static final Log logger = LogFactory.getLog(SpringApplication.class);
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
private <T> Collection<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<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
可以看到,加载监听器的基本步骤和上文获取ApplicationListener的方法基本一样,都是通过springFactoryies从META-INF/spring.factories 中获取。
接下来便是listeners.starting了
private final List<SpringApplicationRunListener> listeners;
listeners.starting();
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
private final SpringApplication application;
private final String[] args;
@Override
public void starting() {
this.initialMulticaster.multicastEvent(
new ApplicationStartingEvent(this.application, this.args));
}
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableTypeeventType) {
ResolvableType type = (eventType != null ? eventType :resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
详情参见:http://www.mamicode.com/info-detail-2644218.html