0
点赞
收藏
分享

微信扫一扫

(四)SpringWeb中的filter体系简介

我们已经搭建好了前后端的基本模块,不过我们需要一个权限控制的功能。这也是一个web项目最基本的功能。后端权限控制框架通常有两种,Shiro和SpringSecurity,我在这里采用了SpringSecurity。而前端路由的权限控制就使用iview-admin提供的默认的就行。

先来简单介绍下SpringSecurity:

SpringSecurity是一个认证和授权的框架。服务端一个完整的权限控制应该分为认证和授权两部门。认证就是登陆的过程,授权则是在登陆成功之后根据某些标志赋予用户访问某些URL的权限。

认证:Authentication
授权:Authorization

无论是是认证还是授权,SpringSecurity都给我们提供了良好的支持。不过,在我们了解SpringSecurity之前最好先来了解一下SpirngWeb中的filter体系,因为SpringSecurity和Web项目的结合是依靠filter来进行的。

Filter作为JavaWeb四大组件之一,在web编程中可以说是黑科技,利用filter可以完成很多了不起的功能。SpirngWeb也有着自己的Filter体系。

(四)SpringWeb中的filter体系简介_生命周期

从上图中可以看到,在spring-web的jar包中web目录下的filter子目录中,存在着许多的filter,这些filter为SpirngWeb的强大功能提供了不少的支撑。

在目录中我们可以看到爱一个名为GenericFilterBean的抽象类,这个类继承了Filter接口,并且还继承了一堆与Spring生命周期相关的接口,这个抽象类可以看做SpirngWeb中Filter的基石,我们来看下它的类图结构:

(四)SpringWeb中的filter体系简介_web容器_02

从类图中可以看到GenericFilterBean主要继承了三个Aware接口,DisposableBean接口,Filter接口,InitializingBean接口,EnvironmentCapable接口,下面让我们来分析一下这些接口都带来了什么功能。

一、Aware接口体系

(四)SpringWeb中的filter体系简介_生命周期_03

Aware接口是一个空接口,主要是用来起到标记作用。起到什么标记呢?这是一个好问题。我们在使用Spring框架的时候,一般都是我们来为Spring做配置,让其根据我们的配置帮我们进行依赖注入,从而实现控制反转。但是我们通常配置的类一般是和Spring无关的,不依附于Spring框架而存在。所以我们可以在Spring框架运行之前就指定这些类的一些动作。但是你不可能在Spring框架运行之前就创建出一个Spring上下文类对吧。还有就是我们在配置一个单例Bean的时候可能会显式的为这个Bean指定一个BeanName,但是我们很有可能会想获得一个ProtoType的Bean的BeanName,这个时候你可能会懵逼了,要怎么才能拿到多例Bean的BeanName呢?等等诸如此类的问题还存在很多,Aware就是Spring提供给我们的解决方案。

每个Aware的子接口,都提供了一个set方法,如下所示:

(四)SpringWeb中的filter体系简介_web容器_04

(四)SpringWeb中的filter体系简介_Web_05

(四)SpringWeb中的filter体系简介_web容器_06

(四)SpringWeb中的filter体系简介_Web_07

(四)SpringWeb中的filter体系简介_web容器_08

对于每一种具体的Aware接口,Spring框架都提供了特别的处理逻辑。比如针对于BeanNameAware来说,当Spring创建Bean时发现其继承了BeanNameAware接口则就会将框架给该Bean生成的BeanName调用setBeanName方法传递给这个Bean。同理对于ServletContext,ApplicationContext等同样如此。这样我们就可以很方便的给Bean传递Spring框架相关的对象供我们使用。特别的方便

Aware接口可以说为类提供了感知功能。但通常这些对象在被注入后都是在内部自己使用,不会再进行二次输出。当然也有少部分这种情况,将通过Aware感知到的对象再次对外提供,比如Environment对象:

(四)SpringWeb中的filter体系简介_生命周期_09

