Mybatis中mapper是怎么和XMl关联起来的

本文详细分析了Mybatis中mapper是如何与XML文件关联的。从XML文件解析开始,解释了namespace的重要性,接着探讨了Mapper动态代理的创建过程,包括MapperRegistry、MapperProxyFactory和MapperAnnotationBuilder的角色。通过源码解析,阐述了XMLMapperBuilder如何解析mapper标签,构建XML结构,并将解析结果存储在Configuration对象中。最后,介绍了Mapper动态代理的生成,揭示了MapperProxy如何在方法调用时与XML文件中的SQL关联。
摘要由CSDN通过智能技术生成

Mybatis中mapper是怎么和XMl关联起来的

从源码来分析,通过Mybatis的都知道,必须指定nameSpace为Mapper的全限定类名。这样就能关联起来。Mapper的实现肯定是动态dialing,在InvocationHandler中做增强。这里就来分析分析具体是怎么做的? 分析的时候设计的东西多,容易走偏。我尽量回归主题。

1. XML文件解析

这里的xml解析比较繁琐,如果逐行来分析的话,很多很多,这里就挑主线来分析了。之后会分块来分话题来做分析。

解析总的配置文件

如果从经典的Mybatis创建SqlSessionFactory开始,那肯定能看到下面的代码

代码里面的有的注释,是我看源码的时候写的,有的写的比较离谱。有的记录我之前看的时候的困惑。之后看的时候又看懂了。所以就保留在这里了。

  private void parseConfiguration(XNode root) {
    try {
      // issue #117 read properties first
      propertiesElement(root.evalNode("properties")); //解析properties标签,并把他放在 parser和config的 Variables 里面
      Properties settings = settingsAsProperties(root.evalNode("settings"));//加载setting标签
      loadCustomVfs(settings); //lcnote 这里的vfs是啥?怎么用 我知道这个
      //我现在知道他的vfs是什么了,vfs(virtual file system)他抽象出了几个api,通过这些api就可以访问文件系统上的资源;比如在
      // 在解析   mapperElement(root.evalNode("mappers"));的时候,如果指定package,就可以通过VFS来获取包路径下面所有的class文件。
      // 并且会将他添加到mappe里面,和spring中的classPathSacnner一样差不多,可以指定过滤器。


      loadCustomLogImpl(settings);
      typeAliasesElement(root.evalNode("typeAliases"));

      //从这里开始,都是解析具体的标签,new出对象,将标签下面的属性设置进去,
      // 从解析的这里基本也能看出mybatis里面重要的几个点,首先是objectFactory,objectFactory。objectFactory,plugins
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectFactory"));
      reflectorFactoryElement(root.evalNode("objectFactory"));

      //这里就具体设置setting标签了
      settingsElement(settings);

      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));

      //lcnote 这里是重点,解析mapper文件,
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
复制代码

直接看是怎么解析mapper文件的。

从这个代码里面可以看到,mappers标签下面是可以写两种标签。packagemapper标签。对于两种有不同的解析方法。

解析package标签

这里只是截取了部分的源码。还会将好几个源码都拼接在一块,便于看

 if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        }
//下面是 configuration.addMappers(mapperPackage)的方法
public void addMappers(String packageName) {
  //mapperRegistry是一个注册mapper的注册器,并且里面维护了很多的所有的mapper组成的对象。
    mapperRegistry.addMappers(packageName);
  }
//下面是mapperRegistry.addMappers(packageName);
  public void addMappers(String packageName) {
    addMappers(packageName, Object.class);
  }
//    addMappers(packageName, Object.class); 方法,

//packageName表示要扫描的包的路径
//superType表示要找的类是这个类的子类。

  public void addMappers(String packageName, Class<?> superType) 
  {
    //resolverUtil就是一个在指定包下,找指定的类的子类集合的一个工具类。
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    
    //找到了合适的Class,将他添加到mapper里面。
    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
    for (Class<?> mapperClass : mapperSet) {
      addMapper(mapperClass);
    }
  }
//resolverUtil.find(new ResolverUtil.IsA(superType), packageName);

