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 之 插件的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis之插件拦截

mybatis 之 插件

mybatis 之 插件

Mybatis插件之Mybatis-Plus的CRUD方法

Spring Boot(十一):MyBatis插件之MyBatis-Plus

mybatis三剑客之插件---MyBatis plugins