Mybatis中的攔截器
先看一下mybatis攔截器的用法和用途,先用為ibatis3提供基於方言(Dialect)的分頁查詢的例子來看一下吧!原始碼:
@Intercepts({@Signature(
type= Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class OffsetLimitInterceptor implements Interceptor{
private static int MAPPED_STATEMENT_INDEX = 0;
private static int PARAMETER_INDEX = 1;
private static int ROWBOUNDS_INDEX = 2;
private Dialect dialect;
public Object intercept(Invocation invocation) throws Throwable {
processIntercept(invocation.getArgs());
return invocation.proceed();
}
private void processIntercept(final Object[] queryArgs) {
//queryArgs = query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)
MappedStatement ms = (MappedStatement)queryArgs[MAPPED_STATEMENT_INDEX];
Object parameter = queryArgs[PARAMETER_INDEX];
final RowBounds rowBounds = (RowBounds)queryArgs[ROWBOUNDS_INDEX];
int offset = rowBounds.getOffset();
int limit = rowBounds.getLimit();
if(dialect.supportsLimit() && (offset != RowBounds.NO_ROW_OFFSET || limit != RowBounds.NO_ROW_LIMIT)) {
BoundSql boundSql = ms.getBoundSql(parameter);
String sql = boundSql.getSql().trim();
if (dialect.supportsLimitOffset()) {
sql = dialect.getLimitString(sql, offset, limit);
offset = RowBounds.NO_ROW_OFFSET;
} else {
sql = dialect.getLimitString(sql, 0, limit);
}
limit = RowBounds.NO_ROW_LIMIT;
queryArgs[ROWBOUNDS_INDEX] = new RowBounds(offset,limit);
BoundSql newBoundSql = new BoundSql(ms.getConfiguration(),sql, boundSql.getParameterMappings(), boundSql.getParameterObject());
MappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql));
queryArgs[MAPPED_STATEMENT_INDEX] = newMs;
}
}
//see: MapperBuilderAssistant
private MappedStatement copyFromMappedStatement(MappedStatement ms,SqlSource newSqlSource) {
Builder builder = new MappedStatement.Builder(ms.getConfiguration(),ms.getId(),newSqlSource,ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.keyGenerator(ms.getKeyGenerator());
builder.keyProperty(ms.getKeyProperty());
//setStatementTimeout()
builder.timeout(ms.getTimeout());
//setStatementResultMap()
builder.parameterMap(ms.getParameterMap());
//setStatementResultMap()
builder.resultMaps(ms.getResultMaps());
builder.resultSetType(ms.getResultSetType());
//setStatementCache()
builder.cache(ms.getCache());
builder.flushCacheRequired(ms.isFlushCacheRequired());
builder.useCache(ms.isUseCache());
return builder.build();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
String dialectClass = properties.getProperty("dialectClass");
try {
dialect = (Dialect)Class.forName(dialectClass).newInstance();
} catch (Exception e) {
throw new IllegalArgumentException(
"cannot create dialect instance by dialectClass:"
+ dialectClass, e);
}
}
public static class BoundSqlSqlSource implements SqlSource {
private BoundSql boundSql;
public BoundSqlSqlSource(BoundSql boundSql) {
this.boundSql = boundSql;
}
public BoundSql getBoundSql(Object parameterObject) {
return boundSql;
}
}
}
註解Intercepts表示該類被用作攔截器,@Signature(type= Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})用來表示攔截那個類得那個方法,攔截器可用於Executor,ParameterHandler,ResultSetHandler和StatementHandler這些類上。
我們看看這個攔截器是怎麼攔截Executor的qurey方法的。先看生成Executor物件的過程:
我們從這個序列圖中可以清晰的看出,我們最後得到的Executor是一個代理物件。返回的這個Executor的代理物件是將Plugin作為呼叫處理器的,我們看一下Plugin的invoke方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
這個方法中需要提的就是signatureMap,我們看看這個signatureMap是怎麼取得的,我們看到是從wrap方法中通過getSignatureMap(interceptor)得到的,getSignatureMap方法原始碼:
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
Signature[] sigs = interceptor.getClass().getAnnotation(Intercepts.class).value();
Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
for (Signature sig : sigs) {
Set<Method> methods = signatureMap.get(sig.type());
if (methods == null) {
methods = new HashSet<Method>();
signatureMap.put(sig.type(), methods);
}
try {
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
}
}
return signatureMap;
}
這個方法就是解析各個迭代器的標註,得到一個以被攔截的物件為key,以被攔截的方法集為值得value的Map。從上面這些原始碼我們可以瞭解,mybatis是將plugin已經做方法呼叫處理器,將目標物件一層層的做代理,在執行的時候判斷方法是否在signatureMap中註冊(也就是@Signature( type= Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))標註了沒有),如果註冊了就執行攔截器中的intercept方法。
@Intercepts({@Signature(
type= Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class OffsetLimitInterceptor implements Interceptor{
private static int MAPPED_STATEMENT_INDEX = 0;
private static int PARAMETER_INDEX = 1;
private static int ROWBOUNDS_INDEX = 2;
private Dialect dialect;
public Object intercept(Invocation invocation) throws Throwable {
processIntercept(invocation.getArgs());
return invocation.proceed();
}
private void processIntercept(final Object[] queryArgs) {
//queryArgs = query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)
MappedStatement ms = (MappedStatement)queryArgs[MAPPED_STATEMENT_INDEX];
Object parameter = queryArgs[PARAMETER_INDEX];
final RowBounds rowBounds = (RowBounds)queryArgs[ROWBOUNDS_INDEX];
int offset = rowBounds.getOffset();
int limit = rowBounds.getLimit();
if(dialect.supportsLimit() && (offset != RowBounds.NO_ROW_OFFSET || limit != RowBounds.NO_ROW_LIMIT)) {
BoundSql boundSql = ms.getBoundSql(parameter);
String sql = boundSql.getSql().trim();
if (dialect.supportsLimitOffset()) {
sql = dialect.getLimitString(sql, offset, limit);
offset = RowBounds.NO_ROW_OFFSET;
} else {
sql = dialect.getLimitString(sql, 0, limit);
}
limit = RowBounds.NO_ROW_LIMIT;
queryArgs[ROWBOUNDS_INDEX] = new RowBounds(offset,limit);
BoundSql newBoundSql = new BoundSql(ms.getConfiguration(),sql, boundSql.getParameterMappings(), boundSql.getParameterObject());
MappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql));
queryArgs[MAPPED_STATEMENT_INDEX] = newMs;
}
}
//see: MapperBuilderAssistant
private MappedStatement copyFromMappedStatement(MappedStatement ms,SqlSource newSqlSource) {
Builder builder = new MappedStatement.Builder(ms.getConfiguration(),ms.getId(),newSqlSource,ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.keyGenerator(ms.getKeyGenerator());
builder.keyProperty(ms.getKeyProperty());
//setStatementTimeout()
builder.timeout(ms.getTimeout());
//setStatementResultMap()
builder.parameterMap(ms.getParameterMap());
//setStatementResultMap()
builder.resultMaps(ms.getResultMaps());
builder.resultSetType(ms.getResultSetType());
//setStatementCache()
builder.cache(ms.getCache());
builder.flushCacheRequired(ms.isFlushCacheRequired());
builder.useCache(ms.isUseCache());
return builder.build();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
String dialectClass = properties.getProperty("dialectClass");
try {
dialect = (Dialect)Class.forName(dialectClass).newInstance();
} catch (Exception e) {
throw new IllegalArgumentException(
"cannot create dialect instance by dialectClass:"
+ dialectClass, e);
}
}
public static class BoundSqlSqlSource implements SqlSource {
private BoundSql boundSql;
public BoundSqlSqlSource(BoundSql boundSql) {
this.boundSql = boundSql;
}
public BoundSql getBoundSql(Object parameterObject) {
return boundSql;
}
}
}
註解Intercepts表示該類被用作攔截器,@Signature(type= Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})用來表示攔截那個類得那個方法,攔截器可用於Executor,ParameterHandler,ResultSetHandler和StatementHandler這些類上。
我們看看這個攔截器是怎麼攔截Executor的qurey方法的。先看生成Executor物件的過程:
我們從這個序列圖中可以清晰的看出,我們最後得到的Executor是一個代理物件。返回的這個Executor的代理物件是將Plugin作為呼叫處理器的,我們看一下Plugin的invoke方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
這個方法中需要提的就是signatureMap,我們看看這個signatureMap是怎麼取得的,我們看到是從wrap方法中通過getSignatureMap(interceptor)得到的,getSignatureMap方法原始碼:
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
Signature[] sigs = interceptor.getClass().getAnnotation(Intercepts.class).value();
Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
for (Signature sig : sigs) {
Set<Method> methods = signatureMap.get(sig.type());
if (methods == null) {
methods = new HashSet<Method>();
signatureMap.put(sig.type(), methods);
}
try {
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
}
}
return signatureMap;
}
這個方法就是解析各個迭代器的標註,得到一個以被攔截的物件為key,以被攔截的方法集為值得value的Map。從上面這些原始碼我們可以瞭解,mybatis是將plugin已經做方法呼叫處理器,將目標物件一層層的做代理,在執行的時候判斷方法是否在signatureMap中註冊(也就是@Signature( type= Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))標註了沒有),如果註冊了就執行攔截器中的intercept方法。
相關文章
- Mybatis 攔截器MyBatis
- MyBatis攔截器MyBatis
- Mybatis Interceptor 攔截器MyBatis
- mybatis註冊攔截器MyBatis
- Spring MVC 中的攔截器的使用“攔截器基本配置” 和 “攔截器高階配置”SpringMVC
- SpringMVC中的攔截器SpringMVC
- grpc中的攔截器RPC
- “犯罪心理”解讀 Mybatis 攔截器MyBatis
- Mybatis 分頁:Pagehelper + 攔截器實現MyBatis
- "犯罪心理"解讀Mybatis攔截器MyBatis
- SpringBoot攔截器中獲取註解、攔截器中注入ServiceSpring Boot
- Spring Boot中攔截器的使用Spring Boot
- SpringMVC攔截器,設定不攔截的URLSpringMVC
- spring中的過濾器與攔截器Spring過濾器
- SpringMVC中的攔截器Interceptor實現SpringMVC
- MyBatis攔截器優雅實現資料脫敏MyBatis
- SpringMVC攔截器SpringMVC
- axios 攔截器iOS
- spring攔截器Spring
- axios攔截器iOS
- sql攔截器SQL
- 使用MyBatis攔截器後,摸魚時間又長了。?MyBatis
- SpringBoot中的過濾器和攔截器的實現Spring Boot過濾器
- spring boot 攔截器Spring Boot
- SpringMVC-攔截器SpringMVC
- gRPC(3):攔截器RPC
- 【SpringMVC】 4.3 攔截器SpringMVC
- spring mvc 攔截器的使用SpringMVC
- webwork的攔截器真是好用Web
- Java實現的攔截器Java
- 攔截器的使用問題
- Flume內建攔截器與自定義攔截器(程式碼實戰)
- Spring Boot新增攔截器Spring Boot
- SpringBoot 手寫攔截器Spring Boot
- HandlerInterceptor - 自定義攔截器
- 攔截過濾器模式過濾器模式
- 16、threadlocal+攔截器thread
- SpringMVC攔截器的使用場景SpringMVC
- 如何增強grpc的攔截器RPC