Mybatis流程学习总结(待续)

来源:互联网 发布:3个人合唱的歌曲 知乎 编辑:程序博客网 时间:2024/05/16 00:43

这几天自己做小项目练手的时候对拦截器如何精准的拦截到selectbypage产生了好奇,进而研究了一下Mybatis的源码,了解了大致的流程。将几个自己的疑问解答了一下。文章最下面画了一幅流程图,图有点丑,凑合看吧。。
部分内容参考http://blog.csdn.net/ABCD898989/article/details/51261163

Question1:哪些类可以被拦截?

函数内部执行interceptorChain.pluginAll的类

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {  .....   //.....   executor = (Executor) interceptorChain.pluginAll(executor);     return executor;   }  

在mybatis中一共有四个:parameterHandler 、resultSetHandler 、statmentHandler
、Executor
InterceptorChain里保存了所有的拦截器,它在mybatis初始化的时候创建。上面这句代码的含义是调用拦截器链里的每个拦截器依次对executor进行plugin,代码如下:

public class InterceptorChain {    private final List<Interceptor> interceptors = new ArrayList<Interceptor>();   public Object pluginAll(Object target) {     for (Interceptor interceptor : interceptors) {         target = interceptor.plugin(target);      }      return target;     }   //...  }  

Question2:为什么分页插件是拦截statment的prepare(Connection connection)方法?
请看下面的代码,如果执行到List localList = handler.query(stmt, resultHandler);
这一步,那就执行了查询,显然要在这一步之前拦截。
那么就选择了拦截stmt = prepareStatement(handler, ms.getStatementLog());

    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(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);         stmt = prepareStatement(handler, ms.getStatementLog());         List localList = handler.query(stmt, resultHandler);        return localList; } finally { closeStatement(stmt); } throw localObject;}

可以执行拦截的类有这几种parameterHandler 、resultSetHandler 、statmentHandler、Executor
所以,正好可以拦截Statement stmt = handler.prepare(connection);
这句话。
再进到prepareStatement里看,这个handler.prepare(connection)。是不是很眼熟,分页拦截器标准开头。method指的就是prepare,args指的是里面的参数类型

   private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException{     Connection connection = getConnection(statementLog);     Statement stmt = handler.prepare(connection);     handler.parameterize(stmt);     return stmt;}
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})

Question3:怎么让拦截器只处理分页的sql语句?
先上段代码,拦截器的标准开头,拦截器首先会拦截所有sql语句,具体判断要依靠代码里自己手动加条件判断

@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})public class PageInterceptor implements Interceptor{    public Object intercept(Invocation arg0) throws Throwable {        StatementHandler statementHandler = (StatementHandler)arg0.getTarget();        MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());        //MetaObject是Mybatis提供的一个的工具类,通过它包装一个对象后可以获取或设置该对象的原本不可访问的属性(比如那些私有属性)        MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");        //你按照这个delegete的参数类型自定义一个处理方法,赋值过去,触发这个delegete的时候,就会执行你添加的方法了        String id = mappedStatement.getId();        if(id.endsWith("ByPage")) {           这里写具体的分页处理        }    }}

就靠id.endsWith(“ByPage”)判断,id我用systemout打印过,是包名+接口名+方法名。就是MyBatis Dao层的接口。下面的代码是我自己小项目的代码:

public interface AdDao {List<Ad> selectByPage(Ad ad);}

Qusetion4:为什么要使用metaObject取值?
去BaseStatementHandler类里面看,发现mappedStatement是protected,所以不能直接用get方法取出来,需要MetaObject的帮助,类似映射。

protected final MappedStatement mappedStatement

Question 5: metaObject.getValue()里面的delegate.XXXX是什么意思,为什么要这么写
这个真不清楚。我猜一下,是通过metaObject.getValue()取值,通过delegate定位使用BaseStatementHandler 类中的参数。

public class RoutingStatementHandler implements StatementHandler {......private final StatementHandler delegate;}
public abstract class BaseStatementHandler implements StatementHandler {    protected final Configuration configuration;    protected final ObjectFactory objectFactory;    protected final TypeHandlerRegistry typeHandlerRegistry;    protected final ResultSetHandler resultSetHandler;    protected final ParameterHandler parameterHandler;    protected final Executor executor;    protected final MappedStatement mappedStatement;    protected final RowBounds rowBounds;    protected BoundSql boundSql;}

其实我具体往metaObject.getValue()的源码里面看了一下,水还挺深的,最近没时间研究这东西了,当做一个TODO项吧。
自己整理的MyBatis流程,稍微有一点乱

原创粉丝点击