- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Executor拦截器高级教程QueryInterceptor的规范由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
executor 拦截器高级教程 - queryinterceptor 规范 。
这篇文档涉及下面几个方面 。
1. executor query 方法介绍 。
在 mybatis 的拦截器的文档部分,我们知道 executor 中的 query 方法可以被拦截,如果你真正写过这个方法的拦截器,你可能会知道在 executor 中的 query 方法有两个:
1
2
3
4
5
6
7
8
9
10
11
12
|
<e> list<e> query(
mappedstatement ms,
object parameter,
rowbounds rowbounds,
resulthandler resulthandler,
cachekey cachekey,
boundsql boundsql)
throws
sqlexception;
<e> list<e> query(
mappedstatement ms,
object parameter,
rowbounds rowbounds,
resulthandler resulthandler)
throws
sqlexception;
|
这两个方法的区别是第一个方法多两个参数 cachekey 和 boundsql,在多数情况下,我们用拦截器的目的就是针对 sql 做处理,如果能够拦截第一个方法,可以直接得到 boundsql 对象,就会很容易的得到执行的 sql,也可以对 sql 做处理.
虽然想的很好,但是 mybatis 提供的 exctutor 实现中,参数多的这个 query 方法都是被少的这个 query 方法在内部进行调用的.
在cachingexecutor中:
1
2
3
4
5
6
7
8
9
|
public
<e> list<e> query(
mappedstatement ms,
object parameter,
rowbounds rowbounds,
resulthandler resulthandler)
throws
sqlexception {
boundsql boundsql = ms.getboundsql(parameterobject);
cachekey key = createcachekey(ms, parameterobject, rowbounds, boundsql);
return
query(ms, parameterobject, rowbounds, resulthandler, key, boundsql);
}
|
在baseexecutor中:
1
2
3
4
5
6
7
8
9
|
public
<e> list<e> query(
mappedstatement ms,
object parameter,
rowbounds rowbounds,
resulthandler resulthandler)
throws
sqlexception {
boundsql boundsql = ms.getboundsql(parameter);
cachekey key = createcachekey(ms, parameter, rowbounds, boundsql);
return
query(ms, parameter, rowbounds, resulthandler, key, boundsql);
}
|
上面这两个方法一样。由于第一个 query 方法在这里是内部调用,并且我们所有的拦截器都是层层代理的cachingexecutor或基于baseexecutor的实现类,所以我们能拦截的就是参数少的这个方法.
分页插件开始从executor拦截开始就一直是拦截的参数少的这个方法。但是从5.0 版本开始,query 的这两个方法都可以被拦截了。在讲这个原理之前,我们先了解一下拦截器的执行顺序.
2. 拦截器配置和调用顺序 。
拦截器的调用顺序分为两大种,第一种是拦截的不同对象,例如拦截 executor 和 拦截 statementhandler 就属于不同的拦截对象,这两类的拦截器在整体执行的逻辑上是不同的,在 executor 中的 query 方法执行过程中,会调用下面的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public
<e> list<e> doquery(
mappedstatement ms,
object parameter,
rowbounds rowbounds,
resulthandler resulthandler,
boundsql boundsql)
throws
sqlexception {
statement stmt =
null
;
try
{
configuration configuration = ms.getconfiguration();
statementhandler handler = configuration.newstatementhandler(wrapper, ms, parameter, rowbounds, resulthandler, boundsql);
stmt = preparestatement(handler, ms.getstatementlog());
return
handler.<e>query(stmt, resulthandler);
}
finally
{
closestatement(stmt);
}
}
|
在这段代码中,才会轮到 statementhandler 去执行,statementhandler 属于 executor 执行过程中的一个子过程。所以这两种不同类别的插件在配置时,一定是先执行 executor 的拦截器,然后才会轮到 statementhandler。所以这种情况下配置拦截器的顺序就不重要了,在 mybatis 逻辑上就已经控制了先后顺序.
第二种拦截器的顺序就是指拦截同一种对象的同一个方法,例如都拦截 executor 的 query 方法,这时你配置拦截器的顺序就会对这里有影响了。假设有如下几个拦截器,都是拦截的 executor 的 query 方法.
1
2
3
4
5
|
<plugins>
<plugin interceptor=
"com.github.pagehelper.executorqueryinterceptor1"
/>
<plugin interceptor=
"com.github.pagehelper.executorqueryinterceptor2"
/>
<plugin interceptor=
"com.github.pagehelper.executorqueryinterceptor3"
/>
</plugins>
|
在org.apache.ibatis.session.configuration中有如下方法:
1
2
3
|
public
void
addinterceptor(interceptor interceptor) {
interceptorchain.addinterceptor(interceptor);
}
|
mybatis 会按照拦截器配置的顺序依次添加到interceptorchain中,其内部就是list<interceptor> interceptors。再看 configuration中创建 executor 的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
executor newexecutor(transaction transaction, executortype executortype) {
executortype = executortype ==
null
? defaultexecutortype : executortype;
executortype = executortype ==
null
? executortype.simple : executortype;
executor executor;
if
(executortype.batch == executortype) {
executor =
new
batchexecutor(
this
, transaction);
}
else
if
(executortype.reuse == executortype) {
executor =
new
reuseexecutor(
this
, transaction);
}
else
{
executor =
new
simpleexecutor(
this
, transaction);
}
if
(cacheenabled) {
executor =
new
cachingexecutor(executor);
}
executor = (executor) interceptorchain.pluginall(executor);
return
executor;
}
|
在调用 interceptorchain.pluginall 之前,executor 就是前一节中的 cachingexecutor 或基于 baseexecutor 的实现类。然后看 interceptorchain.pluginall 方法:
1
2
3
4
5
6
|
public
object pluginall(object target) {
for
(interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return
target;
}
|
前面我们配置拦截器的顺序是1,2,3。在这里也会按照 1,2,3 的顺序被层层代理,代理后的结构如下:
1
2
3
4
5
6
7
|
interceptor3:{
interceptor2: {
interceptor1: {
target: executor
}
}
}
|
从这个结构应该就很容易能看出来,将来执行的时候肯定是按照 3>2>1>executor>1>2>3 的顺序去执行的。可能有些人不知道为什么3>2>1>executor之后会有1>2>3,这是因为使用代理时,调用完代理方法后,还能继续进行其他处理。处理结束后,将代理方法的返回值继续往外返回即可。例如:
1
2
3
4
|
interceptor3 前置处理
object result = interceptor2..query(
4
个参数方法);
interceptor3 后续处理
return
result;
|
对于 interceptor2.invoke 方法也是相同的逻辑:
1
2
3
4
|
interceptor2 前置处理
object result = interceptor1..query(
4
个参数方法);
interceptor2 后续处理
return
result;
|
同理 interceptor1.invoke :
1
2
3
4
|
interceptor1 前置处理
object result = executor.query(
4
个参数方法);
interceptor1 后续处理
return
result;
|
叠加到一起后,如下:
1
2
3
4
5
6
7
8
|
interceptor3 前置处理
interceptor2 前置处理
interceptor1 前置处理
object result = executor.query(
4
个参数方法);
interceptor1 后续处理
interceptor2 后续处理
interceptor3 后续处理
return
result;
|
所以这个顺序就是 3>2>1>executor>1>2>3.
在你弄清楚这个逻辑后,再继续往下看,因为后面的技巧会颠覆这个逻辑,所以才会有后面的规范以及如何配置不同的插件.
3. 拦截 query 方法的技巧 。
上一节的内容中,对拦截器的用法是最常见的一种用法,所以才会出现这种都能理解的执行顺序。但是分页插件 5.0 不是这样,这个插件颠覆了这种顺序,这种颠覆其实也很普通,这也是本节要说的技巧.
在我写作 mybatis 技术书籍的过程中(还没写完,已经因为分页插件占用了几周的写作时间),我就在考虑为什么不能拦截第一个query(6个参数的)方法,如果能拦截这个方法,就可以直接拿到 boundsql,然后处理 sql 就很容易实现其他的操作.
在第1 节介绍为什么第一个query方法不能被拦截时,是因为下面这段代码:
1
2
3
4
5
6
7
8
9
|
public
<e> list<e> query(
mappedstatement ms,
object parameter,
rowbounds rowbounds,
resulthandler resulthandler)
throws
sqlexception {
boundsql boundsql = ms.getboundsql(parameter);
cachekey key = createcachekey(ms, parameter, rowbounds, boundsql);
return
query(ms, parameter, rowbounds, resulthandler, key, boundsql);
}
|
既然cachingexecutor或基于baseexecutor的实现类只是这么简单的调用两个方法得到了boundsql 和cachekey,我们为什么不直接替代他们呢?
所以我们可以有类似下面的拦截器用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@intercepts
(
@signature
(type = executor.
class
, method =
"query"
, args = {mappedstatement.
class
, object.
class
, rowbounds.
class
, resulthandler.
class
}))
public
class
queryinterceptor
implements
interceptor {
@override
public
object intercept(invocation invocation)
throws
throwable {
object[] args = invocation.getargs();
mappedstatement ms = (mappedstatement) args[
0
];
object parameterobject = args[
1
];
rowbounds rowbounds = (rowbounds) args[
2
];
resulthandler resulthandler = (resulthandler) args[
3
];
executor executor = (executor) invocation.gettarget();
boundsql boundsql = ms.getboundsql(parameterobject);
//可以对参数做各种处理
cachekey cachekey = executor.createcachekey(ms, parameterobject, rowbounds, boundsql);
return
executor.query(ms, parameterobject, rowbounds, resulthandler, cachekey, boundsql);
}
@override
public
object plugin(object target) {
return
plugin.wrap(target,
this
);
}
@override
public
void
setproperties(properties properties) {
}
}
|
这个拦截器直接替代了原有 executor 的部分逻辑,直接去调用了 6 个参数的方法,因而导致 4 个参数的后续方法被跳过了。但是由于这里的 executor 是代理对象,所以 6 个参数的 query 方法可以被代理了,这就扰乱了上一节中的执行顺序.
在上一节拦截器的例子中,做简单修改,将 executorqueryinterceptor2 换成上面的 queryinterceptor,配置如下:
1
2
3
4
5
|
<plugins>
<plugin interceptor=
"com.github.pagehelper.executorqueryinterceptor1"
/>
<plugin interceptor=
"com.github.pagehelper.queryinterceptor"
/>
<plugin interceptor=
"com.github.pagehelper.executorqueryinterceptor3"
/>
</plugins>
|
代理后的结构如下:
1
2
3
4
5
6
7
|
interceptor3:{
queryinterceptor: {
interceptor1: {
target: executor
}
}
}
|
这时,调用顺序就变了,interceptor3 执行顺序如下:
1
2
3
4
|
interceptor3 前置处理
object result = queryinterceptor.query(
4
个参数方法);
interceptor3 后续处理
return
result;
|
queryinterceptor.invoke 执行逻辑如下:
1
2
3
4
|
interceptor2 前置处理
object result = executor.query(
6
个参数方法);
interceptor2 后续处理
return
result;
|
在 queryinterceptor 中,没有继续执行 4个参数方法,而是执行了 6 个参数方法。但是 interceptor1 拦截的 4 个参数的方法,所以 interceptor1 就被跳过去了,整体的执行逻辑就变成下面这样了:
1
2
3
4
5
6
|
interceptor3 前置处理
interceptor2 前置处理
object result = executor.query(
6
个参数方法);
interceptor2 后续处理
interceptor3 后续处理
return
result;
|
如果 interceptor1 拦截的是 6 个参数的方法,因为 queryinterceptor 获取的是 interceptor1 代理的 executor 对象,那么 interceptor1 就会被 queryinterceptor 继续执行下去.
分页插件就是类似 queryinterceptor 的执行逻辑,所以当你使用 5.0 版本之后的插件时,如果你还需要配置其他 executor 的 query 插件,你就会遇到一些问题(可以解决,继续往下看).
如果你是自己开发的插件,那么你按照下一节的规范去开发也不会遇到问题。如果你使用的其他人提供的插件,按照第 5 节的配置顺序也能解决问题.
4. 拦截 query 方法的规范 。
queryinterceptor 的逻辑就是进去的是 4 个参数的方法,出去的是 6 个参数的方法。这种处理方法不仅仅不方便和一般的 excutor 拦截器搭配使用,当出现两个以上类似 queryinterceptor 的插件时,由于接口变了,类似 queryinterceptor 插件也无法连贯的执行下去。因而有必要解决这个问题。解决的办法就是使用统一的规范。经过规范后 queryinterceptor 如下:
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
|
@intercepts
(
{
@signature
(type = executor.
class
, method =
"query"
, args = {mappedstatement.
class
, object.
class
, rowbounds.
class
, resulthandler.
class
}),
@signature
(type = executor.
class
, method =
"query"
, args = {mappedstatement.
class
, object.
class
, rowbounds.
class
, resulthandler.
class
, cachekey.
class
, boundsql.
class
}),
}
)
public
class
queryinterceptor
implements
interceptor {
@override
public
object intercept(invocation invocation)
throws
throwable {
object[] args = invocation.getargs();
mappedstatement ms = (mappedstatement) args[
0
];
object parameterobject = args[
1
];
rowbounds rowbounds = (rowbounds) args[
2
];
resulthandler resulthandler = (resulthandler) args[
3
];
executor executor = (executor) invocation.gettarget();
cachekey cachekey;
boundsql boundsql;
//由于逻辑关系,只会进入一次
if
(args.length ==
4
){
//4 个参数时
boundsql = ms.getboundsql(parameterobject);
cachekey = executor.createcachekey(ms, parameterobject, rowbounds, boundsql);
}
else
{
//6 个参数时
cachekey = (cachekey) args[
4
];
boundsql = (boundsql) args[
5
];
}
//todo 自己要进行的各种处理
//注:下面的方法可以根据自己的逻辑调用多次,在分页插件中,count 和 page 各调用了一次
return
executor.query(ms, parameterobject, rowbounds, resulthandler, cachekey, boundsql);
}
@override
public
object plugin(object target) {
return
plugin.wrap(target,
this
);
}
@override
public
void
setproperties(properties properties) {
}
}
|
注意两个变化,第一个就是拦截器签名同时拦截了 4 个 和 6 个参数的方法,这样不管那个插件在前在后都会被执行.
第二个变化就是这段代码:
1
2
3
4
5
6
7
8
9
10
11
12
|
cachekey cachekey;
boundsql boundsql;
//由于逻辑关系,只会进入一次
if
(args.length ==
4
){
//4 个参数时
boundsql = ms.getboundsql(parameterobject);
cachekey = executor.createcachekey(ms, parameterobject, rowbounds, boundsql);
}
else
{
//6 个参数时
cachekey = (cachekey) args[
4
];
boundsql = (boundsql) args[
5
];
}
|
如果这个插件配置的靠后,是通过 4 个参数方法进来的,我们就获取这两个对象。如果这个插件配置的靠前,已经被别的拦截器处理成 6 个参数的方法了,那么我们直接从 args 中取出这两个参数直接使用即可。取出这两个参数就保证了当其他拦截器对这两个参数做过处理时,这两个参数在这里会继续生效.
假设有个排序插件和分页插件,排序插件将 boundsql 修改为带排序的 sql 后,sql 会继续交给分页插件使用。分页插件的分页 sql 执行时,会保留排序去执行,这样的规范就保证了两个插件都能正常的执行下去.
所以如果大家想要使用这种方式去实现拦截器,建议大家遵守这个规范.
这个规范对于已经存在的插件来说就没法控制了,但是仍然可以通过配置顺序来解决.
5. 如何配置不同的 executor 插件 。
当引入类似 queryinterceptor 插件时,由于扰乱了原有的插件执行方式,当配置 executor 顺序不对时会导致插件无法生效.
第 4 节中的例子:
1
2
3
4
5
|
<plugins>
<plugin interceptor=
"com.github.pagehelper.executorqueryinterceptor1"
/>
<plugin interceptor=
"com.github.pagehelper.queryinterceptor"
/>
<plugin interceptor=
"com.github.pagehelper.executorqueryinterceptor3"
/>
</plugins>
|
首先执行顺序为 3>query>1>executor,由于 query 是 4 或 6 个参数进来,6 个参数出去。所以在 query 前面执行的拦截器必须是 4 个的(query 规范拦截器先后都能执行,需要根据逻辑配置先后)参数的,在 query 后面执行的拦截器必须是 6 个参数的.
这个顺序对应到配置顺序时,也就是 4 个参数的配置在 queryinterceptor 拦截器的下面,6 个参数的配置在 queryinterceptor 拦截器的上面。按照这个顺序进行配置时,就能保证拦截器都执行.
如果你想获得如分页插件(queryinterceptor 规范)执行的 sql,你就得按照 queryinterceptor 规范去实现,否则只能配置在分页插件的下面,也就只能获得分页处理前的 sql.
总结 。
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我的支持。如果你想了解更多相关内容请查看下面相关链接 。
原文链接:https://blog.csdn.net/isea533/article/details/53957330 。
最后此篇关于Executor拦截器高级教程QueryInterceptor的规范的文章就讲到这里了,如果你想了解更多关于Executor拦截器高级教程QueryInterceptor的规范的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
Issue 2019/05/09 21:50:07.380 +0800 ERROR [ExecutorManager] [Azkaban] No active executors found
我的问题是:使用 Executors.newFixedThreadPool(1)?? 有意义吗? 。在两个线程(main + oneAnotherThread)场景下使用执行器服务是否高效?正在通过调
我想知道,Executors.newSingleThreadExecutor() 之间有什么区别?和 Executors.newFixedThreadPool(1) 以下摘自javadoc Unlik
我的问题是:使用 Executors.newFixedThreadPool(1) 有意义吗??。在两个线程(main + oneAnotherThread)场景中使用执行器服务是否有效?通过调用 ne
我有一个 Apache Spark 应用程序在集群模式下运行在 YARN 集群上(spark 在这个集群上有 3 个节点)。 当应用程序运行时,Spark-UI 显示 2 个执行程序(每个运行在不同的
我想知道是否有任何理由使用 Executor 而不是 ExecutorService。 据我所知,JDK 中没有实现 Executor 接口(interface),它也不是 ExecutorServi
我有多个使用 Celery Executor 的 dag,但我希望使用 Kubernetes Executor 运行一个特定的 dag。我无法推断出一种良好而可靠的方法来实现这一目标。 我有一个 ai
假设我们的 Controller 中有一个 Action 。在每次请求时,许多用户都会调用 performLogin。 def performLogin( ) = { Async {
创建和管理您自己的 ExecutorService 与使用 Spring Boot 的 @Async 方法和 @Bean 方法创建 Executor 添加一个@Bean来创建一个Executor 手动
问题从无到有,只有我在代码中所做的更改 - 安装了 RaSharper(但删除它并重新安装 Visual Studio 没有帮助)。 所以我使用 NUnit 3 来运行测试。 我有 Visual St
我们知道每个任务当时都在一个核心中执行。 假设我们有这样配置的节点集群: 10 节点。 每个节点 16 个核心。 每个节点 64 GB 内存。 我的问题是 有 1 个 16 核的执行程序和 16 个
我正在从 Jupyter Notebook 中初始化 PySpark,如下所示: from pyspark import SparkContext # conf = SparkConf().setAp
我正在向我的 Web 应用程序添加一个基于 Flask 的 API,以控制某些网络自动化功能的启动和停止。我遇到了一个奇怪的行为,即 Flask-Executor .submit() 方法调用的函数似
单元测试在本地运行良好。 在 Visual Studio 2017 托管生成代理上运行时,VSTest 任务失败并显示: 2018-12-08T10:42:16.3779907Z An excepti
我正在尝试制作一个执行器和线程的简单示例。 当我调用 newSingleThreadExecutor(new CustomThreadFactory) 时,一切顺利,但是当我使用 null 参数调用
对于一个线程,我通过以下代码段捕获未捕获的异常。但是,对于 ExecutorService executor = Executors.newFixedThreadPool(10);,如何捕获未捕获的异
我想创建一个 CompletableFuture,其返回值在 Kotlin 中的特定执行程序上运行。 下面的代码工作得很好。 return CompletableFuture.supplyAsync
考虑基本的固定线程池: Executors.newFixedThreadPool(MaxListeners) 我打算不断提交新任务 - 响应传入的 TCP 套接字服务请求。 然而,当每个任务中的Run
我们可以在定义 ThreadPoolExecutors 时提供 BlockingQueue 实现。但是,如果我使用工厂(执行器)创建单个线程池(如下所示),我想知道使用哪个阻塞队列。我猜它是一个 Li
我编写了一个程序来执行两个在 shell 前台运行的命令,直到在终端上按下 ^c。 外壳命令 ./weed master -mdir=/var/lib/qualebs/weed 上面命令的输出是 qu
我是一名优秀的程序员,十分优秀!