【问题解决】Tomcat启动服务时提示Filter初始化或销毁出现java.lang.AbstractMethodError错误

问题背景

最近在开发项目接口,基于SpringBoot 2.6.8,最终部署到外置Tomcat 8.5.85 下,开发过程中写了一个CookieFilter,实现javax.servlet.Filter接口,代码编译期正常。部署到外置Tomcat 8.5.85 下,在控制台上报错:

16-Jan-2023 16:11:07.756 严重 [localhost-startStop-1] org.apache.catalina.core.StandardContext.filterStart 启动过滤器异常[cookieFilter]
	java.lang.AbstractMethodError
		at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:281)
		at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:109)
		at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4604)
		...省略其他输出...

日志截图如下:

除了初始化错误还有销毁错误,错误类型与以下的错误类型一致:

16-Jan-2023 16:11:07.876 严重 [localhost-startStop-1] org.apache.catalina.core.ApplicationFilterConfig.release 失败的销毁过滤器类型为[xx.CookieFilter]名称为[CookieFilter]
	java.lang.AbstractMethodError
		at org.apache.catalina.core.ApplicationFilterConfig.release(ApplicationFilterConfig.java:312)
		at org.apache.catalina.core.StandardContext.filterStop(StandardContext.java:4638)
		...省略其他输出...

日志截图如下:

我的代码差不多长这样:

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class CookieFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
		//做一些处理...
        filterChain.doFilter(request, response);
    }

}

反复分析

  • 首先要知道这个错误(java.lang.AbstractMethodError)是什么?
    • 也就是说父子两个类编译时机不同,先编译父类,在编译子类前对父子类方法定义做了不兼容的修改,编译子类通过后,让子类与先编译的父类一起运行,导致运行期实例化子类时找不到父类定义抽象方法的实现,从而抛出AbstractMethodError错误。
  • 根据Filter的生命周期,初始化init,运行期doFilter,销毁destroy,我们可以推测出是CookieFilter没有实现init方法与destroy方法
  • 这里问题就来了:这里的父(接口)是javax.servlet.Filter,实现的CookieFilter只是一个Filter实现类,我们并没有办法修改Filter接口,按理说编译期就应该报错才对嘛,为啥能编译成功呢?
    • 原因就在于在SpringBoot上使用Filter接口需要引用javax.servlet:javax.servlet-api依赖包,这个包里定义的Filter接口的init()与destroy()是有默认实现的,代码如图:
    • 也就是说,我们编译期不重写init()与destroy()其实是可以的;
  • 但是运行在外置Tomcat8.5.x上的时候,这两个方法却要求必须实现了么?
    • 有两种可能,一是Tomcat 8.5.x的共享库中有Filter定义,与我们需要的Filter不同,没有默认实现init与destroy方法;另一个可能是我们打的包中有两个包包含javax.servlet.Filter,运行期JVM加载顺序不一致就会引出不同的问题!
  • 根据上边的猜测一,我找到了Tomcat 8.5.85的Filter源码http://github.com/apache/tomcat/blob/8.5.85/java/javax/servlet/Filter.java,我们发现在第67行init方法的确没有default关键字修饰,destroy方法也是这样的,第一个猜测是成立的
  • 根据猜测二,我在程序War包中找到了两个servlet-api包:javax.servlet-api与servlet-api,前者版本较后者新。问题分析到这里就可以做解决方案了。

解决方案

  • 方案一:不管新旧servlet-api包,所有Filter都添加默认init与destroy方法
  • 方案二:升级外置Tomcat版本到9.x,原因是9.x的Tomcat的共享库Filter有默认实现init与destroy方法
  • 方案三:构建排除较新的javax.servlet-api包,继续使用Tomcat 8.5.x,同样地所有Filter都要添加init与destroy方法,空的方法也可以。
  • 方案四:使用内嵌Tomcat9.x部署,构建排除旧版servlet-api包
  • 方案五:构建排除javax.servlet-api包与旧版servlet-api包,代码改造添加init与destroy方法,以共享库定义Filter为主
  • 方案六:不大推荐。构建排除旧版servlet-api包,仍部署在Tomcat 8.5.x,有可能会加载到共享库里的Filter

这几种方案中对于研发层面最简单避免这个问题的就是方案一,这里的解决方案是抛砖引玉,欢迎大家评论给出更优解。我是Hellxz,下次博客见。

本文转载于网络 如有侵权请联系删除

