- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章SpringBoot实现过滤器、拦截器与切片的实现和区别由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
q:使用过滤器、拦截器与切片实现每个请求耗时的统计,并比较三者的区别与联系 。
过滤器filter 。
过滤器概念 。
filter是j2e中来的,可以看做是 servlet 的一种“加强版”,它主要用于对用户请求进行预处理和后处理,拥有一个典型的 处理链 。filter也可以对用户请求生成响应,这一点与servlet相同,但实际上很少会使用filter向用户请求生成响应。使用filter完整的流程是:filter对用户请求进行预处理,接着将请求交给servlet进行预处理并生成响应,最后filter再对服务器响应进行后处理.
过滤器作用 。
在javadoc中给出了几种过滤器的作用 。
1
2
3
4
5
6
7
8
9
10
|
* examples that have been identified
for
this
design are<br>
*
1
) authentication filters, 即用户访问权限过滤
*
2
) logging and auditing filters, 日志过滤,可以记录特殊用户的特殊请求的记录等
*
3
) image conversion filters
*
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>
|
对于第一条,即使用filter作权限过滤,其可以这么实现:定义一个filter,获取每个客户端发起的请求url,与当前用户无权限访问的url列表(可以是从db中取出)作对比,起到权限过滤的作用.
过滤器实现方式 。
自定义的过滤器都必须实现 javax.servlet.filter 接口,并重写接口中定义的三个方法:
1、void init(filterconfig config) 用于完成filter的初始化.
2、void destory() 用于filter销毁前,完成某些资源的回收.
3、void dofilter(servletrequest request,servletresponse response,filterchain chain) 实现过滤功能,即对每个请求及响应增加的额外的预处理和后处理。,执行该方法之前,即对用户请求进行预处理;执行该方法之后,即对服务器响应进行后处理。值得注意的是, chain.dofilter() 方法执行之前为预处理阶段,该方法执行结束即代表用户的请求已经得到控制器处理。因此,如果再 dofilter 中忘记调用 chain.dofilter() 方法,则用户的请求将得不到处理.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
import
org.slf4j.logger;
import
org.slf4j.loggerfactory;
import
org.springframework.stereotype.component;
import
javax.servlet.*;
import
javax.servlet.http.httpservletrequest;
import
java.io.ioexception;
// 必须添加注解,springmvc通过web.xml配置
@component
public
class
timefilter
implements
filter {
private
static
final
logger log = loggerfactory.getlogger(timefilter.
class
);
@override
public
void
init(filterconfig filterconfig)
throws
servletexception {
log.info(
"初始化过滤器:{}"
, filterconfig.getfiltername());
}
@override
public
void
dofilter(servletrequest request, servletresponse response, filterchain chain)
throws
ioexception, servletexception {
log.info(
"start to dofilter"
);
long
starttime = system.currenttimemillis();
chain.dofilter(request, response);
long
endtime = system.currenttimemillis();
log.info(
"the request of {} consumes {}ms."
, geturlfrom(request), (endtime - starttime));
log.info(
"end to dofilter"
);
}
@override
public
void
destroy() {
log.info(
"销毁过滤器"
);
}
private
string geturlfrom(servletrequest servletrequest){
if
(servletrequest
instanceof
httpservletrequest){
return
((httpservletrequest) servletrequest).getrequesturl().tostring();
}
return
""
;
}
}
|
从代码中可看出,类 filter 是在 javax.servlet.* 中,因此可以看出,过滤器的一个很大的局限性在于, 其不能够知道当前用户的请求是被哪个控制器(controller)处理的 ,因为后者是spring框架中定义的.
在springboot中注册第三方过滤器 。
对于springmvc,可以通过在 web.xml 中注册过滤器。但在springboot中不存在 web.xml ,此时如果引用的某个jar包中的过滤器,且这个过滤器在实现时没有使用 @component 标识为spring bean,则这个过滤器将不会生效。此时需要通过java代码去注册这个过滤器。以上面定义的 timefilter 为例,当去掉类注解 @component 时,注册方式为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@configuration
public
class
webconfig {
/**
* 注册第三方过滤器
* 功能与spring mvc中通过配置web.xml相同
* @return
*/
@bean
public
filterregistrationbean thirdfilter(){
thirdpartfilter thirdpartfilter =
new
thirdpartfilter();
filterregistrationbean filterregistrationbean =
new
filterregistrationbean() ;
filterregistrationbean.setfilter(thirdpartfilter);
list<string > urls =
new
arraylist<>();
// 匹配所有请求路径
urls.add(
"/*"
);
filterregistrationbean.seturlpatterns(urls);
return
filterregistrationbean;
}
}
|
相比使用 @component 注解,这种配置方式有个优点,即可以自由配置拦截的url.
拦截器interceptor 。
拦截器概念 。
拦截器,在aop(aspect-oriented programming)中用于在某个方法或字段被访问之前,进行拦截,然后在之前或之后加入某些操作。拦截是aop的一种实现策略.
拦截器作用 。
拦截器实现 。
通过实现 handlerinterceptor 接口,并重写该接口的三个方法来实现拦截器的自定义
1、prehandler(httpservletrequest request, httpservletresponse response, object handler) 方法将在 请求处理之前 进行调用。springmvc中的 interceptor 同filter一样都是 链式调用 。每个interceptor的调用会依据它的声明顺序依次执行,而且最先执行的都是interceptor中的prehandle方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值boolean 类型的,当它返回为false时,表示请求结束,后续的interceptor和controller都不会再执行;当返回值为true时就会继续调用下一个interceptor 的prehandle 方法,如果已经是最后一个interceptor 的时候就会是调用当前请求的controller 方法.
2、 posthandler(httpservletrequest request, httpservletresponse response, object handler, modelandview modelandview) 在当前 请求进行处理之后 ,也就是controller 方法调用之后执行,但是它会在dispatcherservlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对controller 处理之后的modelandview 对象进行操作.
3、aftercompletion(httpservletrequest request, httpservletresponse response, object handle, exception ex) 该方法也是需要当前对应的interceptor的prehandle方法的返回值为true时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在dispatcherservlet 渲染了对应的视图之后执行 。这个方法的主要作用是用于进行资源清理工作的.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
@component
public
class
timeinterceptor
implements
handlerinterceptor {
private
static
final
logger log = loggerfactory.getlogger(timeinterceptor.
class
);
@override
public
boolean
prehandle(httpservletrequest request, httpservletresponse response, object handler)
throws
exception {
log.info(
"在请求处理之前进行调用(controller方法调用之前)"
);
request.setattribute(
"starttime"
, system.currenttimemillis());
handlermethod handlermethod = (handlermethod) handler;
log.info(
"controller object is {}"
, handlermethod.getbean().getclass().getname());
log.info(
"controller method is {}"
, handlermethod.getmethod());
// 需要返回true,否则请求不会被控制器处理
return
true
;
}
@override
public
void
posthandle(httpservletrequest request, httpservletresponse response, object handler, modelandview modelandview)
throws
exception {
log.info(
"请求处理之后进行调用,但是在视图被渲染之前(controller方法调用之后),如果异常发生,则该方法不会被调用"
);
}
@override
public
void
aftercompletion(httpservletrequest request, httpservletresponse response, object handler, exception ex)
throws
exception {
log.info(
"在整个请求结束之后被调用,也就是在dispatcherservlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)"
);
long
starttime = (
long
) request.getattribute(
"starttime"
);
log.info(
"time consume is {}"
, system.currenttimemillis() - starttime);
}
|
与过滤器不同的是,拦截器使用 @component 修饰后,还需要通过实现 webmvcconfigurer 手动注册:
1
2
3
4
5
6
7
8
9
10
11
|
// java配置类
@configuration
public
class
webconfig
implements
webmvcconfigurer {
@autowired
private
timeinterceptor timeinterceptor;
@override
public
void
addinterceptors(interceptorregistry registry){
registry.addinterceptor(timeinterceptor);
}
}
|
切片aspect 。
切片概述 。
相比过滤器,拦截器能够知道用户发出的请求最终被哪个控制器处理,但是拦截器还有一个明显的不足,即不能够获取request的参数以及控制器处理之后的response。所以就有了切片的用武之地了.
切片实现 。
切片的实现需要注意 @aspect , @component 以及 @around 这三个注解的使用,详细查看官方文档:传送门 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@aspect
@component
public
class
timeaspect {
private
static
final
logger log = loggerfactory.getlogger(timeaspect.
class
);
@around
(
"execution(* me.ifight.controller.*.*(..))"
)
public
object handlecontrollermethod(proceedingjoinpoint proceedingjoinpoint)
throws
throwable{
log.info(
"切片开始。。。"
);
long
starttime = system.currenttimemillis();
// 获取请求入参
object[] args = proceedingjoinpoint.getargs();
arrays.stream(args).foreach(arg -> log.info(
"arg is {}"
, arg));
// 获取相应
object response = proceedingjoinpoint.proceed();
long
endtime = system.currenttimemillis();
log.info(
"请求:{}, 耗时{}ms"
, proceedingjoinpoint.getsignature(), (endtime - starttime));
log.info(
"切片结束。。。"
);
return
null
;
}
}
|
过滤器、拦截器以及切片的调用顺序 。
如下图,展示了三者的调用顺序filter->intercepto->aspect->controller。相反的是,当controller抛出的异常的处理顺序则是从内到外的。因此我们总是定义一个注解 @controlleradvice 去统一处理控制器抛出的异常。如果一旦异常被 @controlleradvice 处理了,则调用拦截器的 aftercompletion 方法的参数 exception ex 就为空了.
实际执行的调用栈也说明了这一点:
而对于过滤器和拦截器详细的调用顺序如下图:
过滤器和拦截器的区别 。
最后有必要再说说过滤器和拦截器二者之间的区别:
。
filter | interceptor | |
---|---|---|
实现方式 | 过滤器是基于函数回调 | 基于java的反射机制的 |
规范 | servlet规范 | spring规范 |
作用范围 | 对几乎所有的请求起作用 | 只对action请求起作用 |
。
除此之外,相比过滤器,拦截器能够“看到”用户的请求具体是被spring框架的哪个控制器所处理.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我.
原文链接:https://juejin.im/post/5c6901206fb9a049af6dcdcf 。
最后此篇关于SpringBoot实现过滤器、拦截器与切片的实现和区别的文章就讲到这里了,如果你想了解更多关于SpringBoot实现过滤器、拦截器与切片的实现和区别的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我有一个对象数组,我想在键传入“filter”过滤器时提取值。下面是我尝试过的 Controller 代码片段,但我得到的响应类型未定义。请帮我找出哪里出错了。 var states = [{"HI
如果任何 J2EE 应用程序直接访问 servlet,然后 servlet 将相同的请求转发到某个 .jsp 页面。 request.getRequestDispatcher("Login.jsp")
我有一个带有图像缩略图的表单,可以通过复选框进行选择以进行下载。我想要一个包含 jQuery 中图像的数组,用于 Ajax 调用。 2个问题: - 表格顶部有一个复选框,用于切换我想要从映射中排除的所
我必须从服务器转储数据库,将 .sql 传输到另一台服务器,然后运行以下脚本以使用此语法删除某些行: DELETE wp_posts FROM wp_posts INNER JOIN wp_postm
我想从目录中过滤掉特定类型的文件,但收到错误“ token 语法错误,删除这些 token ”: File dir = new File("c:/etc/etc"); File[] f
几乎所有的 Web 应用程序都依赖外部的输入。这些数据通常来自用户或其他应用程序(比如 web 服务)。通过使用过滤器,您能够确保应用程序获得正确的输入类型。 您应该始终对外部数据进行过滤! 输
我正在开发一个由 OData 服务提供支持的搜索功能。它将返回一个或一列标题对象作为结果。我们需要搜索的许多字段不在标题对象中。它们仅在子对象(导航属性)中。能够针对子字段执行 OData 搜索并仍然
假设我有以下模型,它有一个方法 variants(): class Example(models.Model): text = models.CharField(max_length=255)
我有一个默认的列表列表,但我基本上想这样做: myDefaultDict = filter(lambda k: len(k)>1, myDefaultDict) 除了它似乎只适用于列表。我能做什么?
我正在使用 django-filter 来输出我的模型的过滤结果。那里没有问题。下一步是添加一个分页器……尽管现在已经苦苦挣扎了好几天。 views.py: def funds_overview(re
我正在做一个概念证明,我正在试验一种奇怪的行为。 我有一个按日期字段按范围分区的表,如果我设置固定日期或由 SYSDATE 创建的日期,查询的成本会发生很大变化。 这些是解释计划: SQL> SELE
如果一个或另一个值匹配,是否可以制作一个过滤器,例如一个中性的 PropertyFilter(并传递给链中的下一个过滤器)?就像是: value1 val
我是 VBA 初学者,正在尝试根据单元格值过滤数据,经过一番谷歌搜索后,我编写了一个有效的代码 Sub FilterDepartment_Sales() Sheet6.Activate
假设我在 excel 数据透视表中有两个过滤器。 两者最初都会显示筛选列的选定范围内的所有值。 当我仅在过滤器 1 中选择几个值时,过滤器 2 仍会继续显示基础数据中所选范围内特定过滤器列中的所有值。
是否可以定义自定义 build-ins (名称不再适合)在 ftl? 由于语义前提,我不想让它成为一个函数,而是一个内置的。 最佳答案 这是不可能的,?语法是为内置函数保留的。 (顺便说一句,这意味着
我试图在 Edit | 之外添加一个链接通过插件删除wordpress管理员>用户>所有用户列表中的链接..这是我第一次尝试通过查看其他插件或搜索google来制作wordpress插件.. 我添加了
我正在尝试按照以下教程使用 django 过滤器进行分页,但该教程似乎缺少某些内容,而且我无法使用基于函数的 View 方法显示分页。 https://simpleisbetterthancomple
由于我是 Powershell 新手,因此寻求最佳实践方面的帮助, 我有一个 csv 文件,我想过滤掉 csv 中的每一行,除了包含“未安装”的行 然后,我想根据包含计算机列表的单独 csv 文件过滤
我正在尝试创建一个搜索查询,它会告诉我我作为审阅者添加到其中的打开更改,但我还没有提交最新补丁集的代码审查。这应该包括其他人已经评论过的更改,但我没有。 我能找到的最接近的是 is:reviewer
在我的 Web 应用程序中,我有 3 个主要部分 1. 客户 2. 供应商 3. 管理员 我正在使用 java session 过滤器来检查用户 session 并允许访问网站的特定部分。 因此客户只
我是一名优秀的程序员,十分优秀!