更新时间:2022-08-10 12:36:09 来源:极悦 浏览981次
在很多Java Web项目中,我们会在web.xml中配置一些过滤器来拦截请求,比如下面的编码过滤器来解决乱码:
<过滤器>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<初始化参数>
<param-name>编码</param-name>
<param-value>UTF-8</param-value>
</init-param>
<初始化参数>
<param-name>forceEncoding</param-name>
<param-value>真</param-value>
</init-param>
</过滤器>
<过滤器映射>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
谁调用了这些过滤器?谁组织了他们并确保了他们的执行顺序?过滤器演示如下:
@WebFilter(filterName="FilterDemo", urlPatterns={"/**"})
@Order(1)//当有多个过滤器时,指定过滤器的顺序
公共类 SecurityFilter 实现 Filter{
private static final Logger LOGGER = LoggerFactory.getLogger(FilterDemo.class);
@Override
公共无效销毁(){
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
抛出 IOException,ServletException {
//TODO 做一些过滤动作;
链.doFilter(请求,响应);
}
@Override
公共无效初始化(FilterConfig arg0)抛出 ServletException {
//TODO 自动生成的方法存根 }
}
本以为这样写后过滤器会起作用,但是请求进来后,发现过滤器没有执行。后来想到 Spring 的工作是由 beanFactry 中注册的每个 bean 完成的,所以可能需要在 beanFactory 中注册这个过滤器,像这样:
@Configuration//表示这是一个Spring配置文件
@EnableSwagger2//swagger是一个restful接口文档在线自动生成+功能测试功能框架
@RefreshScope//允许依赖配置的bean在Spring cloud config配置文件更改后无需重启服务即可自动更新
公共类 GlobalBeanConfig {
//将过滤器添加到Spring配置中,否则只会写过滤器类,不添加配置不起作用
@Bean//使用代码配置xml形式的<bean></bean>
公共过滤器 filterDemo(){
返回新的 FilterDemo();
}
@Bean(name = "loggerInteceptor")
公共 GlobalAspectInteceptor getLoggerInteceptor() {
返回新的 GlobalAspectInteceptor();
}
@豆
公共 ThreadPoolTaskExecutor globalTaskExecutor(
@Value("${global.thread.pool.corePoolSize}") 整数 corePoolSize,
@Value("${global.thread.pool.maxPoolSize}") 整数 maxPoolSize,
@Value("${global.thread.pool.queueCapacity}") 整数 queueCapacity
) {
ThreadPoolTaskExecutor 执行器 = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setThreadGroupName("globalTaskExecutor");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
executor.initialize();
返回执行者;
}
}
添加评论后,它立即生效。既然知道了工具的使用方法,那我们就来拆解一下工具,看看效果如何。通过断点调试发现过滤器中的两个关键类在起作用:ApplicationFilterConfig和ApplicationFilterChain。
这两个类在 org.apache.catalina.core 包下。可以肯定的是,tomcat 容器管理过滤器链。
接下来,我们看一下ApplicationFilterConfig,先上部分源码:
Final 类 public ApplicationFilterConfig实现了 FilterConfig , Serializable {
Private Long Final static serialVersionUID = 1L ;
静态最终StringManager SM = StringManager 。getManager(“org.apache.catalina.core”);
私有静态最终Log log = LogFactory 。getLog (
ApplicationFilterConfig.class ) ; 私有静态最终列表<字符串>空字符串=收藏。空列表();
私有最终瞬态上下文上下文;
私有瞬态过滤器 filter = null ;
私人最终过滤器过滤器过滤器;
私有瞬态InstanceManager instanceManager ;
私有对象名oname ;
ApplicationFilterConfig(Context context , FilterDef filterDef) throws ClassCastException , ClassNotFoundException , IllegalAccessException , InstantiationException ,ServletException 、 InvocationTargetException 、 NamingException 、 IllegalArgumentException 、 NoSuchMethodException 、 SecurityException {
this . 上下文=上下文;
这个。过滤器定义=过滤器定义;
IF (filterDef . getFilter () == null ) {
this . getFilter () ;
}其他{
这个.过滤器=过滤器定义。getFilter () ;
这个。获取实例管理器()。新实例(这个。过滤器);
这个。初始化过滤器();
}
}
}
通过分析可以发现,这个类是用来保存一个过滤器的。构造方法传入的上下文必须是tomcat配置文件下context.xml配置的应用环境。FliterDef是过滤器的描述信息,应该通过在web.xml(或其他地方)中配置过滤器参数来构造。
好了,重点来了——ApplicationFilterChain ,名字就透露了它的作用,就是它组织了分散的过滤器。结合上面我的猜测,它应该有一个数组或列表来保存ApplicationFilterConfig,以及一个过滤器光标来记录当前过滤器去了哪里。源代码(部分)如下:
public final 类ApplicationFilterChain实现FilterChain {
private static final ThreadLocal < ServletRequest > lastServicedRequest ;
private static final ThreadLocal < ServletResponse > lastServicedResponse ;
公共静态最终 int INCREMENT = 10 ;
私有ApplicationFilterConfig[] 过滤器=新ApplicationFilterConfig[ 0 ] ;
私人 int pos = 0 ;
私人 int n = 0 ;
私有Servlet servlet = null ;
私有布尔servletSupportsAsync = false ;
私有静态最终StringManager sm ;
private static final Class <?> [] classType ;
私有静态最终类<?> [] classTypeUsedInService ;
公共应用程序过滤器链(){
}
public void doFilter(ServletRequest request , ServletResponse response) throws IOException , ServletException {
if (Globals . IS_SECURITY_ENABLED) {
final ServletRequest req = request ;
最终的 ServletResponse res = response ;
尝试{
AccessController 。DoPrivileged( new PrivilegedExceptionAction() {
public Void run() throws ServletException ,IO异常{
应用过滤链。这. InternalDoFilter(req , res) ;
返回空值;
} }) ;
}捕捉(PrivilegedActionException var7){
异常 e = var7 。获取异常 () ;
if (e instanceof ServletException) {
throw (ServletException)e ;
}
if (e instanceof IOException) {
throw (IOException)e ;
}
if (e instanceof RuntimeException) {
throw (RuntimeException)e ;
}
throw new ServletException(e .getMessage () , e) ;
}
}其他{
这个. internalDoFilter(请求,响应);
}
}
private void internalDoFilter(ServletRequest request , ServletResponse response) throws IOException , ServletException
{ if ( this.pos < this.n ) {
ApplicationFilterConfig e1 =这个。过滤器[这个。位置++ ] ;
试试{
过滤器 res1 = e1 。获取过滤器();
if (request . IsAsyncSupported() && "false" . EqualsIgnoreCase(e1 . GetFilterDef () . GetAsyncSupported ())) {
请求。setAttribute(“org.apache.catalina.ASYNC_SUPPORTED” ,布尔值。假);
}
如果(全局。IS_SECURITY_ENABLED){
主体 principal1 = ((HttpServletRequest)request) 。获取用户主体();
Object[] args1 = new Object[]{request , response , this } ;
安全实用程序。DoAsPrivilege( "doFilter" , res1 , classType , args1 , principal1) ;
}其他{
水库1 。doFilter(request , response , this ) ;
}
} catch (ServletException | RuntimeException | IOException var15) {
throw var15 ;
}捕捉(可抛出的 var16){
可抛出的res = ExceptionUtils 。UnwrapInvocationTargetException(var16) ;
异常实用程序。HandleThrowable(res) ;
throw new ServletException( sm.GetString ( " filterChain.filter" ) , res) ;
} else {
尝试{
if (ApplicationDispatcher . WRAP_SAME_OBJECT) {
最后服务请求。设置(请求);
最后服务响应。设置(响应);
}
if (request .isAsyncSupported () &&! this .servletSupportsAsync ) {
请求。setAttribute(“org.apache.catalina.ASYNC_SUPPORTED” ,布尔值。假);
}
如果( HttpServletRequest的请求实例&& HttpServletResponse的响应实例&&全局。IS_SECURITY_ENABLED){
主体主体= ((HttpServletRequest)request) 。获取用户主体();
Object[] args = new Object[]{request , response} ;
安全实用程序。DoAsPrivilege( "service "
, this.Servlet , classTypeUsedInService , args , principal ) ; }其他{
这个. 小服务程序。服务(请求,响应);
}
} catch (ServletException | RuntimeException | IOException var17) {
throw var17 ;
}捕捉(Throwable var18){
可抛出的e = ExceptionUtils 。UnwrapInvocationTargetException(var18) ;
异常实用程序。HandleThrowable(e) ;
throw new ServletException( sm.GetString ( " filterChain.servlet" ) , e) ;
}最后{
if (ApplicationDispatcher . WRAP_SAME_OBJECT) {
lastServicedRequest 。设置((对象)空);
最后服务响应。设置((对象)空) ;
}
}
}
}
…… }
果然字段中有一个ApplicationFilterConfig[]用来存放一系列filter,pos用来存放当前filter位置,还有其他字段就不深入了。有兴趣的朋友可以自行探索。
我们来看两个关键方法:doFilter、internalDoFilter
doFilter的最终目的只有一个,调用internalDoFilter,中间可能会加一些安全策略,估计Globals.IS_SECURITY_ENABLE和是否开启https服务有关,具体没有仔细研究。
internalDoFilter的最终目的只有一个,就是调整当前pos指向的filter链中某个filter的doFilter(request, response, this)方法。中间可能会添加一些安全策略,当所有过滤器都被调用时,进行一些收尾工作,包括调用servlet.service(request, response)方法处理真正的请求,以及清除当前存储的请求和响应threadLocal 为下一个请求做准备。
再梳理一下流程:
一个请求进来,先给自己给filterChain;
filterChain 启动过滤器链,从头开始,将请求交给第一个过滤器,并将自身传递给过滤器;
Filter在doFilter中完成自己的过滤逻辑,然后调用filterChain的doFilter开始下一个过滤器;
filterChain 光标移动,启动下一个过滤器,依此类推...
过滤器光标走到链尾,filterChain进行收尾工作;
最后,给出一个简单的流程图:
0基础 0学费 15天面授
Java就业班有基础 直达就业
业余时间 高薪转行
Java在职加薪班工作1~3年,加薪神器
工作3~5年,晋升架构
提交申请后,顾问老师会电话与您沟通安排学习