インターセプタ原則MyBatisの点の周りが、またかなり単純な原則は、私たちのエージェントが動的プロキシ技術をJDKでインターセプタクラスが定義されて達成することである、とMyBatisのインターセプタをチェーンになりますので、このエージェントは、より多く持つことができますので、 、処理されたものを形成します。原則をページングすると、古いSQLを取得するための最初のインターセプターで、その後、限界にMyBatisの処理を継続させるために文を縫い合わせます。
列子のために、あなたは工場に行く準備ができているが、ドアはあなたが彼らが入ることができる前に、あなたは自分の小包用手袋の上に置くので、フットカバーを設定する必要がありますので、いくつかの手順、叔父とAを歩いていなかった、叔父にあなたを停止しましたあなたは、あなたのビジネスの最後の壮大を、あなたとヘッドカバーセットは、ここでは、SQLステートメントにあるので、叔父が遮断され、セットでリードを取る(例えばリミット文を追加するなど)インターセプタを介して自分自身を飾るために続けてみましょう。
ソースコード官の最初の分析後。
ソースコード解析
まず、インターセプタクラスをロード
まず、私たちが設定するために知っておく必要があるすべての、これらのインターセプタをMyBatisの実際には、また、プラグインとして知られ、インターセプタと呼ばれるXMLConfigBuilderクラス、中pluginElementの方法で設定ファイルを読み込みます。
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
Iterator var2 = parent.getChildren().iterator();
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor)this.resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
this.configuration.addInterceptor(interceptorInstance);
}
}
}
順に並べMyBatisの自身のDTDは、ノードのプラグインが必要な
ロードと完了インスタンス化、最終的には一連の呼び出しを通じて一覧InterceptorChainクラスに入れました。我々はブロッカー、インターセプタのインターフェースを継承する必要性を達成するためにしていること。
第二に、プロキシを設定します
このInterceptorChain下pluginAll下の動的プロキシ設定のnewExecutor方法の構成完全なチェーンの完成で最も重要なステップである
、ここでpluginAllメソッドが呼び出されたスイッチオフなしバッファが存在しない場合、対象オブジェクトは、他のエグゼキュータの実装クラスであります彼は例のCachingExecutorクラスでなければなりません。プロキシオブジェクトを生成するinterceptor.pluginターゲットへの各呼び出しは、インターセプタで定義されたメソッドから返されたプロキシプラグインと動的オブジェクトされた後、一般の使用Plugin.wrap()
方法返されます。複数のインターセプタがある場合は、彼が最終的に形のものであってもよい:プロキシオブジェクト(プロキシオブジェクト(プロキシオブジェクト(プロキシエグゼキュータ)))、私の祖父のような父のスナックは、私の父は息子の行くスナック機関、息子を聞かせて他の人々は、カバー層の層をスナックを買うために用事を実行させる、この場所は、ビットの周りのかもしれません。
ちょうど2つのインターセプタ構成した後、構造は、彼が、最も内側の層は、本当の息子であるようなものです。
第三に、カスタム・ロジックの処理
インターセプタを継承した後、あなたが最初のオブジェクト(ここでは、オブジェクトが執行、StatementHandler、PameterHandlerとResultSetHandler可能)というインターセプトを指定する必要があり、コメントの仕方によってMyBatisのに語りました。
以下の例としては、私に言うことは、クラスでのインターセプト法にクエリをMyBatisの過負荷エグゼキュータのパラメータ{MappedStatement.class、Object.classを、RowBounds.class、ResultHandler.class方法
@Intercepts({
@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)
})
MyBatisのは、インターセプタの処理方法を傍受するために、そのクエリに移した後ときに呼び出されます。
处理自定义逻辑在intercept方法下,而intercept的参数很重要,所以要先了解他,参数类型是Invocation,他在动态代理invoke时候被转入,但是还是得先看wrap这个方法,warp自己new了自己一下,target是被代理的对象,也就是Executor的实现类,但是代理有多个时,他可能是代理Executor实现类的代理对象,或者更多层套入,interceptor就是我们的拦截器。
在invoke下调用拦截器的的intercept方法时,new出一个Invocation对象传入,其中args比较重要,他就是调用被代理对象的方法调用时的参数,如Executor的query的方法,args[]就是这个方法的参数列表集合,后续会有大用。
四、分页
首先定义一个PageInfo,其中toString通过(页大小×页数)-页数的公式等会limit语句。
如果从2页取10条最终得出(10*2)-10=“limit 10,10”
public class PageInfo {
private int page;
private int pageCount;
public PageInfo(int page, int pageCount) {
this.page = page;
this.pageCount = pageCount;
}
@Override
public String toString() {
return" limit "+( (pageCount*page)-pageCount)+","+pageCount;
}
}
分页思路大致是这样的,首先拿到DAO层的查询参数,这里就包括PageInfo,接着拿到旧的sql,拼接limit,并把新的sql语句设置给Mybatis让他继续执行,实现拦截。
public interface IUserDao {
List<UserEntity> select(@Param("page")PageInfo pageInfo);
}
首先拿到上面的pageInfo参数,直接调用invocation.getArgs()[1]即可,这里的getArgs()数组就是Executor#query方法的第二个参数,即代表参数数组,但是有可能没有被封装成MapperMethod.ParamMap,所以要判断以下。
Mybatis在ParamNameResolver下的getNamedParams中做了判断,当参数上没有注解,并且参数个数为1时直接返回String类型。否则包装一下,这里无伤大雅,不管他。
1.拿到请求参数
ParamMap也是继承HashMap,首先判断有没有page分页这个条件,有的话先拿到分页的语句
String limitPage = "";
Object argObject = invocation.getArgs()[1];
if (argObject instanceof MapperMethod.ParamMap) {
MapperMethod.ParamMap arg = (MapperMethod.ParamMap) invocation.getArgs()[1];
if (arg.get("page") != null) {
PageInfo pageInfo = (PageInfo) arg.get("page");
limitPage = pageInfo.toString();
}
}
2.拿到旧的sql
MappedStatement对象即封装的insert、select、delete、update的信息,同样在Executor#query参数1即表示它。接着通过getBoundSql获取sql语句,完成旧sql和分页数据的拼接
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
Class<?> type = mappedStatement.getParameterMap().getType();
String oldSql = mappedStatement.getBoundSql(argObject).getSql();
String newSql = oldSql + limitPage;
3.给Mybatis设置新的sql
下面就是设置新的sql语句到MappedStatement,这里知识点就有了,MappedStatement中保存sql语句的是SqlSource对象,但是MappedStatement并没有提供修改他的方法。
所以有三个办法
1:通过反射
2:自己创建一个新的MappedStatement,并替换原来的。
3:SystemMetaObject
其中SystemMetaObject是Mybatis提供的非常强大的一个类,两行代码即可修改内部属性。
知道了办法,那就简单了
//构建新的sql对象
SqlSource sqlSource = new RawSqlSource(mappedStatement.getConfiguration(), newSql, type);
MetaObject metaObject = SystemMetaObject.forObject(mappedStatement);
//替换原来的
metaObject.setValue("sqlSource", sqlSource);
最後に、に戻りinvocation.proceed();
、他のインタセプタがある場合は、MyBatisのプロセスをできるように続けるには、彼が変わっていたSQL文を照会するインターセプタ、後に、呼び出すために続けています。
次のようにページングすべてのコードがあります
@Intercepts({
@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)
})
public class MybatisIntercepts implements Interceptor {
private Properties properties = new Properties();
@Override
public Object intercept(Invocation invocation) throws Throwable {
String limitPage = "";
Object argObject = invocation.getArgs()[1];
if (argObject instanceof MapperMethod.ParamMap) {
MapperMethod.ParamMap arg = (MapperMethod.ParamMap) invocation.getArgs()[1];
if (arg.get("page") != null) {
PageInfo pageInfo = (PageInfo) arg.get("page");
limitPage = pageInfo.toString();
}
}
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
Class<?> type = mappedStatement.getParameterMap().getType();
String oldSql = mappedStatement.getBoundSql(argObject).getSql();
System.out.println(oldSql);
String newSql = oldSql + limitPage;
System.out.println("新Sql语句" + newSql);
SqlSource sqlSource = new RawSqlSource(mappedStatement.getConfiguration(), newSql, type);
MetaObject metaObject = SystemMetaObject.forObject(mappedStatement);
metaObject.setValue("sqlSource", sqlSource);
// Field field = mappedStatement.getClass().getDeclaredField("sqlSource");
// field.setAccessible(true);
// field.set(mappedStatement,sqlSource);
Object o = invocation.proceed();
return o;
}
@Override
public void setProperties(Properties properties) {
}
@Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
}
この場合には使用しませんでした。ここsetPropertiesメソッドは、属性のセットは、実際には、パラメータは、設定ファイルのプロパティで書かれています
<plugins>
<plugin interceptor="com.hxl.intercepts.MybatisIntercepts">
<property name="test" value="666"/>
</plugin>
</plugins>
テスト
public static void main( String[] args )
{
String resource = "mybatis-config.xml";
try {
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession2 = build.openSession();
List<UserEntity> select = sqlSession2.getMapper(IUserDao.class).select(new PageInfo(2, 10));
System.out.println(select);
} catch (IOException e) {
e.printStackTrace();
}
}
終わり…