mybatis 之 插件
Posted better_hui
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis 之 插件相关的知识,希望对你有一定的参考价值。
目录
一、自定义插件
mybatis的插件是代理模式 与 责任链模式的结合。 每一个插件以责任链并进行封装,都是一层对Executor的代理
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class , RowBounds.class , ResultHandler.class})}) public class MyInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("my Interceptor ..........."); return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { System.out.println(JSON.toJSONString(properties)); } }
配置文件
<plugins> <plugin interceptor="hee.frame.test.MyInterceptor"> <property name="dialect" value="mysql"/> </plugin> </plugins>
二、扫描
注意这是最原始的xml配置方式。以下是简述的扫描流程:
//1、 根据字节流或者字符流加载配置文件 new SqlSessionFactoryBuilder().builder(reader); //2、 根据配置文件 , 创建sqlSessionFactory public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); return 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. } } } //3、解析各种标签 private void parseConfiguration(XNode root) { try { propertiesElement(root.evalNode("properties")); //issue #117 read properties first typeAliasesElement(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 #631 databaseIdProviderElement(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); } } //4、添加到责任链 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); } } }
三、封装
封装的过程就是将解析后的interceptor配置,生成executor的一层层代理。
//1、sqlSession工厂 ,获取一个session SqlSession sqlSession = sqlSessionFactory.openSession(); //2、打开一个session 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); final Executor executor = configuration.newExecutor(tx, execType); //这一步是关键 return new DefaultSqlSession(configuration, executor, autoCommit); } 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(); } } //3、生成sqlSession内部执行器Executor public Executor newExecutor(Transaction transaction, ExecutorType executorType) { 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); } //这一步是关键 , 在executor的外面包上一层层的代理 executor = (Executor) interceptorChain.pluginAll(executor); return executor; } public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; } //4、封装代理 public static Object wrap(Object target, Interceptor interceptor) { Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor); Class<?> type = target.getClass(); Class<?>[] interfaces = getAllInterfaces(type, signatureMap); if (interfaces.length > 0) { return Proxy.newProxyInstance( type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)); } return target; }
综上 , 我们得到的sqlSession的内部执行器Executor , 实际上是一层层的代理。
四、执行
我们在使用自定义的mapper调用接口时 , 实际上调用的是MapperProxy代理类,核心的代码如下:
// 1、代理的执行 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } //获取映射的method , MapperMethod 包含了两个属性 //SqlCommand command; //MethodSignature method; final MapperMethod mapperMethod = cachedMapperMethod(method); //执行映射的方法 , return mapperMethod.execute(sqlSession, args); } //2、下面以查询为例 , 且忽略其他方法 MapperMethod.execute // sqlSession的查询 public Object execute(SqlSession sqlSession, Object[] args) { //填充参数到sql Object param = method.convertArgsToSqlCommandParam(args); //执行查询,sqlSession的执行 , 实际干活的是内部的Executor Object result = sqlSession.selectOne(command.getName(), param); return result; } //3、下面是内部执行器的逻辑,executor.query() public <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) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } //4、我们之前说过Executor的外面包了一层层的代理 , 这个代理的逻辑就是这个Plugin对象 ,implements InvocationHandler 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)) { //如果这个方法是被拦截的方法 , 那么就执行代理的逻辑 //注意这里是一层层的代理 , 所以会链式的执行 // interceptor1.intercept -> interceptor2.intercept -> ....... -> executor.query() return interceptor.intercept(new Invocation(target, method, args)); } return method.invoke(target, args); } catch (Exception e) { throw ExceptionUtil.unwrapThrowable(e); } }
五、应用
1、水平分表
2、权限控制
3、数据库加解密
以上是关于mybatis 之 插件的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot(十一):MyBatis插件之MyBatis-Plus
Mybatis -- 动态Sql概述动态Sql之<if>(包含<where>)动态Sql之<foreach>sql片段抽取