Capable后缀的接口就表示自己具有Environment的能力,即对外提供该对象。不过Capable着实有点多余,所以并不像Aware体系结构这么完整和庞大。

二、Bean生命周期相关接口

从类图中我们可以看到GenericFilterBean还继承了InitializingBean和DisposableBean两个接口。分别提供了destroy方法和afterProperties方法。这两个接口和其提供的方法,都是与Bean的生命周期密切相关的。

InitializingBean提供的afterProperties方法是在Spring通过set方法注入Bean属性之后调用的。DisposableBean提供的destroy方法是在Bean销毁前调用的。

三、Filter接口

其他的接口给GenericFilterBean提供了Bean相关的功能扩展,但终究他还是一个Filter,所以还必须要继承Servlet组件中的Filter接口。Filter接口中提供了三个方法:

(四)SpringWeb中的filter体系简介_Web_10

(四)SpringWeb中的filter体系简介_web容器_11

(四)SpringWeb中的filter体系简介_Web_12

在web.xml文件中配置过Filter的同学通常应该知道,可以在web.xml文件中给Filter配置初始化参数。在Web容器解析web.xml文件并且创建Filter实例的时候,会将配置好的参数封装成一个FilterConfig对象并且调用init方法传递给Filter。至于FilterChain和doFilter方法这里就不再赘述了,属于JavaWeb的基础知识。

在上面介绍的三点中,我们可以看出GenericFilterBean 在具有Filter的基本功能的同时又额外具有某些Spring框架的特性,从而在SpirngWeb框架中提供了良好的功能支持。

四、具体使用实例

可能说了这么多你还是不能够了解其到底有什么作用。我们通过Spring提供的实现类来继续深入了解一下SpringWeb的Fitler体系:

(四)SpringWeb中的filter体系简介_生命周期_13

从其继承结构来看可以看出GenericFilterBean具有如此多的子类。那么你可能会有疑问,这些Filter是怎么被Web容器和Bean容器识别到的呢?我们又没在web.xml中去显式的配置这些Filter,甚至我们之前可能都不知道有这些东西的存在。这里就不得不说到一个东西——DelegatingFilterProxy

(四)SpringWeb中的filter体系简介_web容器_14

web.xml中配置

(四)SpringWeb中的filter体系简介_web容器_15

DelegatingFilterProxy是GenericFilterBean的一个实现类。无论是接入第三方框架还是我们自己写的Filter想要集成进SpringWeb项目中,都可以依靠它来实现。在继续往下了解之前我们先要了解几个小知识点:

1、注册在web.xml中的filter会被web容器加载生成相应的实例,但这个实例并不是bean,bean是Spring的概念

2、所有被web容器加载的filter,他们的init()方法都会被web容器调用

3、SpringWeb的上下文是WebApplicationContext,web容器的上下文是ServletContext,SpringWeb会把自己加载到ServletContext中,并且自己也会持有ServletContext的实例

在了解上面几个小的知识点之后,我们继续往下看思路会清晰很多。显而易见的,我们要从init方法入手:

(四)SpringWeb中的filter体系简介_Web_16

中间的一大段代码我们可以不同去关注,主要是一些处理初始参数的代码,只需要关注initFilterBean这个方法就可以。

(四)SpringWeb中的filter体系简介_Web_17

因为我们是使用web.xml来注册的,所以delegate和targetBeanName属性肯定都是默认值null,如图所示代码会走到这一步:

(四)SpringWeb中的filter体系简介_web容器_18

(四)SpringWeb中的filter体系简介_生命周期_19

通过debug,可以看到在我的项目中这个DelegatingFilterProxy的名字为springSecurityFilterChain,并且将这个name赋值给targetBeanName。

另外前面已经介绍过,Web容器和Spring容器会互相持有对方的上下文,所以我们可以在加载完成的Web上下文中拿到Spring的上下文,WebApplicationContext

(四)SpringWeb中的filter体系简介_web容器_20

同样的道理,由于是web容器创建的Filter实例,和Spring相关的属性初始值肯定为null,所以代码直接走到最后

