SpringBoot-外置Servlet容器启动原理

Servlet3.0规则
​	1、服务器启动(web应用启动)会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例:

​	2、ServletContainerInitializer的实例放在jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类的全类名

文件内容如下:org.springframework.web.SpringServletContainerInitializer
也就是说在启动服务器时会加载这个类

​	3、还可以使用@HandlesTypes,在应用启动的时候加载我们感兴趣的类;

启动步骤

1、启动Tomcat

2、启动时根据Servlet3.0的规则自动检查文件目录后加载SpringServletContainerInitializer类,注意onStartup方法中的参数Set<Class<?>> webAppInitializerClasses

3、将 Set<Class<?>> webAppInitializerClasses 集合中如果不是接口和抽象类并且是WebApplicationInitializer的子类的话,则创建对象

(@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型的类都将传入到这个集合中,自己写的类因为继承了SpringBootServletInitializer,SpringBootServletInitializer又继承了WebApplicationInitializer,所以自己写的类也将会被传入这个集合中创建实例对象)

4、调用每一个容器对象的onStartup方法

	// SpringServletContainerInitializer.onStartup方法()

	@Override
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

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

		if (webAppInitializerClasses != null) {
			for (Class<?> waiClass : webAppInitializerClasses) {
				// 遍历传入的webAppInitializerClasses集合,如果不是接口和抽象类,并且是WebApplicationInitializer的子类的话,则创建对象
				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);
    // 遍历集合,调用每一个容器中的onStartup方法
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}
	}

5、相当于自己写的ServletInitializer会被创建对象,并且调用onStartup方法。去父类 SpringBootServletInitializer 中可以找到对应方法。

6、在onStartup方法中创建了根容器,通过config方法获取SpringBoot的主程序后去创建应用并运行。Spring的应用启动并创建IOC容器

	// SpringBootServletInitializer类部分代码
	
	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		this.logger = LogFactory.getLog(getClass());
    // 这里会创建根容器
		WebApplicationContext rootAppContext = createRootApplicationContext(servletContext);
		if (rootAppContext != null) {
			servletContext.addListener(new ContextLoaderListener(rootAppContext) {
				@Override
				public void contextInitialized(ServletContextEvent event) {
					// no-op because the application context is already initialized
				}
			});
		}
		else {
			this.logger.debug("No ContextLoaderListener registered, as " + "createRootApplicationContext() did not "
					+ "return an application context");
		}
	}

	protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
    // 创建构建器SpringApplicationBuilder
		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);
    // 调用configure方法,自己写的类重写了这个方法,将SpringBoot的主程序类传入了进来
		builder = configure(builder);
		builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
   	// 使用builder创建一个Spring应用
		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));
		}
    // 启动Spring应用
		return run(application);
	}

8、总结:先启动Servlet容器,再启动SpringBoot应用。启动容器时根据Servlet3.0之后的标准去扫描每个包下的对应实现类并创建容器实例,并且调用每个容器的onStartup方法。调用时会创建根容器,根容器创建时又通过config方法来获取SpringBoot主程序的位置。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值