当前位置: 首页 > 编程日记 > 正文

MyBatis的扩展点(plugins)

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

1、mybatis扩展点plugins

mybatis的扩展是通过拦截器Interceptor来实现的,本质上就是JDK的动态代理,所以它只能对接口进行拦截,mybatis可以对以下四个接口类型进行拦截,也就是说会对这4种对象进行代理,所有的代理拦截都是通过 InterceptorChain.pluginAll(Object target) 来实现的。

Executor: 执行器,执行SQL语句(所有的sql都通过它来执行),并且对事务、缓存等提供统一接口。(在这一层上做拦截的权限会更大)
StatementHandler: 对statement进行预处理,并且提供统一的原子的增、删、改、查接口。(如果要在SQL执行前进行拦截的话,拦截这里就可以了)
ResultSetHandler:对返回结果ResultSet进行处理。
PameterHandler:对参数进行赋值。

SqlSession的创建过程:

mybatis中的SQL都是通过DefaultSqlSession去执行的,其创建过程如下:

  // SqlSessionFactoryBuilder => DefaultSqlSessionFactory => DefaultSqlSessionSqlSession sqlSession = new SqlSessionFactoryBuilder().build(in, "development", pro).openSession();

2、源码解读具体实现(以Executor接口为例)

2.1、创建SqlSession时,SqlSessionFactroy会解析mybatis.xml配置文件中的plugins标签,并将Interceptor属性定义的Interceptor放到interceptorChain中;

  // SqlSessionFactoryBuilder.java  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {try {XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);// 解析mybatis.xml配置文件,并创建DefaultSqlSessionFactoryreturn build(parser.parse());} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {ErrorContext.instance().reset();try {reader.close();} catch (IOException e) {// Intentionally ignore. Prefer previous error.}}}
  // XMLConfigBuilder.javapublic Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;parseConfiguration(parser.evalNode("/configuration"));return configuration;}// 解析mybatis.xml中的各个标签private void parseConfiguration(XNode root) {try {propertiesElement(root.evalNode("properties")); //issue #117 read properties firsttypeAliasesElement(root.evalNode("typeAliases"));pluginElement(root.evalNode("plugins"));objectFactoryElement(root.evalNode("objectFactory"));objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));settingsElement(root.evalNode("settings"));environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631databaseIdProviderElement(root.evalNode("databaseIdProvider"));typeHandlerElement(root.evalNode("typeHandlers"));mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}}// 解析plugins标签,并把Interceptor放到interceptorChain中private void pluginElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {String interceptor = child.getStringAttribute("interceptor");Properties properties = child.getChildrenAsProperties();Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();interceptorInstance.setProperties(properties);configuration.addInterceptor(interceptorInstance);}}}// Configuration,mybatis文件的抽象类public void addInterceptor(Interceptor interceptor) {interceptorChain.addInterceptor(interceptor);}

2.2、DefaultSqlSessionFactory.openSession()时使用JDK动态代理生成@Signature注解指定的被代理类(包含代理的方法以及方法参数)

  // DefaultSqlSessionFactory.javapublic SqlSession openSession() {return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);// 使用Configuration创建Executorfinal Executor executor = configuration.newExecutor(tx, execType, autoCommit);return new DefaultSqlSession(configuration, executor);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}}
  // Configuration.javapublic Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) {executorType = executorType == null ? defaultExecutorType : executorType;executorType = executorType == null ? ExecutorType.SIMPLE : executorType;Executor executor;if (ExecutorType.BATCH == executorType) {executor = new BatchExecutor(this, transaction);} else if (ExecutorType.REUSE == executorType) {executor = new ReuseExecutor(this, transaction);} else {executor = new SimpleExecutor(this, transaction);}if (cacheEnabled) {executor = new CachingExecutor(executor, autoCommit);}// 使用JDK动态代理生成Executor的interceptorChainexecutor = (Executor) interceptorChain.pluginAll(executor);return executor;}

