Mybatis源码解析之三 配置文件子节点作用详解2

目录

 

一、说明

二、objectFactory配置

         1、xml配置

   3、结果验证

二、plugins配置

     1、plugins配置

     2、解析分析

3、代码验证

  二、environments配置

     1、environments 节点配置

     2、代码分析

 三、mapper配置

 1、mapper.xml文件详解  

 2、mapper.xml文件解析

 2.1、解析mapper.xml配置

 2.2、添加资源

      


一、说明

       我们在Mybatis源码解析之二 配置文件子节点作用详解 主要讲解了Mybatis配置节点中的properties、settings、typeAliases、typeHandlers这四个节点,下面这篇博文我们来介绍余下的objectFactory、plugins、environments、mappers这些节点

二、objectFactory配置

         1、xml配置

Mybatis的官方描述:MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。mybatis给我们提供了一个DefaultObjectFactory对象来实现上述行为, 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现(继承DefaultObjectFactory)。

 自己的解释:在使用Mybatis查询的过程中获取的结果对象ResultSet数据通过该对象ObjectFactory的create()方法,要么通过默认构造方法(没有相关的构造器参数),要么在参数映射存在的时候通过参数构造方法来实例化Bean对象,并将结果转换存放在其初始化的bean中,多个可能涉及到集合类的初始化,举例来说我们有一个Book表,从其中查询出来book列表,mybatis查询出来的是List<Book> 则是通过ObjectFacroty会先创建List实例bean(ArrayList),并创建多个Book实例化对象,最终将ResultSet结果信息保存到实力化的List<Book>中。

	<!--设置ObjectFactory对象 -->
	<objectFactory type="com.soecode.lyf.mybatisConfig.MyObjectFactory">
		<!-- 设置相关的属性 -->
		<property name="name" value="xieqx" />
	</objectFactory>

    自定义的ObjectFactory对象,该对象的功能是对查询出来的结果信息Book进行标记mark为true(表示查询过标记为ture)

/**
 * 设置自己的自定义的ObjectFactory
 * 继承DefaultObjectFactory 重写其中的create()方法 
 */
public class MyObjectFactory extends DefaultObjectFactory {

    @Override
    public <T> T create(Class<T> type) {
        T t = super.create(type);

        //针对其中查询出来的Book 设置mark为true(没啥实际的作用只是为了验证功能结果)
        if(t instanceof Book){
            Book book = (Book) t;
            book.setMark(true);
        }
        return  t;
    }
   //创建实例化对象的核心方法
   //根据class信息和构造器类型constructorArgTypes 构造器参数 constructorArgs
   @Override
    public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return super.create(type, constructorArgTypes, constructorArgs);
    }
//设置相关的properties 对应<ObjectFactory> 下的<property />
@Override
    public void setProperties(Properties properties) {
        super.setProperties(properties);
    }
}

2、代码解析

//注册ObjectFactory 
private void objectFactoryElement(XNode context) throws Exception {
    if (context != null) {
      //解析<ObjectFactory> 下的type属性
      String type = context.getStringAttribute("type");
      //获取其中的properties子节点
      Properties properties = context.getChildrenAsProperties();
      //反射创建对象,并设置其中配置的属性
      ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
      factory.setProperties(properties);
      //配置到Configuration
      configuration.setObjectFactory(factory);
    }
  }

   3、结果验证

二、plugins配置

     1、plugins配置

       mybatis中配置的plugins其实就是实现了Interceptor接口的实现类,其实说白了就是拦截器,使用动态代理增强的形式在sql执行过程中动态的拦截方法增强其功能(例如给普通sql增加分页功能)

<!-- 设置拦截器 进行sql语句添加分页查询 -->
	<plugins>
		<plugin interceptor="com.github.pagehelper.PageInterceptor" />
	</plugins>

 这里以PageInterceptor为例子做介绍,PageInterceptor是第三方为mybatis提供的分页插件,我们通过简要了解该类来理解mybatis的plugins配置

先看一下Interceptor接口

public interface Interceptor {
  // 拦截逻辑,参数是代理类
  Object intercept(Invocation invocation) throws Throwable;
  // 加载插件,一般使用Plugin.wrap(target, this);加载当前插件
  Object plugin(Object target);
  // 初始化属性
  void setProperties(Properties properties);
}

PageInterceptor实现类 在实现Interceptor 后还需要使用@Intercepts和@Signature来指定拦截器需要拦截的目标(类、方法、参数)

