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、数据库加解密

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值