/** * Interface which allows a library/runtime to be notified of a web * application's startup phase and perform any required programmatic * registration of servlets, filters, and listeners in response to it. * covered must follow the * application's classloading delegation model. * * @see javax.servlet.annotation.HandlesTypes * * @since Servlet 3.0 */ publicinterfaceServletContainerInitializer{
我们就分析分析这个org.springframework.web.SpringServletContainerInitializer这个类。这个类是SpringBoot启动关键,通过这个启动可以启动和创建SpringBoot的webApplicationContext对象。这样才能让整个基于SpringBoot的启动成为可能。下面No BB Show Code
/** * Start this component and implement the requirements * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. * * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */ @Override protectedsynchronizedvoidstartInternal()throws LifecycleException { .......... //省略好多代码 // Call ServletContainerInitializers 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); l ok = false; break; } } //省略好多代码 ........... } //从这段代码能看出来ServletContainerInitializer是放到一个Map里然后循环去启动的。启动的时候还塞了HandlesTypes注解要处理的使用类,关于这个Map怎么产生的和处理的我就没仔细研究了。
/** * Configure the given {@link ServletContext} with any servlets, filters, listeners * context-params and attributes necessary for initializing this web application. See * examples {@linkplain WebApplicationInitializer above}. * @param servletContext the {@code ServletContext} to initialize * @throws ServletException if any call against the given {@code ServletContext} * throws a {@code ServletException} */ voidonStartup(ServletContext servletContext)throws ServletException;
/** * Set if the {@link ErrorPageFilter} should be registered. Set to {@code false} if * error page mappings should be handled via the server and not Spring Boot. * @param registerErrorPageFilter if the {@link ErrorPageFilter} should be registered. */ //注册错误页 protectedfinalvoidsetRegisterErrorPageFilter(boolean registerErrorPageFilter){ this.registerErrorPageFilter = registerErrorPageFilter; } //这个地方在SpringServletContainerInitializer时候被调用 @Override publicvoidonStartup(ServletContext servletContext)throws ServletException { // Logger initialization is deferred in case an ordered // LogServletContextInitializer is being used this.logger = LogFactory.getLog(getClass()); //创建web应用上下文 WebApplicationContext rootAppContext = createRootApplicationContext( servletContext); if (rootAppContext != null) { servletContext.addListener(new ContextLoaderListener(rootAppContext) { @Override publicvoidcontextInitialized(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"); } } //创建web应用上下文 protected WebApplicationContext createRootApplicationContext( ServletContext servletContext){ SpringApplicationBuilder builder = createSpringApplicationBuilder(); builder.main(getClass()); //获取存在的Web应用上下文 ApplicationContext parent = getExistingRootWebApplicationContext(servletContext); if (parent != null) {//如果不为控设置属性,然后创建ParentContextApplicationContextInitializer对象 this.logger.info("Root context already created (using as parent)."); servletContext.setAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null); builder.initializers(new ParentContextApplicationContextInitializer(parent)); } //创建ServletContextApplicationContextInitializer对象 builder.initializers( new ServletContextApplicationContextInitializer(servletContext)); //设置ApplicationContext的类属性 builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class); builder = configure(builder); //设置一个实现监听器,这个是内部私有类 builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext)); //根据上面属性值创建SpringApplication对象。 SpringApplication application = builder.build(); //检查新创建的SpringApplication对象 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)); } //启动SpringApplication对象 return run(application); }
/** * Returns the {@code SpringApplicationBuilder} that is used to configure and create * the {@link SpringApplication}. The default implementation returns a new * {@code SpringApplicationBuilder} in its default state. * @return the {@code SpringApplicationBuilder}. * @since 1.3.0 */ //创建SpringApplicationBuilder对象,就是你创建工程的那个starter的对象。 protected SpringApplicationBuilder createSpringApplicationBuilder(){ returnnew SpringApplicationBuilder(); }
/** * Called to run a fully configured {@link SpringApplication}. * @param application the application to run * @return the {@link } */ //启动SpringBoot的run方法,在这个地方SpringBoot启动的点,这个地方就是我们平常自己在Idea中点击run()方法的地方。我们自己创建的所有的启动应用都是基于SpringApplication去启动的。这里面去去创建Bean容器,实例化Bean. protected WebApplicationContext run(SpringApplication application){ return (WebApplicationContext) application.run(); } //获取存在的Spring 的容器 private ApplicationContext getExistingRootWebApplicationContext( ServletContext servletContext){ Object context = servletContext.getAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); if (context instanceof ApplicationContext) { return (ApplicationContext) context; } returnnull; }
/** * Configure the application. Normally all you would need to do is to add sources * (e.g. config classes) because other settings have sensible defaults. You might * choose (for instance) to add default command line arguments, or set an active * Spring profile. * @param builder a builder for the application context * @return the application builder * @see SpringApplicationBuilder */ protected SpringApplicationBuilder configure(SpringApplicationBuilder builder){ return builder; } //内部类web环境注册初始化 privatestaticfinalclassWebEnvironmentPropertySourceInitializer implementsApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered{