二方包依赖spring boot引发的坑

问题描述

应用使用的框架基于spring+webX,代码迭代之后,启动报错。
image.png

问题定位

问题比较

  • 网上搜索相关报错信息,大多数都是提示该类错误:

Could not open ServletContext resource [/WEB-INF/applicationContext.xml]

image.png
一般错误的原因都在该目录下找不到applicationContext.xml文件,在web.xml文件中配置一下就行。

  • 但是本文遇到的问题是:

Could not open ServletContext resource [/]

问题解决过程

  • 代码上基本上没有改动框架的配置,都是增加的业务逻辑,初步定位是maven依赖的问题
  • 从master拉新分支(能启动成功),然后将改动之后的maven依赖同步过来,果然,项目启动报了相同的错误。
  • 通过mvn dependency:tree命令,输出不报错分支依赖树和报错分支的依赖树,经过对比,发现在报错的分支中多了和spring-boot相关的启动jar包。
  • 排掉和spring启动相关的包,重新部署,项目启动成功。

细化分析

为什么在非spring boot项目中依赖spring boot包,启动报错?
上诉我们只是将spring boot相关包排掉,但是不清楚具体是哪个jar包造成的影响。

经过分类排包后定位到是org.springframework.boot:spring-boot-autoconfigure这个包引起的,但是我们的报错堆栈中并没有org.springframework.boot相关的类。
spring-boot-autoconfigure这个包用于spring boot自动配置机制,如果在应用中添加了@EnableAutoConfiguration就会触发自动配置,它会根据定义在classpath下的类,自动生成一些Bean,并加载到Spring的Context中。spring boot应用启动类上的@SpringBootApplication便继承自@EnableAutoConfiguration。
但是我们的应用只是一个spring+webx的普通web应用而已,并没有@EnableAutoConfiguration,因此不会触发自动配置,也不会加载embed tomcat。

后来发现这是来自于spring boot的一个官方issue:
始作俑者是spring-boot-autoconfigure中一个配置类JerseyAutoConfiguration中的内嵌类JerseyWebApplicationInitializer

@Order(Ordered.HIGHEST_PRECEDENCE)
public static final class JerseyWebApplicationInitializer implements WebApplicationInitializer {
  @Override
  public void onStartup(ServletContext servletContext) throws ServletException {
    // We need to switch *off* the Jersey WebApplicationInitializer because it
    // will try and register a ContextLoaderListener which we don't need
    servletContext.setInitParameter("contextConfigLocation", "<NONE>");
  }
}

继承了WebApplicationInitializer的类都会被应用加载,原因就在于SpringServletContainerInitializer,他会实例化classpath下所有继承了WebApplicationInitializer的类,并且会触发每个WebApplicationInitializer的onStartup方法。这样,servletContext就被篡改了。
在启动日志中看见有这样的内容(下图):这也印证了JerseyWebApplicationInitializer确实被加载了。
image.png
当ServletContext初始化完成之后web容器就开始启动了,我们的应用是基于webx的,配置在web.xml中的webx的监听器便开始起作用了。
WebxContextLoaderListener实现了spring的ContextLoaderListener。它会调用ContextLoader的initWebApplicationContext()方法,而在webx中初始化的是WebxComponentContext(继承自XmlWebApplicationContext)。ContextLoaderListener是使用servletContext来做初始化的,这时已经被修改过了,那个NONE就是这样被传过来的。

总结

  • 采用最小化原则,不要在二方包中依赖spring boot启动相关的jar包,(spring boot与webx不兼容的)
  • 应该将相关依赖放在根pom的dependencyManagement标签中,让子模块去显示依赖,而不要放在dependencies标签中直接定义
  • 线上部署时应该杜绝SNAPSHOT二方包,采用RELEASE

参考文章:https://www.cnblogs.com/cishengchongyan/p/6235439.html

posted @ 2021-11-01 10:52  cj杨  阅读(294)  评论(0编辑  收藏  举报