第四篇:Configuration对象构建-2

上一篇主要讲了properties、settings、typeAliases三个节点的解析,其实还是比较容易的

这一篇主要讲下这个几个 plugins、factory相关的、environments

话不多说,还是接着看源码

plugins

首先还是先看下使用,具体可以参考官网地址

https://mybatis.org/mybatis-3/zh/configuration.html#plugins


1. 先定义实现了Interceptor接口的实现类

@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  private Properties properties = new Properties();

  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    // implement pre processing if need
    Object returnObject = invocation.proceed();
    // implement post processing if need
    return returnObject;
  }

  @Override
  public void setProperties(Properties properties) {
    this.properties = properties;
  }
}


2. 在mybatis-config.xml文件中进行配置

<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

其中properties 到时mybatis会拿到你的配置设置进去



这个主要是mybatis提供给我们进行方法拦截的功能,分页自动拦截就发生在这些插件中




接着看下源码的解析


  private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      //循环插件列表
      for (XNode child : parent.getChildren()) {
        //获取拦截器全类名
        String interceptor = child.getStringAttribute("interceptor");
        //获取你配置的属性值,到时会调用Interceptor接口的setProperties传给你定义的拦截器
        Properties properties = child.getChildrenAsProperties();
        //解析拦截器,这里别名就发挥作用了,你可以定义一些简短的名字,在引用的时候就比较方便了
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        interceptorInstance.setProperties(properties);
        //添加到配置类中进行统一管理
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }



仍然是一目了然,其实还是挺简单的,别名注册测就发挥了作用,可以看看resolveClass方法做了什么


  protected Class<?> resolveClass(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      //从别名注册里面解析一个类出来
      return resolveAlias(alias);
    } catch (Exception e) {
      throw new BuilderException("Error resolving class. Cause: " + e, e);
    }
  }


再到具体的解析方法

  protected Class<?> resolveAlias(String alias) {
    //其实就是调用前面的注册map进行获取了
    return typeAliasRegistry.resolveAlias(alias);
  }



再稍微看下configuration里面的Interceptor是如何管理的,后面会再说到这个东西,因为这个是sql运行过程中一个比较核心的拦截点


configuration使用了InterceptorChain对象同时使用了组合模式来统一管理所有的拦截器,如下


/**
 * @author Clinton Begin
 * 这里使用了一个组合模式来统一管理Interceptor对象
 */
public class InterceptorChain {

  /**
   * 封装了拦截器链表
   */
  private final List<Interceptor> interceptors = new ArrayList<Interceptor>();

  /**
   * 统一进行代理,所以要看哪里可以代理,直接看这里就行了
   * @param target
   * @return
   */
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

  /**
   * 添加一个拦截器
   * @param interceptor
   */
  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }

  /**
   * 获取不可修改的拦截器链
   * @return
   */
  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}



一般进行代理都是使用 Plugin.wrap(target, this);即可,这是mybatis提供的插件代理生成,可以大概看看里面做了什么


  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) {
      //使用jdk进行动态代理,InvocationHandler为Plugin当前这个类
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }


比如上面的 ExamplePlugin 就是一个例子了,这个代理逻辑等后面sql执行的时候再具体分析,当然有兴趣的可以自行分析

factory相关的

这个我们一般不会用,所以只会讲两个比较简单的,如下

官网参考地址:https://mybatis.org/mybatis-3/zh/configuration.html#objectFactory

objectFactory:这个接口是mybatis用来创建对象时候使用的,他提供了一个默认实现DefaultObjectFactory,我们一般不会定义,当然了你也可以进行定义


1,先提供一个实现类

public class ExampleObjectFactory extends DefaultObjectFactory {
  @Override
  public <T> T create(Class<T> type) {
    return super.create(type);
  }

  @Override
  public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    return super.create(type, constructorArgTypes, constructorArgs);
  }

  @Override
  public void setProperties(Properties properties) {
    super.setProperties(properties);
  }

  @Override
  public <T> boolean isCollection(Class<T> type) {
    return Collection.class.isAssignableFrom(type);
  }}


2. 在mybatis-config.xml进行注册

<objectFactory type="org.mybatis.example.ExampleObjectFactory">
  <property name="someProperty" value="100"/>
</objectFactory>



这样写后mybatis就会用你的对象工厂来生成对象了,但是我们一般不会自己去定义,当然了如果你有需要也是可以提供的



