spring boot2.x启动源码浅析

spring boot程序启动入口是:

@SpringBootApplication
public class ApplicationServer {

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

}

看下SpringApplication这个类的说明:

Class that can be used to bootstrap and launch a Spring application from a Java main
method.

该类用于启动运行spring应用程序。

通常情况下运行spring应用程序可以分为以下步骤:

  • 创建合适的ApplicationContext实例
  • 注册一个CommandLinePropertySource,暴露命令行参数作为spring属性参数
  • 刷新application context,加载所有的单例bean
  • 触发CommandLineRunner bean

这种方式运行spring应用比较少,大多数情况会在main方法里直接调用以启动应用程序:

 * @Configuration
 * @EnableAutoConfiguration
 * public class MyApplication  {
 *
 *   public static void main(String[] args) {
 *     SpringApplication.run(MyApplication.class, args);
 *   }
 * }

有时候启动的时候希望进行一些高级配置,可以这么做:

SpringApplication application = new SpringApplication(MyApplication.class);
//... config your settings here
application.run(args)

可以看下可以进行哪些设置:

比如进行spring boot启动banner设置,webApplicationType设置等等。

SpringApplication可以从不同的数据源读取bean,如:

  • 由XmlBeanDefinitionReader加载,bean配置在XML文件中。
  • 由GroovyBeanDefinitionReader加载,bean配置在script脚本中。
  • 由ClassPathBeanDefinitionScanner加载,扫描包的方式。
  • 根据类全限定名由AnnotatedBeanDefinitionReader加载。

大致分析完SpringApplication这个类的功能,继续往下走:

	public static ConfigurableApplicationContext run(Class<?> primarySource,
			String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}

顺着调用栈,再往后:

	public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

 先看一下这个构造方法new SpringApplication(primarySources)做了哪些事情:

	public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}

创建SpringApplication实例,接着调用的是另外一个构造方法(我们可以看到spring很多地方用了可变参数...) :

	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 = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

这里的primarySources就是主类,webApplicationType.deuceFromClasspath()是推测web应用的类型,点进去我们可以看到

WebApplicationType是枚举类,列举了web应用类型分类:

	/**
	 * The application should not run as a web application and should not start an
	 * embedded web server.
	 */
	NONE,

	/**
	 * The application should run as a servlet-based web application and should start an
	 * embedded servlet web server.
	 */
	SERVLET,

	/**
	 * The application should run as a reactive web application and should start an
	 * embedded reactive web server.
	 */
	REACTIVE;

SERVLET是传统的web服务,如果是这种类型则会启动一个内嵌的servlet web服务容器,默认是Tomcat,可以修改。

REACTIVE是一种异步非阻塞web框架,拥有高并发、高性能的特点,是构建中大型应用的首选。

NONE就不用介绍了,非web类型服务。

接着看方法deduceFromClasspath方法:

	static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

 ClassUtils.isPresent方法是判断类存在且可被加载,如果类或者其依赖不存在或者不能加载则返回false;

	public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
		try {
			forName(className, classLoader);
			return true;
		}
		catch (IllegalAccessError err) {
			throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
					className + "]: " + err.getMessage(), err);
		}
		catch (Throwable ex) {
			// Typically ClassNotFoundException or NoClassDefFoundError...
			return false;
		}
	}

forName方法是加载某个类,但是使用的不是Class.forName,对Class.forName进行了扩展或者说增强,我们知道Class.forName加载的类名为string类型,而此处的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 = 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 [L一维数组
		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 Class.forName(name, false, clToUse);// 调用java.lang包
		}
		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 Class.forName(innerClassName, false, clToUse);
				}
				catch (ClassNotFoundException ex2) {
					// Swallow - let original exception get through
				}
			}
			throw ex;
		}
	}

其中调用resolvePrimitiveClassName方法:

	public static Class<?> resolvePrimitiveClassName(@Nullable String name) {
		Class<?> result = null;
		// Most class names will be quite long, considering that they
		// SHOULD sit in a package, so a length check is worthwhile.
		if (name != null && name.length() <= 8) {
			// Could be a primitive - likely.
			result = primitiveTypeNameMap.get(name);
		}
		return result;
	}

 primitiveTypeNameMap在静态代码块中被初始化。

		for (Class<?> primitiveType : primitiveTypes) {
			primitiveTypeNameMap.put(primitiveType.getName(), primitiveType);
		}

 存储的是基础数据类型的类名和对应类类型。详细可查看static代码块。

实际上对于我们给定主类的程序,Class<?> clazz = resolvePrimitiveClassName(name)得到的clazz肯定是null,clazz = commonClassCache.get(name)返回的也是null,commonClassCache是在静态代码块里调用registerCommonClasses方法里赋值的。最终会调用Class.forName(name, false, clToUse)加载类。

回到deduceFromClasspath方法,在判断完相应的类是否存在后,返回具体的web应用类型。

回到SpringApplication构造方法,接下来的两步是设置spring上下文初始化器和监听器。

		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

这两步获取工厂实例的方法是一样的,通过读取spirng core包下的META/spring.factories文件来实例化不同工厂的实例。

		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);

从上述源码可以学习到:

  • 如何加载工厂实例
  • spring自定义了MultiValueMap数据结构存储多value值以满足自身功能需要

往后是deduceMainApplicationClass()方法推测main方法所在主类,主要是借助StackTraceElement这个类来测。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值