//扫描给定包下(包括子路径下面的所有的类。调用Test方法来匹配,匹配到的class调用getClasses就可以获取的到。)
  public ResolverUtil<T> find(Test test, String packageName) {
    String path = getPackagePath(packageName);

    try {
      List<String> children = VFS.getInstance().list(path);
      for (String child : children) {
        if (child.endsWith(".class")) {
          //加载class对象,调用ResolverUtil里面的静态内部类IsA(实现了Test接口)做匹配。
          addIfMatching(test, child);
        }
      }
    } catch (IOException ioe) {
      log.error("Could not read package: " + packageName, ioe);
    }

    return this;
  }
//addIfMatching(test, child);

  protected void addIfMatching(Test test, String fqn) {
    try {
      String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
      ClassLoader loader = getClassLoader();
      if (log.isDebugEnabled()) {
        log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");
      }
      
      Class<?> type = loader.loadClass(externalName);
      if (test.matches(type)) {
        matches.add((Class<T>) type);
      }
    } catch (Throwable t) {
      log.warn("Could not examine class '" + fqn + "'" + " due to a "
          + t.getClass().getName() + " with message: " + t.getMessage());
    }
  }


//****************************重点***********************************
//   public <T> void addMapper(Class<T> type) 方法,将上面找的,合适的class实例化之后要加载到mapperRegistry里面去。
// 并且这个方法是mapperRegistry里面的。
  public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {//mapper只能注册一次
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        
        // 用mapper new出MapperProxyFactory,放在knownMappers里面
        knownMappers.put(type, new MapperProxyFactory<>(type));
        // It's important that the type is adde
MyBatis配置mapper接口和XML文件的关联,通常需要遵循以下步骤: 1. 创建Mapper接口:首先,你需要创建一个普通的Java接口,这个接口定义了各种你需要执行的数据库操作方法。例如,如果你要操作用户表,可以定义一个名为UserMapper的接口。 2. 编写XML映射文件:接着,你需要编写一个与Mapper接口同名的XML文件,通常放在与Mapper接口相同的包路径下。在这个XML文件,你需要配置具体的SQL语句,并通过`<select>`、`<insert>`、`<update>`、`<delete>`等标签来定义各个方法对应的SQL操作。例如,如果UserMapper接口有一个方法是获取用户信息,那么在UserMapper.xml就会有一个`<select>`标签对应的SQL查询语句。 3. 配置文件关联:为了让MyBatis知道mapper接口和XML文件之间的关系,你需要在MyBatis的全局配置文件(通常是mybatis-config.xml,使用`<mappers>`标签来注册mapper接口的位置。可以通过`<mapper resource="完整路径/UserMapper.xml"/>`来指定XML文件的位置。 4. 使用SqlSessionFactoryBuilder构建SqlSessionFactory:MyBatis使用SqlSessionFactory来构建SqlSession,而SqlSession负责实际的数据库操作。你需要使用SqlSessionFactoryBuilder来根据MyBatis全局配置文件构建SqlSessionFactory。 5. 通过SqlSession获取Mapper代理:通过SqlSession对象,你可以调用getMapper()方法,并传入Mapper接口的Class对象,从而获取到这个接口的动态代理对象。之后就可以直接调用接口方法,MyBatis会根据提供的信息完成SQL的执行。 示例代码如下: **UserMapper.java** ```java public interface UserMapper { User selectUserById(int id); int insertUser(User user); // 其他方法... } ``` **UserMapper.xml** ```xml <mapper namespace="com.example.mapper.UserMapper"> <select id="selectUserById" resultType="com.example.model.User"> SELECT * FROM users WHERE id = #{id} </select> <insert id="insertUser"> INSERT INTO users(name, age) VALUES (#{name}, #{age}) </insert> <!-- 其他SQL配置... --> </mapper> ``` **mybatis-config.xml** ```xml <configuration> <!-- 其他配置... --> <mappers> <mapper resource="com/example/mapper/UserMapper.xml"/> <!-- 其他mapper配置... --> </mappers> </configuration> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值