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

本文深入解析Mybatis配置文件的objectFactory、plugins、environments和mapper配置,涵盖xml配置、代码验证及结果分析。重点讨论了ObjectFactory的自定义、Plugin的拦截器实现、多环境配置以及mapper.xml的解析和映射。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

 

一、说明

二、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.youkuaiyun.com/wf787283810/article/details/77847576实现

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

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

/**
 * 自定义分页拦截器
 */
@Intercepts(
    {@Signature(
        type = StatementHandler.class,
        method = "prepare",
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值