SpringBoot war 和 jar 启动原理

SpringBoot war 启动原理

一、配置war包启动

1、 将pom.xml文件的packaging属性改为war,然后排除tomcat的依赖 。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>

2、 在主配置类中继承SpringBootServletInitializer,然后重写configure方法 。

/**
 * @author lixm
 */
@SpringBootApplication
public class App extends SpringBootServletInitializer{
	public static void main(String[] args) throws Exception {
		SpringApplication.run(App.class, args);
	}
	// 注意下面App.class 配置自己的启动类
	protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
		return builder.sources(new Class[] {App.class});
	}
}

3、打war包

略过。

二、源码分析

1、jar启动构建tomcat容器源码分析

涉计到SpringBoot源码,下面只会分析中间件的先关代码,不再分析和本次内容无关的代码,如果需要后续可以单独出一遍boot的源码。

// 1、spingboot 启动类	
SpringApplication.run(App.class, args);
// 2、org.springframework.boot.SpringApplication.run(String...) 

在这里插入图片描述

// 1、由于我们是Servlet项目,所以返回的是AnnotationConfigServletWebServerApplicationContext
static class Factory implements ApplicationContextFactory {

    @Override
    public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
        return (webApplicationType != WebApplicationType.SERVLET) ? null
            : new AnnotationConfigServletWebServerApplicationContext();
    }
}
// 2、刷新refreshcontext上下文
//通过堆栈代码执行到 
1org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh()
 -> 2org.springframework.context.support.AbstractApplicationContext.refresh()
    	// 上面的AbstractApplicationContext.refresh 函数里面会执行到onRefresh方法。
    -> 3org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh()
		-> 4createWebServer();


private void createWebServer() {
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
    // 初始化条件
		if (webServer == null && servletContext == null) {
			StartupStep createWebServer = 	this.getApplicationStartup().start("spring.boot.webserver.create");
			ServletWebServerFactory factory = getWebServerFactory();
			createWebServer.tag("factory", factory.getClass().toString());
			this.webServer = factory.getWebServer(getSelfInitializer());
			createWebServer.end();
			getBeanFactory().registerSingleton("webServerGracefulShutdown",
					new WebServerGracefulShutdownLifecycle(this.webServer));
			getBeanFactory().registerSingleton("webServerStartStop",
					new WebServerStartStopLifecycle(this, this.webServer));
		}
		else if (servletContext != null) {
			try {
				getSelfInitializer().onStartup(servletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context", ex);
			}
		}
		initPropertySources();
	}
/**
	注意上述webServer的初始化条件是 webServer == null && servletContext == null,这个很关键。下面war源码会在这个地方已经初始化了 servletContext 环境。(很重要)
*/

总结:通过代码的分析,可以发现webServer的构建是依赖IOC容器启动的过程中。

2、war启动源码分析

分析:war启动配置里友谊和核心的操作。主类是需要继承 SpringBootServletInitializer。该类是实现 WebApplicationInitializer。 利用SPI机制的SpringServletContainerInitializer类 可以找到这个WebApplicationInitializer。

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
     
    @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 {
                        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);
        for (WebApplicationInitializer initializer : initializers) {
            initializer.onStartup(servletContext);
        }
    }
 
}

ServletContainerInitializer 是 Servlet 3.0 新增的一个接口,主要用于在容器启动阶段通过编程风格注册Filter, Servlet以及Listener,以取代通过web.xml配置注册。这样就利于开发内聚的web应用框架.
我们以SpringMVC举例, servlet3.0之前我们需要在web.xml中依据Spring的规范新建一堆配置。这样就相当于将框架和容器紧耦合了。而在3.x后注册的功能内聚到Spring里,Spring-web就变成一个纯粹的即插即用的组件,不用依据应用环境定义一套新的配置。 (这里主要是描述servlet的一个扩展特性,利用这个特性实现了IOC初始化工作)

从上述总结我们可以理解就是外部中间件在启动的时候会执行 SpringBootServletInitializer 里的 onStartup 方法。

@Override
public void onStartup(ServletContext servletContext) throws ServletException {
    servletContext.setAttribute(LoggingApplicationListener.REGISTER_SHUTDOWN_HOOK_PROPERTY, false);
    // Logger initialization is deferred in case an ordered
    // LogServletContextInitializer is being used
    this.logger = LogFactory.getLog(getClass());
    WebApplicationContext rootApplicationContext = createRootApplicationContext(servletContext);
    if (rootApplicationContext != null) {
        servletContext.addListener(new SpringBootContextLoaderListener(rootApplicationContext, servletContext));
    }
    else {
        this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not "
                          + "return an application context");
    }
}

大家注意这个 函数参数 ServletContext servletContext 。这个是中间件调用的时候传进来的。这个很关键!!!

接着分析这个函数代码。

WebApplicationContext rootApplicationContext = createRootApplicationContext(servletContext);
在这里插入图片描述

这里面两处关键代码。

1、ServletContext 环境绑定到 initializers 里。

2、执行了 SpringApplication 的run 方法。

在这里插入图片描述

你会发现这块代码和上面jar执行的代码开始重叠了,但是在执行prepareContext 函数时候,由于上述 ServletContext 绑定到initializers 里,这个prepareContext 就做了一件事
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
这行代码主要就是把servlet环境绑定到IOC上的servletContext属性上了。回顾我们jar启动的时候,我们初始化webServer时候说的是他有一个条件。(webServer == null && servletContext == null),看到这里我们或许就明白了原理。

总结: 和jar启动原理不同是 IOC容器启动是依赖webServer初始化。

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值