@Signature(
           type = Executor.class, 拦截的类只能是: StatementHandler | ParameterHandler | ResultSetHandler | Executor 类或者子类
           method = "query",  拦截的类中的方法
            args = { //对于重载的方法,需要使用这里的参数类型唯一定位方法

                    MappedStatement.class,

                   Object.class,

                   RowBounds.class,

                   ResultHandler.class}

对于需要拦截的多个方法可以在@Intercepts中添加多个@Signature注解(每一个注解则指定一个拦截的方法),我们可以看到分页拦截器拦截的方法为

   Executor的query(MapperStatement,object,RowBounds,ResultHandler)方法

   Executor的query(MapperStatement,object,RowBounds,ResultHandler)方法 记性相应的操作

 @Intercepts({@Signature(
            type = Executor.class,
            method = "query",
            args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
    ), @Signature(
            type = Executor.class,
            method = "query",
            args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
    )})
    public class PageInterceptor implements Interceptor {
        protected Cache<String, MappedStatement> msCountMap = null;
        private Dialect dialect;
        private String default_dialect_class = "com.github.pagehelper.PageHelper";
        private Field additionalParametersField;
        private String countSuffix = "_COUNT";

    
        public Object intercept(Invocation invocation) throws Throwable {
           具体实现....
        }

    }

     2、解析分析

  /**
     * 解析配置中的plugins节点 
     */
    private void pluginElement(XNode parent) throws Exception {
        if (parent != null) {
            //遍历<plugins>下的所有子节点<plugin>
            for (XNode child : parent.getChildren()) {
                //获取节点plugin的interceptor属性
                String interceptor = child.getStringAttribute("interceptor");
                //获取节点下的properties
                Properties properties = child.getChildrenAsProperties();
                //根据interceptor获取其中的name(全限定类名)反射实例化拦截器对象
                Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
                //为实例化后的拦截器对象设置相关的属性
                interceptorInstance.setProperties(properties);
                //将生成并完善后的拦截器对象添加到configuration对象
                configuration.addInterceptor(interceptorInstance);
            }
        }
    }

3、代码验证

       自定义实现了相关的分页拦截器MyPageInterceptor类

       参考:https://blog.csdn.net/wf787283810/article/details/77847576实现

      执行相关的拦截器
          这个方法是实际的拦截逻辑,我们的目的是在这里来实现分页
         从当前线程中获取分页对象Page
            1、首先获取其中查询出来的总记录数填充到Page
            2、对普通的sql添加分页信息

  在该方法中针对StatementHandler的prepare方法(预编译sql)执行该拦截器,在拦截方法中进行分页处理

/**
 * 自定义分页拦截器
 */
@Intercepts(
    {@Signature(
        type = StatementHandler.class,
        method = "prepare",
        args = {
                Connection.class
        })
    }
)
public class MyPageInterceptor implements Interceptor  {
    private static final String PAGE_PREFIX = "select";
    /**
     * 执行相关的拦截器
     * 这个方法是实际的拦截逻辑,我们的目的是在这里来实现分页
     * 从当前线程中获取分页对象Page
     * 1、首先获取其中查询出来的总记录数填充到Page
     * 2、对普通的sql添加分页信息
     * @param invocation
     * @return
     * @throws Throwable
     */
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler ha
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Mybatis是一个轻量级的Java持久层开源框架,它封装了JDBC操作数据库的底层细节,提供了一个简单易用的数据库访问方式。 Mybatis源码分为核心模块和附加模块两部分,核心模块主要包括配置解析、SQL解析、SQL执行等功能,附加模块包括连接池、缓存、事务管理等功能。 在Mybatis源码中,配置解析是其中的关键部分。通过解析mybatis-config.xml配置文件,可以获取到数据库连接信息、映射器配置、插件配置等。在配置解析过程中,Mybatis会对配置文件进行校验,确保配置的正确性。 SQL解析Mybatis的另一个重要功能。Mybatis通过解析Mapper接口中的注解或XML配置文件中的SQL语句,将SQL语句解析为ParameterMapping、BoundSql等对象,并将其封装成一个MappedStatement对象,供后续的SQL执行使用。 SQL执行是Mybatis的核心功能之一。在SQL执行阶段,Mybatis会根据MappedStatement中的信息,获取数据库连接,并执行对应的SQL语句。在执行过程中,Mybatis会通过TypeHandler对参数进行类型转换,并使用ResultSetHandler将查询结果封装成Java对象。 除了核心模块,Mybatis源码还包括了连接池、缓存、事务管理等附加模块的实现。连接池模块负责管理数据库连接的获取和释放,缓存模块负责缓存查询结果以提高性能,而事务管理模块则负责管理数据库的事务处理。 总之,Mybatis源码解析涉及多个关键模块的实现,包括配置解析、SQL解析、SQL执行、连接池、缓存、事务管理等。通过了解这些模块的实现原理,我们可以更好地理解和使用Mybatis框架。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值