2.3、InterceptorChain生成的具体过程

  // InterceptorChain.javapublic Object pluginAll(Object target) {for (Interceptor interceptor : interceptors) {target = interceptor.plugin(target);}return target;}
  // Interceptor的具体实现类(即我们业务上要实现的功能)@Overridepublic Object plugin(Object arg0) {return Plugin.wrap(arg0, this);}
  // Plugin.javapublic static Object wrap(Object target, Interceptor interceptor) {// getSignatureMap获取Interceptor类上的@Intercepts(@Signature)内容Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);Class<?> type = target.getClass();Class<?>[] interfaces = getAllInterfaces(type, signatureMap);// 生成目标类target(Executor.class)的代理类,实现我们需要的plugin功能if (interfaces.length > 0) {return Proxy.newProxyInstance(type.getClassLoader(),interfaces,new Plugin(target, interceptor, signatureMap));}return target;}// 解析实现Interceptor接口的类上定义的@Intercepts(@Signature)内容,获取需要拦截的类和方法。// 例如:@Signature(type = Executor.class, method = "query",  args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);if (interceptsAnnotation == null) { // issue #251throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());      }Signature[] sigs = interceptsAnnotation.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 {// sig.type()即Executor.classMethod 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;}  

3、demo关键步骤

3.1、实现自定义的Interceptor

// 自定义拦截器@Intercepts({@Signature(type = Executor.class, method = "query",  args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})public class MyTestInterceptor implements Interceptor {private static final String MSG = "octopus route table info is not exit!";@Overridepublic Object intercept(Invocation arg0) throws Throwable {Object obj = null;try {obj = arg0.proceed();} catch (Throwable e) {if (e.getCause() instanceof MySQLSyntaxErrorException) {MySQLSyntaxErrorException ex = (MySQLSyntaxErrorException) e.getCause();System.out.println("====" + ex.getErrorCode());System.out.println("====" + ex.getSQLState());System.out.println("====" + ex.getMessage());System.out.println("====" + ex.getCause());if (MSG.equals(ex.getMessage())) {throw new RouteTableNoExistException();}}}return obj;}@Overridepublic Object plugin(Object arg0) {return Plugin.wrap(arg0, this);}@Overridepublic void setProperties(Properties arg0) {System.out.println("env value: " + arg0.getProperty("names"));}}

3.2、在mybatis.xml中配置plugins

<configuration><plugins><plugin interceptor="com.pinganfu.interceptor.MyTestInterceptor" /></plugins>	<environments default="development"><environment id="development"><transactionManager type="MANAGED"><property name="closeConnection" value="false" /></transactionManager><dataSource type="POOLED"><property name="driver" value="${driver}" /><property name="url" value="${jdbcUrl}" /><property name="username" value="${username}" /><property name="password" value="${password}" /></dataSource></environment></environments>	<mappers><mapper resource="mappers/TBATMapper.xml" /></mappers>	
</configuration>

3.3、获取SqlSession

  Properties pro = new Properties();try {pro.load(Resources.getResourceAsStream("jdbc.properties"));// 加载mybatis.xml中的pluginsInputStream in = Resources.getResourceAsStream("mybatis.xml");sqlSession = new SqlSessionFactoryBuilder().build(in, "development", pro).openSession();} catch (IOException e) {e.printStackTrace();}		

4、mybatis针对各种异常的处理

mybatis通过DefaultSqlSession执行时,会将发生的所有异常统一包装成PersistenceException再抛出,我们可以通过PersistenceException.getCause()获取具体的异常。

  // DefaultSqlSession.javapublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {MappedStatement ms = configuration.getMappedStatement(statement);List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);return result;} catch (Exception e) {// 对执行发生的所有Exception进行wrap之后再抛出throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}}
  // ExceptionFactory.javapublic static RuntimeException wrapException(String message, Exception e) {// 将Exception进行统一包装成PersistenceExceptionreturn new PersistenceException(ErrorContext.instance().message(message).cause(e).toString(), e);}

Ref:

https://www.cnblogs.com/kevin-yuan/p/7219003.html

https://www.cnblogs.com/fangjian0423/p/mybatis-interceptor.html

https://www.cnblogs.com/fenglanglang/p/6007653.html

转载于:https://my.oschina.net/u/3787772/blog/2962941

相关文章:

linux中使用CST时间

GMT(Greenwich Mean Time&#xff0c;格林威治标准时间): 是指位于英国伦敦郊区的格林尼治天文台的标准时间&#xff0c;因为本初子午线被定义在通过那里的经线。 UTC(Universal Time/Temps Cordonn 世界标准时间)CST(Central Standard Time 國家標準時間&#xff0c;一說中原標…

到「黄埔学院」去:打造AI首席架构师,第二期限量招募!

今年 1 月&#xff0c;百度联合“深度学习技术及应用国家工程实验室”成立黄埔学院&#xff0c;旨在为产业培养第一批“首席AI架构师”。黄埔学院一期学员历时半年的学习和交流&#xff0c;6 月 16 日&#xff0c;黄埔学院一期学员迎来了毕业典礼&#xff0c;并在 7 月百度 AI开…

linux守护进程的创建

下面的完成了这样一个功能&#xff0c;创建一个守护进程&#xff0c;每个一秒在/tmp目录下的文件peng.txt中记录当前系统时间。 一、守护进程 守护进程是linux中的后台服务进程&#xff0c;在系统启动时开始运行&#xff0c;在系统关闭时终止。Linux系统中的大多数服务进程都是…

tesseract3.01的训练和使用

相关源码、资源下载&#xff1a;http://code.google.com/p/tesseract-ocr/downloads/list 训练步骤&#xff1a; 1、 Generate Training Images&#xff1a;生成tif图像文件(简单的几个汉字)&#xff1b; 如&#xff1a;ABC.Roman.exp0.tif([lang].[fontname].exp[num].tif)…

旷视推出鼻纹识别,用AI寻找丢失宠物

来源 | 转载自旷视城市大脑&#xff08;ID&#xff1a;MEGVII_CityBrain)导读&#xff1a;随着人工智能技术&#xff08;AI&#xff09;的大热&#xff0c;基于深度学习方法的人脸识别技术已成熟落地&#xff0c;在解锁、支付、认证、摄像等生活方方面面&#xff0c;各个大厂推…

Qt浅谈之一:内存泄露(总结)

一、简介 Qt内存管理机制&#xff1a;Qt 在内部能够维护对象的层次结构。对于可视元素&#xff0c;这种层次结构就是子组件与父组件的关系&#xff1b;对于非可视元素&#xff0c;则是一个对象与另一个对象的从属关系。在 Qt 中&#xff0c;在 Qt 中&#xff0c;删除父对…

LINUX新手入门-1.装系统

LINUX新手入门-1.装系统首先我们用虚拟机模拟 装linux系统&#xff0c;然后下一步下一步&#xff0c;然后完成后&#xff0c;编辑一些设置&#xff0c;把镜像放上面就可以了选第一项&#xff0c;安装系统&#xff0c;查看镜像是否能运行&#xff0c;直接跳过&#xff0c;选择语…

Log4cplus1.04的使用

首先&#xff0c;从http://sourceforge.net/projects/log4cplus/files/log4cplus-stable/下载最新的版本&#xff0c;解压缩&#xff0c;用vs2008打开msvc8文件夹下的log4cplus.sln&#xff0c;并按照提示转换。在Solution Configurations下拉列表框中&#xff0c;会有Debug、D…

FRVT赛程全纪录:格灵深瞳全球排名前五

作者 | 张德兵&#xff0c;格灵深瞳首席科学家&算法部负责人来源 | 转载自知乎张德兵最近两个月&#xff0c;格灵深瞳首席科学家&算法部负责人张德兵与算法团队参加了全球人脸识别算法测试(FRVT、Face Recognition Vendor Test)。虽然是第一次参加此比赛&#xff0c;格…

反转比特位(文章最后有干货)【转】

转自&#xff1a;https://blog.csdn.net/wuxianglonghaohao/article/details/21602305 http://www.newhottopic.com/2014/03/20/reverse-bits/ 把一个无符号整数的比特位反转顺序。有很多种方法来实现这个。我们这里给出一个算法&#xff1a;通过异或运算来交换&#xff0c;然后…

过关斩将打进Kaggle竞赛Top 0.3%,我是这样做的

作者 | Lavanya Shukla译者 | Monanfei责编 | 夕颜出品 | AI科技大本营&#xff08;id&#xff1a;rgznai100&#xff09;导读&#xff1a;刚开始接触数据竞赛时&#xff0c;我们可能会被一些高大上的技术吓到。各界大佬云集&#xff0c;各种技术令人眼花缭乱&#xff0c;新手们…

JavaBean规范

2019独角兽企业重金招聘Python工程师标准>>> &#xff08;1&#xff09;JavaBean 类必须是一个公共类&#xff0c;并将其访问属性设置为 public &#xff08;2&#xff09;JavaBean 类必须有一个空的构造函数&#xff1a;类中必须有一个不带参数的公用构造器&#x…

vigra1.8.0的使用

VIGRA stands for "Vision with Generic Algorithms". Its a novel computer vision library that puts its main emphasis oncustomizablealgorithms and data structures. 1、首先&#xff0c;从http://hci.iwr.uni-heidelberg.de/vigra/下载最新源代码&#xff0…

17个Python小窍门

python中相对不常见却很实用的小窍门。 空谈不如来码代码吧&#xff1a; 交换变量值 给列表元素创建新的分隔符 找列表中出现次数最多的元素 核对两个字符是否为回文 反向输出字符串 反向输出列表 转置2维数组 链式比较 我刚整理了一套2018最新的0基础入门和进阶教程&#xff0…

用产品思路建设中台,这走得通吗?| 白话中台

作者 | 王健&#xff0c;ThoughtWorks首席咨询师。 十多年国内外大型企业软件设计开发&#xff0c;团队组织转型经验。一直保持着对技术的热爱&#xff0c;热衷于技术分享。目前专注在企业平台化转型、中台战略规划&#xff0c;微服务架构与实施&#xff0c;大型遗留系统服务化…

利用cvMinAreaRect2求取轮廓最小外接矩形

转自&#xff1a;http://blog.csdn.net/mine1024/article/details/6044856 对给定的 2D 点集&#xff0c;寻找最小面积的包围矩形&#xff0c;使用函数&#xff1a; CvBox2D cvMinAreaRect2( const CvArr* points, CvMemStorage* storageNULL ); points 点序列或点集数组 …

电脑开机显示Invalidsystemdisk

开机或重启无法进入系统&#xff0c;并在屏幕上显示Invalidsystemdisk&#xff0c;Replacethediskandthenpressanykey或者diskerror之类的字样&#xff0c;这是怎么回事&#xff0c;该如何解决&#xff1f;今天u大师就为大家解决下。 出现这个原因是因为现在的电脑没有可以启…

Windows7 64位下vs2008配置OpenCV2.3.1

1、下载OpenCV2.3.1&#xff1a;http://www.opencv.org.cn/index.php/Download&#xff1b; 2、下载后解压缩&#xff1a;OpenCV-2.3.1-win-superpack.exe&#xff0c;生成一个opencv文件夹&#xff1b; 3、下载CMake&#xff1a;http://www.cmake.org/cmake/resources/softw…

腾讯拥抱开源:首次公布开源路线图,技术研发向共享、复用和开源迈进

整理 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;导读&#xff1a;去年&#xff0c;知乎上一篇讨论腾讯技术的帖子异常火爆&#xff0c;讨论的主题是当下&#xff08;2018 年&#xff09;腾讯的技术建设是否处于落后同体量公司的状态&#xff0c;这篇帖子得…

Babylon.js 3.3发布:更强大的粒子系统和WebVR支持

Babylon.js 3.3版本利用微软混合现实工具包&#xff08;MRTK&#xff09;的功能来改进WebVR开发&#xff0c;并改进了其粒子系统控件。 MRTK提供了一系列脚本和组件来加速混合现实应用程序的开发。为了简化GUI VR构建&#xff0c;Bablyon.js利用3D体积网格来布局VR场景的界面&a…

基于Erlang语言的视频相似推荐系统 | 深度

作者丨gongyouliu来源 | 转载自大数据与人工智能&#xff08;ID:ai-big-data&#xff09;【导语】&#xff1a;作者在上一篇文章《基于内容的推荐算法》中介绍了基于内容的推荐算法的实现原理。在本篇文章中作者会介绍一个具体的基于内容的推荐算法的实现案例。该案例是作者在2…

MinGW简介

转自&#xff1a;http://baike.baidu.com/view/98554.htm MinGW是指只用自由软件来生成纯粹的Win32可执行文件的编译环境&#xff0c;它是Minimalist GNU on Windows的略称。这里的“纯粹”是指使用msvcrt.dll的应用程序。无法使用MFC (Microsoft Foundation Classes微软基础类…

Confluence 6 创建小组的公众空间

2019独角兽企业重金招聘Python工程师标准>>> 现在是我们可以开始创建公众空间的时候了&#xff0c;全世界都希望知道这个项目和勇敢的探险活动。 在这个步骤中&#xff0c;我们将会创建一个项目小组的空间&#xff0c;并且将这个空间公布给全世界。这个表示的是你将…

windows 7 可以清除的文件

缓解系统磁盘空间不足的情况1、系统盘根目录下的MSOCache是office的安装备份文件&#xff0c;可以删除。2、c:\user\用户名\appdate\local\temp是软件安装时留下的临时文件。3、c:\windows\SoftwareDistribution中存放的是系统补丁更新包及旧的系统文件。4、c:\windows\winsxs\…

阿里最新论文解读:考虑时空域影响的点击率预估模型DSTN

作者 | 石晓文转载自小小挖掘机&#xff08;ID: wAIsjwj&#xff09;【导语】&#xff1a;在本文中&#xff0c;阿里的算法人员同时考虑空间域信息和时间域信息&#xff0c;来进行广告的点击率预估。什么是时空域&#xff1f;我们可以分解为空间域(spatial domain)和时间域(tem…

windows7 64位机上配置MinGW+Codeblocks+ wxWidgets

在Windows7 64位机子上安装配置MinGWCodeblockswxWidgets步骤如下&#xff1a; 1、 下载mingw-get-inst-20111118&#xff1a;http://sourceforge.net/projects/mingw/&#xff1b; 2、 双击mingw-get-inst-20111118.exe&#xff0c;一般按默认即可&#xff0c;选择自己需要…

jQuery带动画的弹出对话框

在线演示 本地下载

陶哲轩实分析 习题 13.4.6

设 $(X,d)$ 是度量空间,并设 $(E_{\alpha})_{\alpha\in I}$ 是 $X$ 中的一族连通集合.还设 $\bigcap_{\alpha\in I}E_{\alpha}$ 不空.证明 $\bigcup_{\alpha\in I}E_{\alpha}$ 是连通的.证明:由于 $\bigcap_{\alpha\in I}E_{\alpha}$ 是不空的,因此存在 $p\in \bigcap_{\alpha\…

一年参加一次就够,全新升级的AI开发者大会议程出炉!

“只讲技术&#xff0c;拒绝空谈”的AI开发者大会再次来临&#xff01;2018 年的AI开发者大会&#xff0c;作为年度人工智能领域面向专业开发者的一次高规格技术盛会&#xff0c;上千名开发者与上百名技术专家齐聚一堂&#xff0c;大会以“AI技术与应用”为核心&#xff0c;就人…

Windows7下配置MinGW+CodeBlocks+OpenCV2.3.1

1、下载mingw-get-inst-20111118&#xff1a;http://sourceforge.net/projects/mingw/&#xff1b; 2、双击mingw-get-inst-20111118.exe&#xff0c;一般按默认即可&#xff0c;选择自己需要的组件&#xff1b; 3、添加MinGW环境变量&#xff1a;选择计算机-->点击右键--…