SpringBoot源码深度解析(一)深入理解SpringApplication

对于每一个使用过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

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值