zl程序教程

您现在的位置是:首页 >  后端

当前栏目

spring boot中servlet启动原理详解编程语言

2023-06-13 09:20:46 时间
configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); Banner printedBanner = printBanner(environment); //创建一个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():创建IOC容器,如果是web应用则创建AnnotationConfigEmbeddedWebApplacation的IOC容器,如果不是,则创建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 {
//根据应用环境,创建不同的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容器(创建容器对象,并初始化容器,创建容器每一个组件)

private void refreshContext(ConfigurableApplicationContext context) { 

 refresh(context); 

 if (this.registerShutdownHook) { 

 try { 

 context.registerShutdownHook(); 

 catch (AccessControlException ex) { 

 // Not allowed in some environments. 

 }

4 refresh(context);刷新刚才创建的IOC容器

protected void refresh(ApplicationContext applicationContext) { 

 Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); 

 ((AbstractApplicationContext) applicationContext).refresh(); 

 }

5 调用父类的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方法

@Override 

 protected void onRefresh() { 

 super.onRefresh(); 

 try { 

 createEmbeddedServletContainer(); 

 catch (Throwable ex) { 

 throw new ApplicationContextException("Unable to start embedded container", 

 ex); 

 }

7  在createEmbeddedServletContainer放啊发中会获取嵌入式Servlet容器工厂,由容器工厂创建Servlet

private void createEmbeddedServletContainer() { 

 EmbeddedServletContainer localContainer = this.embeddedServletContainer; 

 ServletContext localServletContext = getServletContext(); 

 if (localContainer == null localServletContext == null) {
//获取嵌入式Servlet容器工厂 EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
//根据容器工厂获取对应嵌入式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 dont 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容器,具体使用哪一个容器工厂看配置环境依赖

this.embeddedServletContainer = containerFactory 

 .getEmbeddedServletContainer(getSelfInitializer()); 

10  上述创建过程  首先启动IOC容器,接着启动嵌入式Servlet容器,接着将IOC容器中剩下没有创建的对象获取出来,比如自己创建的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() { 

 @Override 

 public 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 {
//注册bean this.getBean(beanName); }

是使用getBean方法来通过反射将所有未创建的实例创建出来

 使用嵌入式Servlet容器:

  优点:   简单,便携

缺点:   默认不支持jsp,优化定制比较复杂

使用外置Servlet容器的步骤:

1  必须创建war项目,需要剑豪web项目的目录结构

2  嵌入式Tomcat依赖scope指定provided

3  编写SpringBootServletInitializer类子类,并重写configure方法

public class ServletInitializer extends SpringBootServletInitializer { 

 @Override 

 protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 

 return application.sources(SpringBoot04WebJspApplication.class); 

} 

 

       4  启动服务器

jar包和war包启动区别

    jar包:执行SpringBootApplication的run方法,启动IOC容器,然后创建嵌入式Servlet容器

war包:  先是启动Servlet服务器,服务器启动Springboot应用(springBootServletInitizer),然后启动IOC容器

Servlet 3.0+规则

  1  服务器启动(web应用启动),会创建当前web应用里面所有jar包里面的ServletContainerlnitializer实例

   2 ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下

3  [email protected],在应用启动的时候加载指定的类。

外部Tomcat流程以及原理

①  启动Tomcat

② 根据上述描述的Servlet3.0+规则,可以在Spring的web模块里面找到有个文件名为javax.servlet.ServletContainerInitializer的文件,而文件的内容为org.springframework.web.SpringServletContainerInitializer,用于加载SpringServletContainerInitializer类

③看看SpringServletContainerInitializer定义

@HandlesTypes(WebApplicationInitializer.class) 

public class SpringServletContainerInitializer implements ServletContainerInitializer { 

 /** 

 * Delegate the [email protected] ServletContext} to any [email protected] WebApplicationInitializer} 

 * implementations present on the application classpath. 

 * p Because this class declares @[email protected] HandlesTypes(WebApplicationInitializer.class)}, 

 * Servlet 3.0+ containers will automatically scan the classpath for implementations 

 * of Springs [email protected] WebApplicationInitializer} interface and provide the set of all 

 * such types to the [email protected] webAppInitializerClasses} parameter of this method. 

 * p If no [email protected] 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 [email protected] ServletContainerInitializer} has indeed been invoked but that 

 * no [email protected] WebApplicationInitializer} implementations were found. 

 * p Assuming that one or more [email protected] WebApplicationInitializer} types are detected, 

 * they will be instantiated (and em sorted /em if the @[email protected] 

 * org.springframework.core.annotation.Order @Order} annotation is present or 

 * the [email protected] org.springframework.core.Ordered Ordered} interface has been 

 * implemented). Then the [email protected] WebApplicationInitializer#onStartup(ServletContext)} 

 * method will be invoked on each instance, delegating the [email protected] ServletContext} such 

 * that each instance may register and configure servlets such as Springs 

 * [email protected] DispatcherServlet}, listeners such as Springs [email protected] ContextLoaderListener}, 

 * or any other Servlet API componentry such as filters. 

 * @param webAppInitializerClasses all implementations of 

 * [email protected] WebApplicationInitializer} found on the application classpath 

 * @param servletContext the servlet context to be initialized 

 * @see WebApplicationInitializer#onStartup(ServletContext) 

 * @see AnnotationAwareOrderComparator 

 @Override 

 public 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类型创建实例,并加入集合中 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);
//调用每一个WebApplicationInitializer实例的onstartup方法 for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); }

 在上面一段长长的注释中可以看到,[email protected](WebApplicationInitializer.class)标注的所有WebApplicationInitializer这个类型的类都传入到onStartup方法的Set参数中,并通过反射为这些WebApplicationInitializer类型的类创建实例;

④  方法最后,每一个WebApplicationInitilizer实现调用自己onstartup方法

⑤ 而WebApplicationInitializer有个抽象实现类SpringBootServletInitializer(记住我们继承了该抽象类),则会调用每一个WebApplicationInitializer实例(包括SpringBootServletInitializer)的onStartup方法:

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()); 

 //创建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) { 

 //创建Spring应用构建器,并进行相关属性设置 

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

 //调用configure方法,创建war类型的web项目后,由于编写SpringBootServletInitializer的子类重写configure方法,所以此处调用的是我们定义的子类重写的configure方法 

 builder = configure(builder); 

 //通过构建器构建了一个Spring应用 

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

 //启动Spring应用 

 return run(application); 

 //Spring应用启动,创建并返回IOC容器 

 protected WebApplicationContext run(SpringApplication application) { 

 return (WebApplicationContext) application.run(); 

} 

 SpringBootServletInitializer实例执行onStartup方法的时候会通过createRootApplicationContext方法来执行run方法,接下来的过程就同以jar包形式启动的应用的run过程一样了,在内部会创建IOC容器并返回,只是以war包形式的应用在创建IOC容器过程中,不再创建Servlet容器了。

原文地址:http://www.cnblogs.com/developerxiaofeng/p/9081689.html

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/16226.html

cjava