Tomcat 8.5 运行war包出现“一个或多个筛选器启动失败”问题排除
Posted katus
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tomcat 8.5 运行war包出现“一个或多个筛选器启动失败”问题排除相关的知识,希望对你有一定的参考价值。
Tomcat 8.5 运行war包出现“一个或多个筛选器启动失败”问题排除
本文系本人开发和学习过程中遇到的问题记录,如有错误,还请不吝赐教。
问题说明
前些时间新的Java后端项目(基于Spring Boot)采用war包打包,使用Tomcat 8.5进行发布。在引入了多个过滤器之后,发布时出现了问题,始终报错“一个或多个筛选器启动失败”。
项目采用的Spring Boot 2.4.5版本,使用Maven 3.6.3进行依赖管理。
相关的依赖配置如下:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<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>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- 其他不相关依赖已经省略 -->
</dependencies>
问题排除
网上有很多关于 Tomcat 发布war项目时报错“一个或多个筛选器启动失败”的文章,基本都是由于在打war包的过程中没有将相关的依赖引入,或者引入不全导致。
经过仔细核对,本项目的打包过程不存在任何问题,也不存在依赖没有引入的问题。
后来在查看源码的过程中发现Spring Boot 2.4.5内嵌的Tomcat版本为9.0,过滤器接口源码如下。
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package javax.servlet;
import java.io.IOException;
/**
* A filter is an object that performs filtering tasks on either the request to
* a resource (a servlet or static content), or on the response from a resource,
* or both. <br>
* <br>
* Filters perform filtering in the <code>doFilter</code> method. Every Filter
* has access to a FilterConfig object from which it can obtain its
* initialization parameters, a reference to the ServletContext which it can
* use, for example, to load resources needed for filtering tasks.
* <p>
* Filters are configured in the deployment descriptor of a web application
* <p>
* Examples that have been identified for this design are<br>
* 1) Authentication Filters <br>
* 2) Logging and Auditing Filters <br>
* 3) Image conversion Filters <br>
* 4) Data compression Filters <br>
* 5) Encryption Filters <br>
* 6) Tokenizing Filters <br>
* 7) Filters that trigger resource access events <br>
* 8) XSL/T filters <br>
* 9) Mime-type chain Filter <br>
*
* @since Servlet 2.3
*/
public interface Filter
/**
* Called by the web container to indicate to a filter that it is being
* placed into service. The servlet container calls the init method exactly
* once after instantiating the filter. The init method must complete
* successfully before the filter is asked to do any filtering work.
* <p>
* The web container cannot place the filter into service if the init method
* either:
* <ul>
* <li>Throws a ServletException</li>
* <li>Does not return within a time period defined by the web
* container</li>
* </ul>
* The default implementation is a NO-OP.
*
* @param filterConfig The configuration information associated with the
* filter instance being initialised
*
* @throws ServletException if the initialisation fails
*/
public default void init(FilterConfig filterConfig) throws ServletException
/**
* The <code>doFilter</code> method of the Filter is called by the container
* each time a request/response pair is passed through the chain due to a
* client request for a resource at the end of the chain. The FilterChain
* passed in to this method allows the Filter to pass on the request and
* response to the next entity in the chain.
* <p>
* A typical implementation of this method would follow the following
* pattern:- <br>
* 1. Examine the request<br>
* 2. Optionally wrap the request object with a custom implementation to
* filter content or headers for input filtering <br>
* 3. Optionally wrap the response object with a custom implementation to
* filter content or headers for output filtering <br>
* 4. a) <strong>Either</strong> invoke the next entity in the chain using
* the FilterChain object (<code>chain.doFilter()</code>), <br>
* 4. b) <strong>or</strong> not pass on the request/response pair to the
* next entity in the filter chain to block the request processing<br>
* 5. Directly set headers on the response after invocation of the next
* entity in the filter chain.
*
* @param request The request to process
* @param response The response associated with the request
* @param chain Provides access to the next filter in the chain for this
* filter to pass the request and response to for further
* processing
*
* @throws IOException if an I/O error occurs during this filter's
* processing of the request
* @throws ServletException if the processing fails for any other reason
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
/**
* Called by the web container to indicate to a filter that it is being
* taken out of service. This method is only called once all threads within
* the filter's doFilter method have exited or after a timeout period has
* passed. After the web container calls this method, it will not call the
* doFilter method again on this instance of the filter. <br>
* <br>
*
* This method gives the filter an opportunity to clean up any resources
* that are being held (for example, memory, file handles, threads) and make
* sure that any persistent state is synchronized with the filter's current
* state in memory.
*
* The default implementation is a NO-OP.
*/
public default void destroy()
可以发现过滤器接口中init()
和destroy()
方法都是默认方法,在实现过滤器接口时无需一定实现上述两个方法。因此我的其中的一个过滤器如下。
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 跨域过滤器
* @author SUN Katus
* @version 1.0, 2021-05-21
*/
@Order(1)
@Slf4j
@WebFilter(
filterName = "corsFilter",
urlPatterns = "/*"
)
public class CORSFilter implements Filter
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.setContentType("application/json; charset=utf-8");
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,OPTIONS");
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type,Authorization,X-Requested-With,token");
httpResponse.setHeader("Access-Control-Expose-Headers", "*");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name()))
httpResponse.setStatus(HttpStatus.SC_OK);
return;
chain.doFilter(httpRequest, httpResponse);
后来我又去查看了Tomcat 8.5过滤器接口的源码如下。
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package javax.servlet;
import java.io.IOException;
/**
* A filter is an object that performs filtering tasks on either the request to
* a resource (a servlet or static content), or on the response from a resource,
* or both. <br>
* <br>
* Filters perform filtering in the <code>doFilter</code> method. Every Filter
* has access to a FilterConfig object from which it can obtain its
* initialization parameters, a reference to the ServletContext which it can
* use, for example, to load resources needed for filtering tasks.
* <p>
* Filters are configured in the deployment descriptor of a web application
* <p>
* Examples that have been identified for this design are<br>
* 1) Authentication Filters <br>
* 2) Logging and Auditing Filters <br>
* 3) Image conversion Filters <br>
* 4) Data compression Filters <br>
* 5) Encryption Filters <br>
* 6) Tokenizing Filters <br>
* 7) Filters that trigger resource access events <br>
* 8) XSL/T filters <br>
* 9) Mime-type chain Filter <br>
*
* @since Servlet 2.3
*/
public interface Filter
/**
* Called by the web container to indicate to a filter that it is being
* placed into service. The servlet container calls the init method exactly
* once after instantiating the filter. The init method must complete
* successfully before the filter is asked to do any filtering work.
* <p>
* The web container cannot place the filter into service if the init method
* either:
* <ul>
* <li>Throws a ServletException</li>
* <li>Does not return within a time period defined by the web
* container</li>
* </ul>
*
* @param filterConfig The configuration information associated with the
* filter instance being initialised
*
* @throws ServletException if the initialisation fails
*/
public void init(FilterConfig filterConfig) throws ServletException;
/**
* The <code>doFilter</code> method of the Filter is called by the container
* each time a request/response pair is passed through the chain due to a
* client request for a resource at the end of the chain. The FilterChain
* passed in to this method allows the Filter to pass on the request and
* response to the next entity in the chain.
* <p>
* A typical implementation of this method would follow the following
* pattern:- <br>
* 1. Examine the request<br>
* 2. Optionally wrap the request object with a custom implementation to
* filter content or headers for input filtering <br>
* 3. Optionally wrap the response object with a custom implementation to
* filter content or headers for output filtering <br>
* 4. a) <strong>Either</strong> invoke the next entity in the chain using
* the FilterChain object (<code>chain.doFilter()</code>), <br>
* 4. b) <strong>or</strong> not pass on the request/response pair to the
* next entity in the filter chain to block the request processing<br>
* 5. Directly set headers on the response after invocation of the next
* entity in the filter chain.
*
* @param request The request to process
* @param response The response associated with the request
* @param chain Provides access to the next filter in the chain for this
* filter to pass the request and response to for further
* processing
*
* @throws IOException if an I/O error occurs during this filter's
* processing of the request
* @throws ServletException if the processing fails for any other reason
*/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
/**
* Called by the web container to indicate to a filter that it is being
* taken out of service. This method is only called once all threads within
* the filter's doFilter method have exited or after a timeout period has
* passed. After the web container calls this method, it will not call the
* doFilter method again on this instance of the filter. <br>
* <br>
*
* This method gives the filter an opportunity to clean up any resources
* that are being held (for example, memory, file handles, threads) and make
* sure that any persistent state is synchronized with the filter's current
* state in memory.
*/
public void destroy();
可以明显发现过滤器接口中init()
和destroy()
方法声明都是普通的,没有默认实现,这也就说明了相应的过滤器需要显式实现过滤器接口中的全部方法。
经过修改的过滤器如下。
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 跨域过滤器
* @author SUN Katus
* @version 1.0, 2021-05-21
*/
@Order(1)
@Slf4j
@WebFilter(
filterName = "corsFilter",
urlPatterns = "/*"
)
public class CORSFilter implements Filter
@Override
public void init(FilterConfig filterConfig)
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.setContentType("application/json; charset=utf-8");
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,OPTIONS");
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type,Authorization,X-Requested-With,token");
httpResponse.setHeader("Access-Control-Expose-Headers", "*");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name()))
httpResponse.setStatus(HttpStatus.SC_OK);
return;
chain.doFilter(httpRequest, httpResponse);
@Override
public void destroy()
修改之后打包部署,完美运行,问题解决。
问题反思
归根到底是开发时使用的依赖和生产环境中不统一导致的,以后应当尽可能减少这种不规范的开发习惯。
以上是关于Tomcat 8.5 运行war包出现“一个或多个筛选器启动失败”问题排除的主要内容,如果未能解决你的问题,请参考以下文章
Tomcat 8.5 运行war包出现“一个或多个筛选器启动失败”问题排除
Tomcat 8.5 运行war包出现“一个或多个筛选器启动失败”问题排除
maven打包成war,方到tomcat里面运行,页面出现乱码
使用war将spring-boot和angular 7应用程序部署到tomcat 8.5中
springSecurityService.principal 在 tomcat 8.5 中部署为 WAR 时返回 Null