博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring boot传统部署
阅读量:5828 次
发布时间:2019-06-18

本文共 9979 字,大约阅读时间需要 33 分钟。

使用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. }  
@SpringBootApplicationpublic class TestApplication extends SpringBootServletInitializer{        @Override    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {        return builder.sources(TestApplication .class);    }    public static void main(String[] args) {        SpringApplication.run(TestApplication.class, args);    }}

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

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

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

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

 也就是只要是非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()  
SpringApplication.run(String...)    SpringApplication.createAndRefreshContext(SpringApplicationRunListeners, ApplicationArguments)        SpringApplication.refresh(ApplicationContext)            AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).refresh()                AnnotationConfigEmbeddedWebApplicationContext(AbstractApplicationContext).refresh()                    AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).onRefresh()                        AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).createEmbeddedServletContainer()

 有下面一个分支代码

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

 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. }  
public GenericWebApplicationContext(ServletContext servletContext) {        this.servletContext = servletContext;    }    public GenericWebApplicationContext(DefaultListableBeanFactory beanFactory, ServletContext servletContext) {        super(beanFactory);        this.servletContext = servletContext;    }    public void setServletContext(ServletContext servletContext) {        this.servletContext = servletContext;    }

 查找一下,发现构造函数并没有地方调用,调用的是这个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.run(String...)    SpringApplication.createAndRefreshContext(SpringApplicationRunListeners, ApplicationArguments)        SpringApplication.applyInitializers(ConfigurableApplicationContext)            ServletContextApplicationContextInitializer.initialize(ConfigurableApplicationContext)                ServletContextApplicationContextInitializer.initialize(ConfigurableWebApplicationContext)                    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. }  
protected void applyInitializers(ConfigurableApplicationContext context) {        for (ApplicationContextInitializer initializer : getInitializers()) {            Class
requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } }

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

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

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

 内部实现就是

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

 

至于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. }  
private void selfInitialize(ServletContext servletContext) throws ServletException {    --------省略------------        for (ServletContextInitializer beans : getServletContextInitializerBeans()) {            beans.onStartup(servletContext);        }    }

 类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. }  
@Override    public void onStartup(ServletContext servletContext) throws ServletException {        Assert.notNull(this.servlet, "Servlet must not be null");        String name = getServletName();        if (!isEnabled()) {            logger.info("Servlet " + name + " was not registered (disabled)");            return;        }        logger.info("Mapping servlet: '" + name + "' to " + this.urlMappings);        Dynamic added = servletContext.addServlet(name, this.servlet);        if (added == null) {            logger.info("Servlet " + name + " was not registered "                    + "(possibly already registered?)");            return;        }        configure(added);    }

 

转载地址:http://gkodx.baihongyu.com/

你可能感兴趣的文章
zabbix监控php状态(四)
查看>>
实战Django:小型CMS Part2
查看>>
原创]windows server 2012 AD架构试验系列 – 16更改DC计算机名
查看>>
统治世界的十大算法
查看>>
SSH中调用另一action的方法(chain,redirect)
查看>>
表格排序
查看>>
关于Android四大组件的学习总结
查看>>
java只能的round,ceil,floor方法的使用
查看>>
新开的博客,为自己祝贺一下
查看>>
【CQOI2011】放棋子
查看>>
采用JXL包进行EXCEL数据写入操作
查看>>
一周总结
查看>>
将txt文件转化为json进行操作
查看>>
线性表4 - 数据结构和算法09
查看>>
Online Patching--EBS R12.2最大的改进
查看>>
Binary Search Tree Iterator leetcode
查看>>
uva-317-找规律
查看>>
我的2014-相对奢侈的生活
查看>>
Java设计模式
查看>>
一文读懂 AOP | 你想要的最全面 AOP 方法探讨
查看>>