深入讲解spring boot中servlet的启动过程与原理

网友投稿 477 2023-01-27


深入讲解spring boot中servlet的启动过程与原理

前言

本文主要介绍了关于spring boot中servlet启动过程与原理的相关内容,下面话不多说了,来一起看看详细的介绍吧

启动过程与原理:

1 spring boot 应用启动运行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);

//创建一个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 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容器,具体使用哪一个容器工厂看配置环境依赖

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

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

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  还可以使用@HandlesTypes注解,在应用启动的时候加载指定的类。

外部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 {@code ServletContext} to any {@link WebApplicationInitializer}

* implementations present on the application classpath.

*

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.

*

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.

*

Assuming that one or more {@code WebApplicationInitializer} types are detected,

* they will be instantiated (and sorted 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

*/

@Override

public void onStartup(Set> webAppInitializerClasses, ServletContext servletContext)

throws ServletException {

List initializers = new LinkedList();

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

}

}

}

在上面一段长长的注释中可以看到,SpringServletContainerInitializer将@HandlesTypes(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容器了。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring Cloud之配置中心的搭建
下一篇:共享文件系统怎么找回来(共享文件丢失怎么找回来)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~