zoukankan      html  css  js  c++  java
  • Spring boot传统部署

    使用spring boot很方便,一个jar包就可以启动了,因为它里面内嵌了tomcat等服务器。

    但是spring boot也提供了部署到独立服务器的方法。

    如果你看文档的话,从jar转换为war包很简单,pom.xml的配置修改略去不讲。

    只看source的修改,很简单,只要一个配置类,继承自SpringBootServletInitializer, 并覆盖configure方法。

    Java代码  收藏代码
    1. @SpringBootApplication  
    2. public class TestApplication extends SpringBootServletInitializer{  
    3.   
    4.       
    5.     @Override  
    6.     protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {  
    7.         return builder.sources(TestApplication .class);  
    8.     }  
    9.   
    10.     public static void main(String[] args) {  
    11.         SpringApplication.run(TestApplication.class, args);  
    12.     }  
    13. }  

     对,你没看错,就这么简单。

    但是,我觉得但凡有点儿好奇心的人都不甘于就这么用它,总会想知道为啥这样就行了?

    那么我们根据调用关系来弄个究竟。

    SpringBootServletInitializer.configure

    <-createRootApplicationContext

    <-onStartup

    <-SpringServletContainerInitializer.onStartup

     

    SpringServletContainerInitializer这个类比较特殊,实现的是interface ServletContainerInitializer,这个类的onStartup方法,是由tomcat调用了。

    那么tomcat是怎么找到它的呢?是搜寻的这个资源文件META-INF/services/javax.servlet.ServletContainerInitializer

    而在spring的包spring-web-xxxx.jar包里正好有这个文件,它注册的恰恰就是这个类

     

    写道
    org.springframework.web.SpringServletContainerInitializer

     

    这个类有个注解@HandlesTypes(WebApplicationInitializer.class)。

    调用SpringServletContainerInitializer.onStartup方法时,会把所有的WebApplicationInitializer类以及子类都传过来。

    然后再通过条件过滤一下。

    Java代码  收藏代码
    1. if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&  
    2.                         WebApplicationInitializer.class.isAssignableFrom(waiClass)) {  
    3.                     try {  
    4.                         initializers.add((WebApplicationInitializer) waiClass.newInstance());  
    5.                     }  
    6.                     catch (Throwable ex) {  
    7.                         throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);  
    8.                     }  
    9.                 }  

     也就是只要是非interface,且非抽象类,并都是WebApplicationInitializer的字类的话,就会被实例化,并最终调用。

    然后,在SpringBootServletInitializer的createRootApplicationContext方法里,最终会初始化SpringApplication,调用其run方法,跟直接运行入口的main方法是一样的了。

     

     既然从,SpringApplication.run方法以后走的逻辑是一样的,那么是不是需要启动内嵌web服务器的分支是在哪儿呢?

    顺着这条线往下走。

    Java代码  收藏代码
    1. SpringApplication.run(String...)  
    2.     SpringApplication.createAndRefreshContext(SpringApplicationRunListeners, ApplicationArguments)  
    3.         SpringApplication.refresh(ApplicationContext)  
    4.             AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).refresh()  
    5.                 AnnotationConfigEmbeddedWebApplicationContext(AbstractApplicationContext).refresh()  
    6.                     AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).onRefresh()  
    7.                         AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).createEmbeddedServletContainer()  

     有下面一个分支代码

    Java代码  收藏代码
    1. if (localContainer == null && localServletContext == null) {  
    2.     EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();  
    3.     this.embeddedServletContainer = containerFactory  
    4.             .getEmbeddedServletContainer(getSelfInitializer());  
    5. }  

     localContainer在初始化的时候没有赋值过程,一直会是null,主要是localServletContext,看看什么时候为null,什么时候有值。

    它的赋值有三个地方,两个构造函数,一个set方法

    Java代码  收藏代码
    1. public GenericWebApplicationContext(ServletContext servletContext) {  
    2.     this.servletContext = servletContext;  
    3. }  
    4. public GenericWebApplicationContext(DefaultListableBeanFactory beanFactory, ServletContext servletContext) {  
    5.     super(beanFactory);  
    6.     this.servletContext = servletContext;  
    7. }  
    8. public void setServletContext(ServletContext servletContext) {  
    9.     this.servletContext = servletContext;  
    10. }  

     查找一下,发现构造函数并没有地方调用,调用的是这个set方法,过程如下

    Java代码  收藏代码
    1. SpringApplication.run(String...)  
    2.     SpringApplication.createAndRefreshContext(SpringApplicationRunListeners, ApplicationArguments)  
    3.         SpringApplication.applyInitializers(ConfigurableApplicationContext)  
    4.             ServletContextApplicationContextInitializer.initialize(ConfigurableApplicationContext)  
    5.                 ServletContextApplicationContextInitializer.initialize(ConfigurableWebApplicationContext)  
    6.                     AnnotationConfigEmbeddedWebApplicationContext(GenericWebApplicationContext).setServletContext(ServletContext)  

     你会发现,至少到SpringApplication.applyInitializers(ConfigurableApplicationContext)这一步,部署不部署到tomcat,都会执行这个方法的,那么区别在哪儿呢?

    先看看这个方法的内容

    Java代码  收藏代码
    1. protected void applyInitializers(ConfigurableApplicationContext context) {  
    2.     for (ApplicationContextInitializer initializer : getInitializers()) {  
    3.         Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(  
    4.                 initializer.getClass(), ApplicationContextInitializer.class);  
    5.         Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");  
    6.         initializer.initialize(context);  
    7.     }  
    8. }  

     也就是说,如果注入的initializers里是否包含了ServletContextApplicationContextInitializer,就能决定是否会调用以后的逻辑。

    那么返回到文章的开头,看看抽象类SpringBootServletInitializer,就会发现在方法createRootApplicationContext里,类ServletContextApplicationContextInitializer的注入过程。

    Java代码  收藏代码
    1. builder.initializers(new ServletContextApplicationContextInitializer(servletContext));  

     内部实现就是

    Java代码  收藏代码
    1. public SpringApplicationBuilder initializers(  
    2.         ApplicationContextInitializer<?>... initializers) {  
    3.     this.application.addInitializers(initializers);  
    4.     return this;  
    5. }  

    至于spring的御用servlet——DispatcherServlet,则是通过动态添加方式添加到ServletContext里的。类EmbeddedWebApplicationContext

    Java代码  收藏代码
    1. private void selfInitialize(ServletContext servletContext) throws ServletException {  
    2. --------省略------------  
    3.     for (ServletContextInitializer beans : getServletContextInitializerBeans()) {  
    4.         beans.onStartup(servletContext);  
    5.     }  
    6. }  

     类ServletRegistrationBean

    Java代码  收藏代码
    1. @Override  
    2. public void onStartup(ServletContext servletContext) throws ServletException {  
    3.     Assert.notNull(this.servlet, "Servlet must not be null");  
    4.     String name = getServletName();  
    5.     if (!isEnabled()) {  
    6.         logger.info("Servlet " + name + " was not registered (disabled)");  
    7.         return;  
    8.     }  
    9.     logger.info("Mapping servlet: '" + name + "' to " + this.urlMappings);  
    10.     Dynamic added = servletContext.addServlet(name, this.servlet);  
    11.     if (added == null) {  
    12.         logger.info("Servlet " + name + " was not registered "  
    13.                 + "(possibly already registered?)");  
    14.         return;  
    15.     }  
    16.     configure(added);  
    17. }  
  • 相关阅读:
    Linux常用操作命令大全
    判断系统,是否是移动端
    一款兼容小程序和web端的框架 Kbone-API
    defer 和 async的区别
    快应用多次弹出添加到桌面的bug
    js判断一个元素是否在数组中
    css 画太极
    css3锯齿
    js 获取屏幕宽度
    js 判断div距离浏览器顶部或者底部的距离
  • 原文地址:https://www.cnblogs.com/jpfss/p/9719629.html
Copyright © 2011-2022 走看看