当前位置: 首页 > 后端技术 > Java

通过PageHelper看Mybatis的插件机制

时间:2023-04-01 21:03:51 Java

插件就是插件,本质上是一系列方便用户自定义扩展的拦截器。保留了四个扩展点:Executor(update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)ParameterHandler(getParameterObject,setParameters)ResultSetHandler(handleResultSets,handleOutputParameters)StatementHandler(prepare,parameterize,batch,update,query)我们拿以Pagehelper为例,看看它是如何实现Executor扩展的。1、startPage是做什么的?PageHelper.startPage(1,20);这是PageHelper的推荐使用方式,后续的第一个查询方法会被分页。观察这个方法的内部逻辑publicstaticPagestartPage(intpageNum,intpageSize,booleancount){//封装成一个页面对象Pagepage=newPage(pageNum,pageSize,数数);//==将页面对象存储在`PageMethod#LOCAL_PAGE`中-这是一个ThreadLocalsetLocalPage(page);returnpage;}二、PageHelper是如何与Mybatis建立联系的?1.在配置mybatis的xml中添加配置添加插件配置后,解析xml的时候会放在配置中org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration{//解析插件pluginElement(root.evalNode("plugins"));}org.apache.ibatis.builder.xml.XMLConfigBuilder#pluginElement{for(XNodechild:parent.getChildren()){//根据配置创建一个Interceptor对象Stringinterceptor=child.getStringAttribute("拦截器");属性properties=child.getChildrenAsProperties();拦截器interceptorInstance=(Interceptor)resolveClass(interceptor).getDeclaredConstructor().newInstance();//执行setProperties方法设置属性interceptorInstance.setProperties(properties);//==将拦截器存储在配置中configuration.addInterceptor(interceptorInstance);看到拦截器最终存储在哪里了吗?org.apache.ibatis.session.Configuration#addInterceptororg.apache.ibatis.plugin.InterceptorChain#addInterceptorclassInterceptorChain{publicvoidaddInterceptor(Interceptor拦截器){interceptors.add(拦截器);}//==最终存储位置privatefinalListinterceptors=newArrayList<>();结构观察PageInterceptor结构//==由@Intercepts注解修饰@Intercepts({//==通过@Signature指定拦截的方法@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}),})//==实现拦截器接口publicclassPageInterceptorimplementsInterceptorInterceptor接口有三个方法://==开始拦截后,会执行该方法的逻辑Objectintercept(Invocationinvocation)throwsThrowable;//==defaultwrapperdefaultObjectplugin(Objecttarget){returnPlugin.wrap(target,this);}//在初始化期间设置属性defaultvoidsetProperties(Propertiesproperties){}triggerPageInterceptor执行者创建部分中的条目org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSession()org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource.apache.ibatis.session.ExecutorType){//==过滤链封装executorexecutor=(Executor)interceptorChain.pluginAll(executor);}org.apache.ibatis.plugin.InterceptorChain#pluginAll{for(Interceptorinterceptor:interceptors){//==包装目标对象target=interceptor.plugin(target);}returntarget;}查看具体的封装方式defaultObjectplugin(Objecttarget){returnPlugin.wrap(target,this);??????//==核心逻辑是动态代理returnProxy.newProxyInstance(type.getClassLoader(),interfaces,newPlugin(target,interceptor,signatureMap));}既然是动态代理,直接观察invokcationHandler(Plugin)的调用方法publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{//执行拦截器的拦截方法returninterceptor.intercept(newInvocation(target,method,args));}observePageInterceptor#interceptimplementation:com.github.pagehelper.PageInterceptor#intercept{//==判断是否需要分页,如果不需要,直接返回结果if(!dialect.skip(ms,parameter,rowBounds)){//--判断是否需要分页查询,page.getPageSize>0if(dialect.beforePage(ms,parameter,rowBounds)){//--调用dialect获取分页sql,添加limit语句StringpageSql=dialect.getPageSql(ms,boundSql,parameter,rowBounds,pageKey);//执行分页查询resultList=executor.query(ms,parameter,RowBounds.DEFAULT,resultHandler,pageKey,pageBoundSql);}}}如何判断是否需要分页?com.github.pagehelper.PageHelper#skippublicbooleanskip(MappedStatementms,ObjectparameterObject,RowBoundsrowBounds){//==PageMethod#LOCAL_PAGE页面是否有值page=pageParams.getPage(parameterObject,rowBounds);//--PageMethod#LOCAL_PAGE有值,初始化方言autoDialect.initDelegateDialect(毫秒);returnfalse;}这个接第一节PageHelper.startPage将page对象存储在ThreadLocal中这里skip()方法判断:如果有值返回false,需要分页,同时初始化dialect方言;没有值返回true,不需要分页。方言方言的初始化逻辑通过数据库链接url中截取的数据库别名选择具体的方言类,并通过反射初始化com.github.pagehelper.page.PageAutoDialect#initDelegateDialect{this.delegate=getDialect(ms);}com。github.pagehelper.page.PageAutoDialect#getDialect{//通过数据库链接拦截别名StringdialectStr=fromJdbcUrl(url);//--通过别名dialect=initDialect(dialectStr,properties);初始化AbstractHelperDialect;}com.github.pagehelper.page.PageAutoDialect#initDialect{//分析方言类ClasssqlDialectClass=resloveDialectClass(dialectClass);?????//==通过dialectAliasMap获取dialectAliasMap.get(className.toLowerCase());//通过反射创建方言=(AbstractHelperDialect)sqlDialectClass.newInstance();}看看dialectAliasMap中存储了什么com.github.pagehelper.page.PageAutoDialect#dialectAliasMapstatic{//初始化时注册一个别名dialectAliasMap.put("hsqldb",HsqldbDialect.class);方言别名映射。放("h2",HsqldbDialect.class);dialectAliasMap.put("postgresql",HsqldbDialect.class);dialectAliasMap.put("mysql",MySqlDialect.class);}附录P6-P7知识宝典

最新推荐
猜你喜欢