mybatis插件机制源码解析
引言
本篇源码解析基于MyBatis3.5.8版本。
首先需要说明的码搜是,本篇文章不是插件mybatis插件开发的教程,而是源码用插dapp 钱包 源码从源码层面分析mybatis是如何支持用户自定义插件开发的。
mybatis的搜索索用插件机制,让其扩展能力大大增加。码搜比如我们项目中经常用到的插件PageHelper,这就是源码用插一款基于mybatis插件能力开发的产品,它的搜索索用功能是让基于mybatis的数据库分页查询更容易使用。
当然基于插件我们还可以开发其它功能,码搜比如在执行sql前打印日志、插件做权限控制等。源码用插
正文mybatis插件也叫mybatis拦截器,搜索索用它支持从方法级别对mybatis进行拦截。整体架构图如下:
解释下几个相关概念:
Interceptor拦截器接口,用户自定义的拦截器就是实现该接口。
InterceptorChain拦截器链,其内部维护一个interceptorslist,表示拦截器链中所有的拦截器,并提供增加或获取拦截器链的方法。比如有个核心的方法是pluginAll。该方法用来生成代理对象。源码包升级mysql
Invocation拦截器执行时的上下文环境,其实就是目标方法的调用信息,包含目标对象、调用的方法信息、参数信息。核心方法是proceed。该方法的主要目的就是进行处理链的传播,执行完拦截器的方法后,最终需要调用目标方法的invoke方法。
mybatis支持在哪些地方进行拦截呢?你只需要在代码里搜索interceptorChain.pluginAll的使用位置就可以获取答案,一共有四处:
parameterHandler=(ParameterHandler)interceptorChain.pluginAll(parameterHandler);resultSetHandler=(ResultSetHandler)interceptorChain.pluginAll(resultSetHandler);statementHandler=(StatementHandler)interceptorChain.pluginAll(statementHandler);executor=(Executor)interceptorChain.pluginAll(executor);这四处实现的原理都是一样的,我们只需要选择一个进行分析就可以了。
我们先来看下自定义的插件是如何加载进来的,比如我们使用PageHelper插件,通常会在mybatis-config.xml中加入如下的配置:
<plugins><plugininterceptor="com.github.pagehelper.PageInterceptor"><!--configparamsasthefollowing--><propertyname="param1"value="value1"/></plugin></plugins>mybatis在创建SqlSessionFactory的时候会加载配置文件,
publicConfigurationparse(){ if(parsed){ thrownewBuilderException("EachXMLConfigBuildercanonlybeusedonce.");}parsed=true;parseConfiguration(parser.evalNode("/configuration"));returnconfiguration;}parseConfiguration方法会加载包括plugins在内的很多配置,
privatevoidparseConfiguration(XNoderoot){ try{ ...pluginElement(root.evalNode("plugins"));...}catch(Exceptione){ thrownewBuilderException("ErrorparsingSQLMapperConfiguration.Cause:"+e,e);}}privatevoidpluginElement(XNodeparent)throwsException{ if(parent!=null){ for(XNodechild:parent.getChildren()){ Stringinterceptor=child.getStringAttribute("interceptor");Propertiesproperties=child.getChildrenAsProperties();InterceptorinterceptorInstance=(Interceptor)resolveClass(interceptor).getDeclaredConstructor().newInstance();interceptorInstance.setProperties(properties);configuration.addInterceptor(interceptorInstance);}}}pluginElement干了几件事情:
创建Interceptor实例
设置实例的属性变量
添加到Configuration的interceptorChain拦截器链中
mybatis的插件是通过动态代理实现的,那肯定要生成代理对象,生成的逻辑就是前面提到的pluginAll方法,比如对于Executor生成代理对象就是,
executor=(Executor)interceptorChain.pluginAll(executor);接着看pluginAll方法,
/***该方法会遍历用户定义的企业html网站源码插件实现类(Interceptor),并调用Interceptor的plugin方法,对target进行插件化处理,*即我们在实现自定义的Interceptor方法时,在plugin中需要根据自己的逻辑,对目标对象进行包装(代理),创建代理对象,*那我们就可以在该方法中使用Plugin#wrap来创建代理类。*/publicObjectpluginAll(Objecttarget){ for(Interceptorinterceptor:interceptors){ target=interceptor.plugin(target);}returntarget;}这里遍历所有我们定义的拦截器,调用拦截器的plugin方法生成代理对象。有人可能有疑问:如果有多个拦截器,target不是被覆盖了吗?
其实不会,所以如果有多个拦截器的话,生成的代理对象会被另一个代理对象代理,从而形成一个代理链条,执行的时候,依次执行所有拦截器的拦截逻辑代码。
plugin方法是接口Interceptor的默认实现类,
defaultObjectplugin(Objecttarget){ returnPlugin.wrap(target,this);}然后进入org.apache.ibatis.plugin.Plugin#wrap,
publicstaticObjectwrap(Objecttarget,Interceptorinterceptor){ Map<Class<?>,Set<Method>>signatureMap=getSignatureMap(interceptor);Class<?>type=target.getClass();Class<?>[]interfaces=getAllInterfaces(type,signatureMap);if(interfaces.length>0){ returnProxy.newProxyInstance(type.getClassLoader(),interfaces,newPlugin(target,interceptor,signatureMap));}returntarget;}首先是获取我们自己实现的Interceptor的方法签名映射表。然后获取需要代理的对象的Class上声明的所有接口。比如如果我们wrap的是Executor,就是windbg查看对应源码Executor的所有接口。然后就是最关键的一步,用Proxy类创建一个代理对象(newProxyInstance)。
注意,newProxyInstance方法的第三个参数,接收的是一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。
我们这里传入的是Plugin类,故在动态运行过程中会执行Plugin的invoker方法。
如果对这一段不是很理解,建议先了解下java动态代理的原理。java动态代理机制中有两个重要的角色:InvocationHandler(接口)和Proxy(类),这个是背景知识需要掌握的。
我们在深入看下上面的getSignatureMap方法,
privatestaticMap<Class<?>,Set<Method>>getSignatureMap(Interceptorinterceptor){ //从Interceptor的类上获取Intercepts注解,说明我们自定义拦截器需要带注解InterceptsinterceptsAnnotation=interceptor.getClass().getAnnotation(Intercepts.class);//issue#if(interceptsAnnotation==null){ thrownewPluginException("No@Interceptsannotationwasfoundininterceptor"+interceptor.getClass().getName());}Signature[]sigs=interceptsAnnotation.value();Map<Class<?>,Set<Method>>signatureMap=newHashMap<>();//解析Interceptor的values属性(Signature[])数组,存入HashMap,Set<Method>>for(Signaturesig:sigs){ Set<Method>methods=MapUtil.computeIfAbsent(signatureMap,sig.type(),k->newHashSet<>());try{ Methodmethod=sig.type().getMethod(sig.method(),sig.args());methods.add(method);}catch(NoSuchMethodExceptione){ thrownewPluginException("Couldnotfindmethodon"+sig.type()+"named"+sig.method()+".Cause:"+e,e);}}returnsignatureMap;}首先需要从Interceptor的类上获取Intercepts注解,说明我们自定义拦截器需要带注解,比如PageHelper插件的定义如下:
<plugins><plugininterceptor="com.github.pagehelper.PageInterceptor"><!--configparamsasthefollowing--><propertyname="param1"value="value1"/></plugin></plugins>0所以我们可以知道,getSignatureMap其实就是拿到我们自定义拦截器声明需要拦截的类以及类对应的方法。
前面说过,当我们调用代理对象时,java的native源码最终会执行Plugin类的invoker方法,我们看下Plugin的invoker方法,
<plugins><plugininterceptor="com.github.pagehelper.PageInterceptor"><!--configparamsasthefollowing--><propertyname="param1"value="value1"/></plugin></plugins>1Interceptor接口的intercept方法就是我们自定义拦截器需要实现的逻辑,其参数为Invocation,可从Invocation参数中拿到执行方法的对象,方法,方法参数,比如我们可以从statementHandler拿到SQL语句,实现自己的特殊逻辑。
在该方法的结束需要调用invocation#proceed()方法,进行拦截器链的传播。
参考:
blogs.com/chenpi/p/.html
wordpress SEO:Code Snippets插件给网站添加头条搜索“时间因子”,亲测无误!
在对网站流量进行深入分析后,甲爸注意到尽管搜索引擎带来的流量来源多样,但头条搜索的流量却相对较少。在探索了多个站长平台后,他发现头条站长搜索平台的独特功能——“时间因子”引起了关注,这个SEO优化技巧有望在年发挥重要作用。
为了实现这个功能,甲爸借助了WordPress平台的Code Snippets插件。首先,他成功安装并启用该插件,然后直接添加适用于“时间因子”的实用代码。无需复杂的设置,只需按照插件界面的指示操作,如设置1、2、3、4所示,确保在内容页面的源代码中生成了“头条时间因子”标签。
尽管目前还不清楚这个功能能带来多大的SEO提升,但方法的确简洁有效。相比之下,修改主题header.php文件虽然也能实现,但甲爸建议避免这种做法,因为随着主题升级,先前的修改可能会被覆盖,导致功能失效。因此,使用Code Snippets插件来添加“时间因子”是一个更稳定且易于维护的解决方案。
IDEA插件Apipost-Helper-2.0使用指南
IDEA插件Apipost-Helper-2.0的使用指南提供了高效便捷的API文档生成和调试体验。这款插件能让你在IDEA项目中轻松实现以下功能:
1. 无需额外操作,代码编写完成后,只需右键同步接口,即可自动生成详细的API文档和访问链接。
2. 类似Postman的快速调试模式,让你在IDEA内直接进行接口测试。
3. 强大的搜索功能,支持根据API查找源码和生成的接口树,提高代码定位效率。
4. 点击接口代码处的箭头图标,能快速跳转到对应接口的列表,便于管理和理解代码逻辑。
5. 发送请求后,你可以查看详细的发送记录和详情,这对于跟踪接口行为和调试非常有帮助。
6. 新增的保存功能,方便你在调试过程中记录重要的发送和返回数据,便于后续查阅。
安装和更新插件也很简单,直接在IDEA编辑器的插件中心搜索Apipost-Helper-2.0,安装支持.3月至.2月的版本即可。无需担心云端域名设置,因为默认值已经设置好。总的来说,Apipost-Helper-2.0是IDEA开发人员的理想工具,不仅功能全面,而且免费且易于使用。
maven的Package的jar怎么看源码?
Maven的package的jar的源码可以通过以下几种方式查看:
1. 使用Eclipse或IntelliJ IDEA,导入该jar文件,然后导入项目,就可以查看源码了。
2. 使用JD-GUI工具,可以查看JAR文件的反编译源码。
3. 使用Maven插件,可以查看Maven依赖的源码,比如使用Maven-Source-Plugin插件,可以查看当前项目依赖的源码,通过以下命令可以查看:
mvn dependency:sources
FindBugs源码分析工具使用指南
FindBugs是一款提供Java源码静态分析的开源工具,它在程序未运行的情况下,通过分析jar包或classes文件,帮助开发者发现潜在的bug。FindBugs具备GUI、命令行、ant、插件等多种运行模式,本文以命令行和Ant方式为例,介绍其基本使用方法。
在命令行模式下,首先生成HTML报告样例,随后,可以通过Ant方式运行,前提是在项目中创建build.xml文件。执行ant findbugs命令后,开始执行分析过程,对于大型项目,此过程可能耗时较长,通常需十几分钟。若无异常输出,耐心等待直至生成报告。
FindBugs将bug分为几大类,并提供详细的bug描述,访问findbugs.sourceforge.net...即可查看。通过HTML报告,用户可以在“List bugs by bug category”页签下按类别查看bug,展开任一bug,可了解其类别、所属类及其属性或方法、代码文件名与行数。对于不清楚bug原因的情况,可查阅描述页面获取详细解释。
对于项目中包含多个jar包的情况,本文推荐使用rejarForAnalysis脚本。该脚本位于FindBugs工具的bin目录下,Linux环境下运行需赋予可执行权限。rejarForAnalysis脚本用于整合多个jar包,形成一个大型的analyze.jar包。结合shell命令,可自动在当前目录及子目录下查找所有jar包,并生成整合包,随后,分析过程与常规FindBugs执行方式相同。
通过本文介绍,用户应能熟练掌握FindBugs的基本使用方法,并解决项目中遇到的常见问题。参考文献提供进一步学习资源。
2024-11-23 11:02
2024-11-23 10:09
2024-11-23 09:57
2024-11-23 09:45
2024-11-23 09:36