问题出现背景
最近项目上要求国产化,麒麟V10、达梦V8等等;其中一个要求就需要使用应用服务器TongWeb,不能使用SpringBoot内嵌的Tomcat;TongWeb是以war包进行部署管理的,那就基于现有的JAR部署改呗;
一、jar转war适配
1.修改pom.xml
将打包方式由jar改成war
<artifactId>com.idto.project</artifactId>
<artifactId>chinese</artifactId>
<packaging>war</packaging>
排除tomcat,引入servlet-api
<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>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
新增一个maven插件用于打war包
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
2.修改启动类
启动类集成SpringBootServletInitializer 重写configure方法
@SpringBootApplication(exclude = {DruidDataSourceAutoConfigure.class})
@EnableScheduling
@EnableAsync
@EnableTransactionManagement
@EnableRetry
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application .class);
}
public static void main(String[] args) {
SpringApplication.run(Application .class, args);
}
}
至此,项目jar部署转war部署改造完成,配置tomcat启动项目调试
二、@WebFilter问题
提前剧透:
@WebFilter使用外置tomcat无法通过@Resource从容器中获取bean,但使用内置tomcat则无此问题
如果tomcat启动日志乱码,先修改tomcat编码方式:
打开配置文件apache-tomcat-8.5.81/conf/logging.properties
搜索关键字:.encoding = UTF-8
并替换成为:.encoding = GBK
tomcat启动项目调试时始终报错,关键信息如下:
错误关键信息1:
18-Jun-2022 15:26:30.818 严重 [RMI TCP Connection(2)-127.0.0.1] org.apache.catalina.core.StandardContext.startInternal 一个或多个筛选器启动失败。完整的详细信息将在相应的容器日志文件中找到
18-Jun-2022 15:26:30.818 严重 [RMI TCP Connection(2)-127.0.0.1] org.apache.catalina.core.StandardContext.startInternal 由于之前的错误,Context[/Chinese_server_war]启动失败
错误关键信息2:
..........
18-Jun-2022 15:44:58.271 严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.StandardContext.filterStart 启动过滤器异常
javax.naming.NamingException: 无法创建资源实例
at org.apache.naming.factory.FactoryBase.getObjectInstance(FactoryBase.java:98)
at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:321)
at org.apache.naming.NamingContext.lookup(NamingContext.java:846)
at org.apache.naming.NamingContext.lookup(NamingContext.java:157)
at org.apache.naming.NamingContext.lookup(NamingContext.java:834)
at org.apache.naming.NamingContext.lookup(NamingContext.java:171)
at org.apache.catalina.core.DefaultInstanceManager.lookupFieldResource(DefaultInstanceManager.java:576)
..........
根据关键信息2提示,在NamingManager.java 321行前面下断点进行debug
最后发现是因为安全模块CsrfFilter在启动时无法通过@Resource获取bean,原始代码如下:
@Slf4j
@Component
@WebFilter(filterName = "CsrfFilter", urlPatterns = "/*")
public class CsrfFilter implements Filter {
// 病根所在
@Resource
private CsrfProperties csrfProperties;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
//******
}
估计问题是springboot启动时的先后顺序所致,那我们就手动从容器中去获取bean,修改代码如下:
@Slf4j
@Component
@WebFilter(filterName = "CsrfFilter", urlPatterns = "/*")
public class CsrfFilter implements Filter {
private static CsrfProperties csrfProperties;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 若csrfProperties为空,则手动去容器中获取bean
if (Objects.isNull(csrfProperties)) {
ServletContext context = filterConfig.getServletContext();
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(context);
csrfProperties = ctx.getBean(CsrfProperties.class);
}
//******
}
修改完成后debug,发现可以获取到csrfProperties的属性值,服务提供不报错了。
完美收官~~~
总结:在适配过程有些问题问题,通过打印的日志不能直接定位到问题所在,所以可以通过关键位置debug,根据上下文进行分析判断解决。
tips:网上也有其他解决方案,因为我这是已经功能代码,为了减少代码的改动,故手动获取bean的方式,以尽可能少的改动去解决问题,避免产生其他连带问题。