spring boot中servlet启动原理
啟動(dòng)過程及原理
1 spring boot 應(yīng)用啟動(dòng)運(yùn)行run方法
StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;FailureAnalyzers analyzers = null;configureHeadlessProperty();SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);Banner printedBanner = printBanner(environment);//創(chuàng)建一個(gè)ApplicationContext容器context = createApplicationContext();analyzers = new FailureAnalyzers(context);prepareContext(context, environment, listeners, applicationArguments,printedBanner);//刷新IOC容器 refreshContext(context);afterRefresh(context, applicationArguments);listeners.finished(context, null);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}return context;}catch (Throwable ex) {handleRunFailure(context, listeners, analyzers, ex);throw new IllegalStateException(ex);}?
2? createApplicationContext():創(chuàng)建IOC容器,如果是web應(yīng)用則創(chuàng)建AnnotationConfigEmbeddedWebApplacation的IOC容器,如果不是,則創(chuàng)建AnnotationConfigApplication的IOC容器
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."+ "annotation.AnnotationConfigApplicationContext";/*** The class name of application context that will be used by default for web* environments.*/public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {//根據(jù)應(yīng)用環(huán)境,創(chuàng)建不同的IOC容器contextClass = Class.forName(this.webEnvironment? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable create a default ApplicationContext, "+ "please specify an ApplicationContextClass",ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);}
3? ? refreshContext(context) spring boot刷新IOC容器(創(chuàng)建容器對象,并初始化容器,創(chuàng)建容器每一個(gè)組件)
private void refreshContext(ConfigurableApplicationContext context) {refresh(context);if (this.registerShutdownHook) {try {context.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments. }}}4 refresh(context);刷新剛才創(chuàng)建的IOC容器
protected void refresh(ApplicationContext applicationContext) {Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);((AbstractApplicationContext) applicationContext).refresh();}5 調(diào)用父類的refresh()的方法
public void refresh() throws BeansException, IllegalStateException {Object var1 = this.startupShutdownMonitor;synchronized(this.startupShutdownMonitor) {this.prepareRefresh();ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();this.prepareBeanFactory(beanFactory);try {this.postProcessBeanFactory(beanFactory);this.invokeBeanFactoryPostProcessors(beanFactory);this.registerBeanPostProcessors(beanFactory);this.initMessageSource();this.initApplicationEventMulticaster();this.onRefresh();this.registerListeners();this.finishBeanFactoryInitialization(beanFactory);this.finishRefresh();} catch (BeansException var9) {if (this.logger.isWarnEnabled()) {this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);}this.destroyBeans();this.cancelRefresh(var9);throw var9;} finally {this.resetCommonCaches();}}}6? 抽象父類AbstractApplicationContext類的子類EmbeddedWebApplicationContext的onRefresh方法
@Overrideprotected void onRefresh() {super.onRefresh();try {createEmbeddedServletContainer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start embedded container",ex);}}7? 在createEmbeddedServletContainer放啊發(fā)中會(huì)獲取嵌入式Servlet容器工廠,由容器工廠創(chuàng)建Servlet
private void createEmbeddedServletContainer() {EmbeddedServletContainer localContainer = this.embeddedServletContainer;ServletContext localServletContext = getServletContext();if (localContainer == null && localServletContext == null) {//獲取嵌入式Servlet容器工廠EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
//根據(jù)容器工廠獲取對應(yīng)嵌入式Servlet容器this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer());}else if (localServletContext != null) {try {getSelfInitializer().onStartup(localServletContext);}catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context",ex);}}initPropertySources();}
8? 從IOC容器中獲取Servlet容器工廠
//EmbeddedWebApplicationContext#getEmbeddedServletContainerFactory protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() { // Use bean names so that we don't consider the hierarchy String[] beanNames = getBeanFactory() .getBeanNamesForType(EmbeddedServletContainerFactory.class); if (beanNames.length == 0) { throw new ApplicationContextException( "Unable to start EmbeddedWebApplicationContext due to missing " + "EmbeddedServletContainerFactory bean."); } if (beanNames.length > 1) { throw new ApplicationContextException( "Unable to start EmbeddedWebApplicationContext due to multiple " + "EmbeddedServletContainerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); } return getBeanFactory().getBean(beanNames[0], EmbeddedServletContainerFactory.class); }?
9? 使用Servlet容器工廠獲取嵌入式Servlet容器,具體使用哪一個(gè)容器工廠看配置環(huán)境依賴
this.embeddedServletContainer = containerFactory .getEmbeddedServletContainer(getSelfInitializer());10? 上述創(chuàng)建過程? 首先啟動(dòng)IOC容器,接著啟動(dòng)嵌入式Servlet容器,接著將IOC容器中剩下沒有創(chuàng)建的對象獲取出來,比如自己創(chuàng)建的controller
// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory); protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {// Initialize conversion service for this context.if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {beanFactory.setConversionService(beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));}// Register a default embedded value resolver if no bean post-processor// (such as a PropertyPlaceholderConfigurer bean) registered any before:// at this point, primarily for resolution in annotation attribute values.if (!beanFactory.hasEmbeddedValueResolver()) {beanFactory.addEmbeddedValueResolver(new StringValueResolver() {@Overridepublic String resolveStringValue(String strVal) {return getEnvironment().resolvePlaceholders(strVal);}});}// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);for (String weaverAwareName : weaverAwareNames) {getBean(weaverAwareName);}// Stop using the temporary ClassLoader for type matching.beanFactory.setTempClassLoader(null);// Allow for caching all bean definition metadata, not expecting further changes. beanFactory.freezeConfiguration();// Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons();}?
看看?preInstantiateSingletons方法
public void preInstantiateSingletons() throws BeansException {if (this.logger.isDebugEnabled()) {this.logger.debug("Pre-instantiating singletons in " + this);}List<String> beanNames = new ArrayList(this.beanDefinitionNames);Iterator var2 = beanNames.iterator();while(true) {while(true) {String beanName;RootBeanDefinition bd;do {do {do {if (!var2.hasNext()) {var2 = beanNames.iterator();while(var2.hasNext()) {beanName = (String)var2.next();Object singletonInstance = this.getSingleton(beanName);if (singletonInstance instanceof SmartInitializingSingleton) {final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton)singletonInstance;if (System.getSecurityManager() != null) {AccessController.doPrivileged(new PrivilegedAction<Object>() {public Object run() {smartSingleton.afterSingletonsInstantiated();return null;}}, this.getAccessControlContext());} else {smartSingleton.afterSingletonsInstantiated();}}}return;}beanName = (String)var2.next();bd = this.getMergedLocalBeanDefinition(beanName);} while(bd.isAbstract());} while(!bd.isSingleton());} while(bd.isLazyInit());if (this.isFactoryBean(beanName)) {final FactoryBean<?> factory = (FactoryBean)this.getBean("&" + beanName);boolean isEagerInit;if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {isEagerInit = ((Boolean)AccessController.doPrivileged(new PrivilegedAction<Boolean>() {public Boolean run() {return ((SmartFactoryBean)factory).isEagerInit();}}, this.getAccessControlContext())).booleanValue();} else {isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean)factory).isEagerInit();}if (isEagerInit) {this.getBean(beanName);}} else {//注冊beanthis.getBean(beanName);}}}}
是使用getBean方法來通過反射將所有未創(chuàng)建的實(shí)例創(chuàng)建出來
??使用嵌入式Servlet容器:
? 優(yōu)點(diǎn):? ?簡單,便攜
缺點(diǎn):? ?默認(rèn)不支持jsp,優(yōu)化定制比較復(fù)雜
使用外置Servlet容器的步驟:
1? 必須創(chuàng)建war項(xiàng)目,需要?jiǎng)纖eb項(xiàng)目的目錄結(jié)構(gòu)
2? 嵌入式Tomcat依賴scope指定provided
3? 編寫SpringBootServletInitializer類子類,并重寫configure方法
public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(SpringBoot04WebJspApplication.class); } }?
? ? ? ?4? 啟動(dòng)服務(wù)器
jar包和war包啟動(dòng)區(qū)別
? ? jar包:執(zhí)行SpringBootApplication的run方法,啟動(dòng)IOC容器,然后創(chuàng)建嵌入式Servlet容器
war包:? 先是啟動(dòng)Servlet服務(wù)器,服務(wù)器啟動(dòng)Springboot應(yīng)用(springBootServletInitizer),然后啟動(dòng)IOC容器
Servlet 3.0+規(guī)則
? 1? 服務(wù)器啟動(dòng)(web應(yīng)用啟動(dòng)),會(huì)創(chuàng)建當(dāng)前web應(yīng)用里面所有jar包里面的ServletContainerlnitializer實(shí)例
? ?2?ServletContainerInitializer的實(shí)現(xiàn)放在jar包的META-INF/services文件夾下
3? 還可以使用@HandlesTypes注解,在應(yīng)用啟動(dòng)的時(shí)候加載指定的類。
外部Tomcat流程以及原理
①? 啟動(dòng)Tomcat
②??根據(jù)上述描述的Servlet3.0+規(guī)則,可以在Spring的web模塊里面找到有個(gè)文件名為javax.servlet.ServletContainerInitializer的文件,而文件的內(nèi)容為org.springframework.web.SpringServletContainerInitializer,用于加載SpringServletContainerInitializer類
③看看SpringServletContainerInitializer定義
@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer {/*** Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}* implementations present on the application classpath.* <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},* Servlet 3.0+ containers will automatically scan the classpath for implementations* of Spring's {@code WebApplicationInitializer} interface and provide the set of all* such types to the {@code webAppInitializerClasses} parameter of this method.* <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,* this method is effectively a no-op. An INFO-level log message will be issued notifying* the user that the {@code ServletContainerInitializer} has indeed been invoked but that* no {@code WebApplicationInitializer} implementations were found.* <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,* they will be instantiated (and <em>sorted</em> if the @{@link* org.springframework.core.annotation.Order @Order} annotation is present or* the {@link org.springframework.core.Ordered Ordered} interface has been* implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}* method will be invoked on each instance, delegating the {@code ServletContext} such* that each instance may register and configure servlets such as Spring's* {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},* or any other Servlet API componentry such as filters.* @param webAppInitializerClasses all implementations of* {@link WebApplicationInitializer} found on the application classpath* @param servletContext the servlet context to be initialized* @see WebApplicationInitializer#onStartup(ServletContext)* @see AnnotationAwareOrderComparator*/@Overridepublic void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)throws ServletException {List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();if (webAppInitializerClasses != null) {for (Class<?> waiClass : webAppInitializerClasses) {// Be defensive: Some servlet containers provide us with invalid classes,// no matter what @HandlesTypes says...if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&WebApplicationInitializer.class.isAssignableFrom(waiClass)) {try {//為所有的WebApplicationInitializer類型創(chuàng)建實(shí)例,并加入集合中initializers.add((WebApplicationInitializer) 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);
//調(diào)用每一個(gè)WebApplicationInitializer實(shí)例的onstartup方法for (WebApplicationInitializer initializer : initializers) {initializer.onStartup(servletContext);}}}
public abstract class SpringBootServletInitializer implements WebApplicationInitializer { //other code... @Override public void onStartup(ServletContext servletContext) throws ServletException { // Logger initialization is deferred in case a ordered // LogServletContextInitializer is being used this.logger = LogFactory.getLog(getClass()); //創(chuàng)建IOC容器 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) { //創(chuàng)建Spring應(yīng)用構(gòu)建器,并進(jìn)行相關(guān)屬性設(shè)置 SpringApplicationBuilder builder = createSpringApplicationBuilder(); StandardServletEnvironment environment = new StandardServletEnvironment(); environment.initPropertySources(servletContext, null); builder.environment(environment); 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(AnnotationConfigEmbeddedWebApplicationContext.class); //調(diào)用configure方法,創(chuàng)建war類型的web項(xiàng)目后,由于編寫SpringBootServletInitializer的子類重寫configure方法,所以此處調(diào)用的是我們定義的子類重寫的configure方法 builder = configure(builder); //通過構(gòu)建器構(gòu)建了一個(gè)Spring應(yīng)用 SpringApplication application = builder.build(); if (application.getSources().isEmpty() && AnnotationUtils .findAnnotation(getClass(), Configuration.class) != null) { application.getSources().add(getClass()); } Assert.state(!application.getSources().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.getSources().add(ErrorPageFilterConfiguration.class); } //啟動(dòng)Spring應(yīng)用 return run(application); } //Spring應(yīng)用啟動(dòng),創(chuàng)建并返回IOC容器 protected WebApplicationContext run(SpringApplication application) { return (WebApplicationContext) application.run(); } }
?SpringBootServletInitializer實(shí)例執(zhí)行onStartup方法的時(shí)候會(huì)通過createRootApplicationContext方法來執(zhí)行run方法,接下來的過程就同以jar包形式啟動(dòng)的應(yīng)用的run過程一樣了,在內(nèi)部會(huì)創(chuàng)建IOC容器并返回,只是以war包形式的應(yīng)用在創(chuàng)建IOC容器過程中,不再創(chuàng)建Servlet容器了。
轉(zhuǎn)載于:https://www.cnblogs.com/developerxiaofeng/p/9081689.html
總結(jié)
以上是生活随笔為你收集整理的spring boot中servlet启动原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 判断页面关闭的方法 UNLOAD/onb
- 下一篇: 汉三水属国(北地属国、安定属国)