springboot 启动流程(一) 借助外部容器启动

对springboot项目,可以通过两种方式启动服务,一是利用内嵌的Tomcat作为web容器启动,两一种方式是借助外部容器启动。本章先介绍通过外部容器启动的源码流程。

一、springboot 借助外部容器

@EnableSwagger2
@SpringBootApplication
public class Application extends SpringBootServletInitializer{
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(Application.class);
    }
}

二、流程启动原理

1、先看servlet3.0规范

  • javax.servlet.ServletContainerInitializer是一个接口,web应用程序启动的时候,会创建一个ServletContainerInitializer实例
  • 提供ServletContainerInitializer的框架必须名为javax.servlet的文件捆绑到jar文件的META-INF/services目录中
  • ServletContainerInitializer 实现上的handlesTypes注解,用于寻找感兴趣的类–要么是@HandlesTypes注解指定的类,要么是其子类。
  • ServletContainerInitializer实例的onStartup 方法将在应用程序启动时且任何servlet侦听器事件被激发之前被调用。
  • ServletContainerInitializer 的onStartup 方法调用对象是(Set<Class<?>> webAppInitializerClasses),这些类要么是initializer的扩展类,要么是添加了@HandlesTypes注解的类,将会依次调用webAppInitializerClasses实例的onStartup方法。

对于spring框架,ServletContainerInitializer的绑定在spring-web jar包中,并给出了一个实现类org.springframework.web.SpringServletContainerInitializer

 2、Tomcat容器启动

在tomcat-core中,启动的时候,会主动调用ServletContainerInitializer的onstartup方法:

public class StandardContext extends ContainerBase implements Context, NotificationEmitter{

     private Map<ServletContainerInitializer,Set<Class<?>>> initializers =
        new LinkedHashMap<>();

    ...
    // 启动
    @Override
    protected synchronized void startInternal() throws LifecycleException{
        ...
        // Call ServletContainerInitializers 这里会调用onstart方法
        for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry : initializers.entrySet()) {
            try {
                entry.getKey().onStartup(entry.getValue(),getServletContext());
            } catch (ServletException e) {
                log.error(sm.getString("standardContext.sciFail"), e);
                ok = false;
                break;
           }
        }
    }
    ...
}

3、查看SpringServletContainerInitializer方法的具体实现

// 感兴趣的类型是WebApplicationInitializer
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
	@Override
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = new LinkedList<>();

		if (webAppInitializerClasses != null) {
			for (Class<?> waiClass : webAppInitializerClasses) {
				// 非接口和虚类
				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
					try {
						initializers.add((WebApplicationInitializer)
								ReflectionUtils.accessibleConstructor(waiClass).newInstance());
					}
					catch (Throwable ex) {
						throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
					}
				}
			}
		}

		if (initializers.isEmpty()) {
			servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
			return;
		}

		servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
		AnnotationAwareOrderComparator.sort(initializers);
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}
	}

}

4、回到开始,在springboot中SpringBootServletInitializer的实现

public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
    protected Log logger;
    private boolean registerErrorPageFilter = true;

    public SpringBootServletInitializer() {
    }

    protected final void setRegisterErrorPageFilter(boolean registerErrorPageFilter) {
        this.registerErrorPageFilter = registerErrorPageFilter;
    }
    // 此方法会被调用,并在所有的操作之前
    public void onStartup(ServletContext servletContext) throws ServletException {
        this.logger = LogFactory.getLog(this.getClass());
        WebApplicationContext rootAppContext = this.createRootApplicationContext(servletContext);
        if (rootAppContext != null) {
            servletContext.addListener(new ContextLoaderListener(rootAppContext) {
                public void contextInitialized(ServletContextEvent event) {
                }
            });
        } else {
            this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
        }
    }

    // 查看创建跟容器
    protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
		// 创建builder
		SpringApplicationBuilder builder = createSpringApplicationBuilder();
		builder.main(getClass());
		// 查询是否存在根容器
		ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
		if (parent != null) {
			this.logger.info("Root context already created (using as parent).");
			servletContext.setAttribute(
					WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
			builder.initializers(new ParentContextApplicationContextInitializer(parent));
		}
		// 创建初始化事件
		builder.initializers(new ServletContextApplicationContextInitializer(servletContext));
		builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
        // 此方法被外部覆写,所以会主动调用sources方法
		builder = configure(builder);
		builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
		SpringApplication application = builder.build();
		if (application.getAllSources().isEmpty() && AnnotationUtils
				.findAnnotation(getClass(), Configuration.class) != null) {
			application.addPrimarySources(Collections.singleton(getClass()));
		}
		Assert.state(!application.getAllSources().isEmpty(),
				"No SpringApplication sources have been defined. Either override the "
						+ "configure method or add an @Configuration annotation");
		// Ensure error pages are registered
		if (this.registerErrorPageFilter) {
			application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
		}
		// 这里和直接用main函数没有区别,后面介绍Main函数启动方式
		return run(application);
	}
}

接着看是如何创建SpringApplication的,

public class SpringApplicationBuilder {
	private final Set<Class<?>> sources = new LinkedHashSet<>();
	
	// 这里传入了资源类App的类,带着springboot的注解
	public SpringApplicationBuilder sources(Class<?>... sources) {
		this.sources.addAll(new LinkedHashSet<>(Arrays.asList(sources)));
		return this;
	}
	// application add primarySources 
	public SpringApplication build(String... args) {
		configureAsChildIfNecessary(args);
		this.application.addPrimarySources(this.sources);
		return this.application;
	}
}

接着看SpringApplication.run()

// 这里会创建一个容器并刷新
public ConfigurableApplicationContext run(String... args) {
	// 简单计时
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	configureHeadlessProperty();
	// 1. 获取并启动个监听器,每一次listeners.XXX()方法调用,都将会广播对应事件给applicationListeners监听器处理
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		// 构造容器环境
		ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
		// 设置需要忽略的bean
		configureIgnoreBeanInfo(environment);
		Banner printedBanner = printBanner(environment);
		// 创建根容器
		context = createApplicationContext();
		// 启动错误报告实例,用来报告错误
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
		// 准备容器
		prepareContext(context, environment, listeners, applicationArguments,printedBanner);
		// 刷新容器
		refreshContext(context);
		// 刷新容器扩展接口
		afterRefresh(context, applicationArguments);
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass)
				.logStarted(getApplicationLog(), stopWatch);
		}
		// 这里是对容器后加入的一些listener启动
		listeners.started(context);
		//调用CommandLineRunner和ApplicationRunner的run方法
		callRunners(context, applicationArguments);
	}catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}

	try {
		listeners.running(context);
	}catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	return context;
}

创建listener的实现

// args可为空
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 = getClassLoader();
	// 加载SpringApplicationRunListener的所有Listener,这个是加载classPath下的listener
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}
// 创建Listener的实例
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 {
			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;
}

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值