相关文章

  • 用 Ansible 自动化系统管理员的 5 个日常任务

    如果你讨厌执行重复性的任务,那么我有一个提议给你,去学习Ansible!Ansible是一个工具,它可以帮助你更轻松、更快速地完成日常任务,这样你就可以更有效地利用时间,比如学习重要的新技术。对于系统管理员来说,它是一个很好的工具,因为它可以帮助你实现标准化,并在日常活动中进行协作,包括:安装、配置和调配服务器和应用程序;定期更新和升级系统;监测、减轻和排除问题。通常,许多这些基本的日常任务都需要手动步骤,而根据个人的技能的不同,可能会造成不一致并导致配置发生漂移。这在小规模的实施中可能是可以接受的,因为你管理一台服务器,并且知道自己在做什么。但当你管理数百或数千台服务器时会发生什么?如果不小心,这些手动的、可重复的任务可能会因为人为的错误而造成延误和问题,而这些错误可能会影响你及你的组织的声誉。这就是自动化的价值所在。而Ansible是自动化这些可重复的日常任务的完美工具。自动化的一些原因是:你想要一个一致和稳定的环境。你想要促进标准化。你希望减少停机时间,减少严重事故案例,以便可以享受生活。你想喝杯啤酒,而不是排除故障问题!本文提供了一些系统管理员可以使用Ansible自动化的日常

  • Android之自定义View的死亡三部曲之Layout

    作者博客http://www.cherylgood.cn 前言大家好!本次我们将继续学习Android之自定义View的死亡三部曲中的第二部:排兵布阵 我们在上一篇「Android之自定义View的死亡三部曲之Measure」中分析了死亡三部曲的第一部,也是三部中最复杂的一步:View的测量,想知道View的测量相关知识可以点进去查看哦!通过第一部View的测量,我们就能拿到View的三围数据了(View的宽高)。那么接下来我们要做的当然就是对测量好的View进行布局了。开始布局Ok,说干就干,这次,我们同样是从ViewRootImpl的performTraversals方法开始,还记得我们的performTraversals方法体内部都有哪些内容么?我们再粘贴一下代码吧。我们上次分析测量是以performMeasure为入口进行分析的,那么本次分析到布局,当然是从performLayout作为起点了。Ok,那么我们就直接看performLayout方法体内部的源码吧我们可以看到,在1处,直接调用了host.layout进行布局,而host是什么东东呢?其实host就是我们的Decor

  • Echarts饼状图交互数据

    前面写到一个关于Echarts饼状图交互数据的例子,但是当时name是写死的,现在的value和name都是需要从后端获取,然后渲染在界面,大致的json数据是这样的: 模拟一个json格式的数据:{"list":[{"value":"管理","name":40}, {"value":"炼钢工","name":30}, {"value":"焊工","name":36}, {"value":"操作工","name":31}]}复制开始写ajax+json数据模拟一个demo,仅供参考,代码如下:<!DOCTYPEhtml> <html> <head> <metacharset="utf-8"> <title>饼状图数据交互</title> &

  • JVM相关

    Stringinter()方法https://blog.csdn.net/soonfly/article/details/70147205finalize方法finalize是Object的protect方法,可以完成一次救赎,注意finalize系统只可以调用一次intval; publicstaticTestFinalizetestFinalize=null; publicTestFinalize(intval){ this.val=val; } @Override protectedvoidfinalize()throwsThrowable{ super.finalize(); System.out.println("finalizemethodexecuting..."); testFinalize=this; } publicstaticvoidmain(String[]args)throwsInterruptedException{ testFinalize=newTestFinalize(1000); testFinalize=null; Sys

  • 说说前端未来几年的发展方向

    在知乎上看到这么一个问题,觉得很有意思,以下是原提问者的见解过去五年前端的发展过程基本上是一个工程化的过程,框架和工程化工具层出不穷。 近两年其实发展已经比较迟滞了。 框架方面:基本就是三大框架鼎立的局面,三大框架都在相互借鉴吸收,而且方向各有侧重,未来短时间内我看格局不可能有什么大变化. 工程化工具:基本上是webpack一统江湖的趋势,虽然有parcel等来小打小闹,但是生态一旦形成,没有革命性的项目是无法取代webpack的,而且webpack也在进化. 个人认为前面五年是前端生产力提高的五年,工程化使得前端的生产力得到了极大提升,但是现在也基本上是在已有的格局中修修补补了我谈谈我对前端未来几年的发展方向的看法。看未来的发展方向,无非就是看现在的解决方案所存在的痛点。1.浏览器的性能问题做web前端的同学都知道,和原生的App相比,性能一直一个致命的痛点,如果要追求性能,肯定得用原生App。那么在性能上,未来几年可能是一个方向。①前端代码编译为字节码浏览器这几年在Chrome的带动下,性能飞速发展,但毕竟其核心原理没有变化,性能始终难以达到原生App的水平,这部分是很有可能出现大

  • Python实现倒计时按钮

    很多网站会在注册或交易后给出一个许可协议或提醒信息,一般页面上会有个按钮,但是这个按钮是倒计时的,在倒计时结束之前是禁用的。本文使用tkinter模拟了这样的倒计时按钮。importtkinter importtime importthreading#创建应用程序窗口,设置标题和大小 root=tkinter.Tk() root.title('倒计时按钮') root['width']=400 root['height']=300#创建Text组件,放置一些文字 richText=tkinter.Text(root,width=380) richText.place(x=10,y=10,width=380,height=230) richText.insert('0.0','假设阅读这些文字需要10秒钟时间')#创建倒计时按钮组件 btnTime=tkinter.Button(root,text='',width=200) btnTime.place(x=80,y=2

  • 机器学习中训练和验证指标曲线图能告诉我们什么?

    点击上方“DeephubImba”,关注公众号,好文章不错过! 我们在训练和验证模型时都会将训练指标保存成起来制作成图表,这样可以在结束后进行查看和分析,但是你真的了解这些指标的图表的含义吗?在本文中将对训练和验证可能产生的情况进行总结并介绍这些图表到底能为我们提供什么样的信息。让我们从一些简单的代码开始,以下代码建立了一个基本的训练流程框架。fromsklearn.model_selectionimporttrain_test_split fromsklearn.datasetsimportmake_classification importtorch fromtorch.utils.dataimportDataset,DataLoader importtorch.optimastorch_optim importtorch.nnasnn importtorch.nn.functionalasF importnumpyasnp importmatplotlib.pyplotaspltclassMyCustomDataset(Dataset): def__init__(self,X,Y

  • 腾讯云云数据库SQLServer修改只读组详情api接口

    1.接口描述接口请求域名:sqlserver.tencentcloudapi.com。 本接口(ModifyReadOnlyGroupDetails)用于修改只读组详情。 默认接口请求频率限制:20次/秒。 APIExplorer提供了在线调用、签名验证、SDK代码生成和快速检索接口等能力。您可查看每次调用的请求内容和返回结果以及自动生成SDK调用示例。 2.输入参数以下请求参数列表仅列出了接口请求参数和部分公共参数,完整公共参数列表见公共请求参数。 参数名称 必选 类型 描述 Action 是 String 公共参数,本接口取值:ModifyReadOnlyGroupDetails。 Version 是 String 公共参数,本接口取值:2018-03-28。 Region 是 String 公共参数,详见产品支持的地域列表。 InstanceId 是 String 主实例ID,格式如:mssql-3l3fgqn7 ReadOnlyGroupId 是 String 只读组ID ReadOnlyGroupName 否 String

  • python安装以及切换/pip相关问题

    python安装   第一步:访问官网,找到windos下载页面       Python2的安装   Python3的安装   下载之后的文件是       统一添加环境变量【这里将py2与py3的地址安装到同一层级下】 C:\Python27\;C:\Python27\Scripts;C:\Python38\;C:\Python38\Scripts\     验证python      验证pip pip-v/pip3-v     python2-mpip-V / Python3-mpip-V     关于pip使用时的问题   常用命令 1.查看已安装过的拓展包:piplist 2.卸载安装好的包:pipuninstall包名 pip的版本问题   关于pip相关问题 1.pip报错:ERROR:CouldnotinstallpackagesduetoanEnv

  • 浅析HttpSession

    苏格拉底曰:我唯一知道的,就是自己一无所知 源头 最近在翻阅SpringbootSecurity板块中的会话管理器过滤器SessionManagementFilter源码的时候,发现其会对单用户的多会话进行校验控制,比如其下的某个策略ConcurrentSessionControlAuthenticationStrategy,节选部分代码 publicvoidonAuthentication(Authenticationauthentication, HttpServletRequestrequest,HttpServletResponseresponse){ //获取单用户的多会话 finalList<SessionInformation>sessions=sessionRegistry.getAllSessions( authentication.getPrincipal(),false); //一系列判断 intsessionCount=sessions.size(); intallowedSessions=getMaxim

  • curl_multi 多并发访问 封装

    /** *curl_multi_do并发 *通过curl请求一个url,data为k=v&k1=v1的格式(数组时自动整理)或post一个xml数据 *$parameters[$url,$data,$method,$param]对应curl_do **/ functioncurl_multi_do($parameters=[]){ $result=[]; $ch_arr=[]; $mh=curl_multi_init(); //如果使用证书则在$cert参数传入包含证书cert.pem和私钥key.pem的文件夹路径 foreach($parametersas$parameter){ $url=$parameter['url']; $data=$parameter['data']; $method=$parameter['method']; $param=$parameter['param']; if(!isset($param['cookie'])){$param['cookie']=false;} if(!isset($param['header'])){$param['

  • Linux c++ 试验-4 一个输出整形类型数值问题

    include<stdlib.h> include<stdio.h> include<time.h> intmain() { time_tnSeconds; chara[256]; time(&nSeconds); //chara='1'; //printf("%ld",nSeconds); sprintf(a,"%ld\n",nSeconds); printf(a); sprintf(a,"%lld\n",nSeconds); printf(a); return(0); } 在arm32linux20.04上,gccv4.9版本,输出的两个值一个对一个错。在另外一个系统编译器上编译后运行正常,使用时要小心。 本博客是个人工作中记录,遇到问题可以互相探讨,没有遇到的问题可能没有时间去特意研究,勿扰。 另外建了几个QQ技术群: 2、全栈技术群:616945527,加群口令abc123 2、硬件嵌入式开发:75764412 3、Go语言交流群:9924600 闲置域名www.nsxz.com出售(等宽等高字符四字域名)。

  • 正则表达式规则以及在oracle函数中的用法

    正则表达式元数据 ^    匹配开头位置 $    匹配结尾位置 *     匹配*前面的字符0次/一次或者多次出现 例如^he*llo$可以匹配hllo;hello;heello;heeello... +    匹配+前面的字符一次或者多次出现 例如^he*llo$可以匹配hello;heello;heeello ?   匹配?前面的字符0次/1次出现  例如^he*llo$可以匹配hllo;hello {n}   匹配一个字符串n次例如^hel{2}o$匹配的就是hello {n,m}匹配一个字符串至少n次,至多m次。其中n和m都是整数。例如^he{1,3}llo$匹配的只有:hello;heello;heeello .     匹配除了null之外的任何单个字符串

  • [题解]数学学科竞赛

    \[\color{red}{\text{校长者,真神人也,左马桶,右永神,会执利笔破邪炁,何人当之?}}\\ \begin{array}{|} \hline \color{pink}{\text{Theprincipalisreallyagod}}\\ \color{pink}{\text{withaclosestoolontheleftandYongshenontheright}}\\ \color{pink}{\text{holdingasharppentopiercethetruth}}\\ \color{pink}{\text{Whocanresisthim?}}\\ \hline \end{array}\\ \begin{array}{|} \hline \color{green}{\text{校長は本当に神であり、左側にトイレ、右側にヨンシェンがあり}}\\ \color{green}{\text{鋭いペンを持って真実を突き刺している。誰が彼に抵抗できるだろうか?}}\\ \hline \end{array}\\ \begin{array}{|} \hline \color

  • 二分图的最大匹配、完美匹配和匈牙利算法

    原文: http://www.renfei.org/blog/bipartite-matching.html 二分图的最大匹配、完美匹配和匈牙利算法   这篇文章讲无权二分图(unweightedbipartitegraph)的最大匹配(maximummatching)和完美匹配(perfectmatching),以及用于求解匹配的匈牙利算法(HungarianAlgorithm);不讲带权二分图的最佳匹配。 二分图:简单来说,如果图中点可以被分为两组,并且使得所有边都跨越组的边界,则这就是一个二分图。准确地说:把一个图的顶点划分为两个不相交集$U$和$V$,使得每一条边都分别连接$U$、$V$中的顶点。如果存在这样的划分,则此图为一个二分图。二分图的一个等价定义是:不含有「含奇数条边的环」的图。图1是一个二分图。为了清晰,我们以后都把它画成图2的形式。 匹配:在图论中,一个「匹配」(matching)是一个边的集合,其中任意两条边都没有公共顶点。例如,图3、图4中红色的边就是图2的匹配。       

  • 数据结构的入门

    前言 这里采用一种比较有逻辑性的学习方法--思维导图式学习 其实画这个的软件也找了好久,后来觉得百度脑图很赞 主要是分享学习思路,供大家一起交流学习 学习线路 学习目标 学习路线 概述 递归 数组和链表 栈和队列 树 常用排序 以下为非郝斌数据结构课程内容(完善ing) 图 字符串 哈希表 贴上百度脑图桌面版下载地址 https://wwa.lanzous.com/iJj7uits9cb 我这学的是非常浅的,学完只能入门(可能才刚半只脚进门),还有很多很多东西需要学咯~ 你好我叫郑某人,期待你的关注!

  • 最长上升子序列

    1、O(n^2)复杂度 dp转移方程: dp[i]=max(dp[i],dp[j]+1)(j<i&&dp[i]>dp[j]) 代码: #include<iostream> usingnamespacestd; inti,j,n,a[100],b[100],max; intmain() { cin>>n; for(i=0;i<n;i++)cin>>a[i]; b[0]=1;//初始化,以a[0]结尾的最长递增子序列长度为1 for(i=1;i<n;i++) { b[i]=1;//b[i]最小值为1 for(j=0;j<i;j++) if(a[i]>a[j])b[i]=max(b[i],b[j]+1); } for(max=i=0;i<n;i++)if(b[i]>max)max=b[i]; cout<<max<<endl; }复制   2、O(n*logn)复杂度 每次取栈顶元素和读到的元素做比较,如果大于栈顶元素,则将它入栈;如果小于,则二分查找栈中的

  • SQL Server like多个值

    SELECTa.*FROMaINNERJOINConvertStrsToTable('设计评,点火,掉落',',')bONCHARINDEX(b.[Name],a.[Comment])>0 ConvertStrsToTable为自己写的function,将字符串分割为表 ------------------------------------------------------------------------------------ select *fromtablex wheref1like'%[你好|你|我们]%' 这种[]通配符,SQLServer里面都是按单一字符来匹配的,不是按字符串,所以会有问题

  • 【HALF】CSP-S2 2022 游记 - Dawn Eve?

    先这样,周末回家传一下相册。 Day-2 周三。折腾了好几天,考场从深圳换到广州最后换到东莞。疫情爆炸... 只是希望自己最后两场比赛不会受到影响。 下午是高二体锻课,结果我们得去做核酸。四点多跑去抢了个场,然后拖着yxt就走。 清净的三医院。 出来的时候买了7个山东杂粮煎饼。大家走在回学校的山路上谈笑着,yxt举报这个饼真的好辣。 走着风好大,回到机房饼有点凉了。TwT,还真的很辣呢。 吃完跑上去打了会球。明明停课前打算不参加排球赛的,结果还是跟着打了。但是太菜了 晚上是crq继续讲字符串。 Day-1 “再写两道DP就去背Tarjan。” yl煮了竹芯水,放了冰糖。很好喝 yxt和我贴贴!! 回家的时候yxt在车上事故了,还好她晚饭吃得很香。 睡得挺香。我记得以前都会失眠。 Day0 你马!!!!!怎么又是五点多醒了懒床懒到六点多。复刻去年。 早饭后开始弹琴,惯例旅行前的数曲。 太阳很瞎眼睛。 在群里看xgf虚空上车。今年头一回没有坐动车,没法竞赛生涯车票collection全收集了。 评价是不晕车,但是头疼。这次好像往返全程没人晕车(可能只是没有严重)。 头疼。 头疼。 中停后绷

  • layui 点击 radio 获取后面的值

      插入代码  functionqueryGifts(){ $.ajax({ url:"../Gift/getGiftListALL.do", async:false, type:"POST", dataType:"JSON", success:function(data){ $("#gifts").empty(); for(vari=0;i<data.length;i++){ vargifthtml="<divclass='giftin'>" gifthtml+="<imgsrc='../toupiao/"+data[i].giftImg+"'/>" gifthtml+="<inputclass='gcount'type='radio'name='giftId'value='"+data[i].giftID+"'/><span>"+data[i].giftVoteCount+"</span></div>" $("#gifts").append(gifthtml); } } }) }复制

  • 如何隐藏UITableView中的一项

    我最近工作中的一个iOSApp中经常有在不同的场合,隐现菜单列表里某一项的需求.如果初始化的时候就去掉某一项的话,有可能让序号变化,处理上会比较麻烦容易出错.我采用了初始化列表相同但是隐藏section的方式,保持序号不变,方便处理.那么如何隐藏一个section呢?其实很简单,就是将section的高度设置为0重载heightForRowAtIndexPath方法,假设要隐藏section1的话,-(CGFloat)tableView:(UITableView*)tableViewheightForRowAtIndexPath:(NSIndexPath*)indexPath{if(indexPath.section==1)return0;elsereturn60;}简单的列表这样就可以了.如果你的菜单项带有header和footer,也需要将他们隐藏,同理重载heightForHeaderInSection和heightForFooterInSection方法 -(CGFloat)tableView:(UITableView*)tableViewheightForHeaderInS

相关推荐

推荐阅读