再来看下源码的解析


  if (context != null) {
      //获取类型
      String type = context.getStringAttribute("type");
      //获取properties
      Properties properties = context.getChildrenAsProperties();
      //依然使用了别名处理器进行解析
      ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
      factory.setProperties(properties);
      //设置给configuration对象
      configuration.setObjectFactory(factory);
    }
  }


就这样,一目了然,没有太多可以说的



至于objectWrapperFactory reflectorFactory这里就不说了,原理都是一样的,我们一般不会去自己设置,有兴趣的话可以自己参考下官网和解析

environments

这是环境相关的一个类,主要就是数据源和事务工厂了,不得不说mybatis的抽象还是比我们普通人要好的

官网配置地址:https://mybatis.org/mybatis-3/zh/configuration.html#environments

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
  </environment>
</environments>



这里主要是讲几个比较核心的接口

DataSourceFactory:数据源工厂,用来获取数据源的,想必大家对这个还是比较熟悉的,没错,mybatis自己提供了数据库连接池的实现,接口就这样



public interface DataSourceFactory {

  /**
   * 设置你配置的熟悉
   * @param props
   */
  void setProperties(Properties props);

  /**
   * 获取数据源
   * @return
   */
  DataSource getDataSource();

}



TransactionFactory:事务工厂,主要是用来获取Transaction事务对象的,看看接口的样子



public interface TransactionFactory {

  /**
   * Sets transaction factory custom properties.
   * @param props
   */
  void setProperties(Properties props);

  /**
   * Creates a {@link Transaction} out of an existing connection.
   * @param conn Existing database connection
   * @return Transaction
   * @since 3.1.0
   */
  Transaction newTransaction(Connection conn);
  
  /**
   * Creates a {@link Transaction} out of a datasource.
   * @param dataSource DataSource to take the connection from
   * @param level Desired isolation level
   * @param autoCommit Desired autocommit
   * @return Transaction
   * @since 3.1.0
   */
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);

}



Transaction:事务管理对象,用来处理一次事务过程中所需要的属性和对象已经操作的,看到接口的定义你就明白了


public interface Transaction {

  /**
   * Retrieve inner database connection
   * @return DataBase connection
   * @throws SQLException
   */
  Connection getConnection() throws SQLException;

  /**
   * Commit inner database connection.
   * @throws SQLException
   */
  void commit() throws SQLException;

  /**
   * Rollback inner database connection.
   * @throws SQLException
   */
  void rollback() throws SQLException;

  /**
   * Close inner database connection.
   * @throws SQLException
   */
  void close() throws SQLException;

  /**
   * Get transaction timeout if set
   * @throws SQLException
   */
  Integer getTimeout() throws SQLException;
  
}


这些方法是不是都很熟悉,就是在一次事务过程中会进行的一些提交....




最后一个对象是 Environment : 也就是环境对象,对于mybatis来说,环境对象就是数据源和事务处理等,如下



public final class Environment {
  private final String id;
  private final TransactionFactory transactionFactory;
  private final DataSource dataSource;

  public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {
    if (id == null) {
      throw new IllegalArgumentException("Parameter 'id' must not be null");
    }
    if (transactionFactory == null) {
        throw new IllegalArgumentException("Parameter 'transactionFactory' must not be null");
    }
    this.id = id;
    if (dataSource == null) {
      throw new IllegalArgumentException("Parameter 'dataSource' must not be null");
    }
    this.transactionFactory = transactionFactory;
    this.dataSource = dataSource;
  }



上面mybatis就用了很多设计模式:比如抽象工厂、建造者模式等




最后再简单看下源码解析,其实源码解析一直都不难


  private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      //当前环境没配置的时候拿默认的
      if (environment == null) {
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        //循环环境,每个环境都有一个id,必须保证不一样即可
        String id = child.getStringAttribute("id");
        //如果是不指定就是上面默认的
        if (isSpecifiedEnvironment(id)) {
          //解析事务工厂
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          //解析数据源工厂
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          //从数据源工厂获取数据源
          DataSource dataSource = dsFactory.getDataSource();
          //使用建造者模式构建环境对象
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          //放到configuration进行管理
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }



下面的子标签就不看了,都是比较简单的,这里主要是学习隔离的设计,mybatis抽象出了数据源工厂,事务工厂,事务对象等等

好了这一篇就到这里,内容看起来挺多,但是源码看懂并不复杂,复杂的是对mybatis的抽象进行理解,程序的美妙之处是在设计上面,而不是单纯的实现

好的设计可以提供更多的扩展性给其他开发者,而且维护起来也会更方便,基本都是在跟接口打交道进行维护的

这才是我们学习的重点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值