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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章