(四)SpringWeb中的filter体系简介_web容器_21

直接从FilterConfig中拿到Web容器上下文ServletContext

(四)SpringWeb中的filter体系简介_生命周期_22

然后就是从ServletContext中拿到WebApplicationContext

(四)SpringWeb中的filter体系简介_Web_23

(四)SpringWeb中的filter体系简介_Web_24

(四)SpringWeb中的filter体系简介_web容器_25

(四)SpringWeb中的filter体系简介_生命周期_26

由上面的debug过程我们可以看到,WebApplicationContext对象是作为attribute存储到ServletContext中的,存储的键为上图所示。在拿到WebApplicationContext对象后,才开展DelegatingFilterProxy真正的工作:

(四)SpringWeb中的filter体系简介_生命周期_27

(四)SpringWeb中的filter体系简介_生命周期_28

在前面的debug中我们已经看到了,targetName就是我们注册的DelegatingProxyFilter的filterName,在我的项目中就是springSecurityFilterChain,所以这一步的作用就是从Spring上下文中拿到一个beanName为springSecurityFilterChain的Filter类型的Bean。

(四)SpringWeb中的filter体系简介_生命周期_29

debug显示我的项目中当前WebApplicationContext的实际类型是XMLWebApplicationContext,所以我们去这个类型看一下getBean的一些细节:

(四)SpringWeb中的filter体系简介_web容器_30

就是直接从BeanFactory中拿出这个Bean。并且通过debug,我们可以看出这个Filter的实际类型为:FilterChainProxy,不过这个类具体有什么作用暂且不说。

(四)SpringWeb中的filter体系简介_Web_31

然后下面有个isTargetFilterLifecycle方法,这个是用来控制Spring上下文中filter的调用filter原生的生命周期函数的。因为由web容器加载的Filter,web容器会管理其生命周期,调用其init,destroy方法,而注册在Spring上下文中的filter的init方法需要我们自己去显式的调用。不过默认情况下,isTargetFilterLifecycle返回的是false,即不需要调用SpringWeb体系结构中Filter的Servlet生命周期函数。原因很简单,最开始的时候已经介绍了,DelegatingFilterProxy作为SpringWeb的Filter体系中的一员,其继承的是GenericFilterBean抽象类,而这个抽象类已经继承了两个Spring生命周期的接口,所以使用Spring提供的Bean生命周期管理就行。

综上所述,init方法调用最终的结果就是将spring上下文中beanName为springSecurityFilterChain且类型为FilterChainProxy的Bean赋值给DelegatingFilterProxydelegate属性。

(四)SpringWeb中的filter体系简介_生命周期_32

不过到这init方法就结束了,那它到底是怎么工作的呢?答案就是doFilter方法:

(四)SpringWeb中的filter体系简介_Web_33

由于我是使用web.xml注册的,所以并没有进行懒加载,而是直接走到了最后如上图所示:

(四)SpringWeb中的filter体系简介_web容器_34

最终发现,剩下的工作都交给了delegate来进行。

总结:

通过debug我们可以看出,SpringWeb中的filter体系和JavaWeb中的Filter并没有本质上的区别,只是多了一些Spring相关的特性。SpringWeb的Filter体系中的DelegatingFilterProxy相当于对外部提供了一个入口或者说是代理,无论是第三方框架还是开发者自己,都可以将自己的Filter创建到Spring的上下文中,又能集成到web容器的filterChain上。让我们在自定义filter功能的同时又能够使用Spring的强大功能,一举两得,可以说是完美诠释了什么叫低耦合高扩展。

DelegatingFilterProxy的具体使用方法在这里也再说一遍好了,以我项目中的DelegatingProxy举例:

(四)SpringWeb中的filter体系简介_web容器_35

filter-name其实就是指定的我们要代理的已经注册在Spring上下文中的一个Filter类型的Bean的BeanName,使用方法就这么简单,下一节我们以FilterChainProxy为例,来继续以debug的形式讲一下被代理的Filter可以做哪些事情。

举报

相关推荐

0 条评论