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