龙空技术网

Mybatis之Mapper接口的注册过程

小雨在进步 120

前言:

目前大家对“xml文件的命名空间在哪里看”大体比较重视,我们都想要学习一些“xml文件的命名空间在哪里看”的相关资讯。那么小编也在网上汇集了一些关于“xml文件的命名空间在哪里看””的相关文章,希望看官们能喜欢,姐妹们快快来了解一下吧!

Mapper接口用于定义执行SQL语句相关的方法,方法名一般和Mapper XML配置文件中<select|update|delete|insert>标签的id属性相同,接口的完全限定名一般对应Mapper XML配置文件的命名空间。

可以看一下Mapper XML,如下面的UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"        ";><mapper namespace="com.blog4java.mybatis.example.mapper.UserMapper">    <sql id="userAllField">      id,create_time, name, password, phone, nick_name    </sql>    <select id="listAllUser"   resultType="com.blog4java.mybatis.example.entity.UserEntity" >        select        <include refid="userAllField"/>        from user    </select>    <select id="getUserByEntity"  resultType="com.blog4java.mybatis.example.entity.UserEntity">        select        <include refid="userAllField"/>        from user        <where>            <if test="id != null">                AND id = #{id}            </if>            <if test="name != null">                AND name = #{name}            </if>            <if test="phone != null">                AND phone = #{phone}            </if>        </where>    </select>    <select id="getUserByPhone" resultType="com.blog4java.mybatis.example.entity.UserEntity">        select        <include refid="userAllField"/>        from user        where phone = ${phone}    </select></mapper>

UserMapper的代码

public interface UserMapper {    List<UserEntity> listAllUser();    List<UserEntity> getUserByEntity( UserEntity user);    UserEntity getUserByPhone(@Param("phone") String phone);}

看一下如何执行这个UserMapper.xml,参考下面的代码

    // 获取配置文件输入流        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");        // 通过SqlSessionFactoryBuilder的build()方法创建SqlSessionFactory实例        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);        // 调用openSession()方法创建SqlSession实例        SqlSession sqlSession = sqlSessionFactory.openSession();        // 获取UserMapper代理对象        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);        // 执行Mapper方法,获取执行结果        List<UserEntity> userList = userMapper.listAllUser();        System.out.println(JSON.toJSONString(userList));

如上面的代码所示,在创建SqlSession实例后,需要调用SqlSession的getMapper()方法获取一个UserMapper的引用,然后通过该引用调用Mapper接口中定义的方法,UserMapper是一个接口,我们调用SqlSession对象getMapper()返回的到底是什么呢?

我们知道,接口中定义的方法必须通过某个类实现该接口,然后创建该类的实例,才能通过实例调用方法。所以SqlSession对象的getMapper()方法返回的一定是某个类的实例。具体是哪个类的实例呢?实际上getMapper()方法返回的是一个动态代理对象。

我们一步步解析

DefaultSqlSession实现了SqlSession接口,可以直接看一下DefaultSqlSession对getMapper()的具体实现

SqlSession对象getMapper()具体的实现

通过调用configuration类的getMapper获取UserMapper的实列,继续看看getMapper的实现

configuration类的getMapper的实现

configuration类的getMapper的是通过调用mapperRegistry的getMapper方法来实现的

这里的mapperRegistry用于注册Mapper接口信息,建立Mapper接口的Class对象和MapperProxyFactory对象之间的关系,其中MapperProxyFactory对象用于创建Mapper动态代理对象

继续下一步

根据Mapper接口Class对象获取Mapper动态代理对象

Mapper动态代理对象是通过MapperProxyFactory创建的。

重点来了,MapperProxyFactory如何通过动态代理来创建Mapper对象的

MapperProxy使用的是JDK内置的动态代理

MapperProxy使用的是JDK内置的动态代理,实现了InvocationHandler接口,invoke()方法中为通用的拦截逻辑,具体内容在介绍Mapper方法调用过程时再做介绍。使用JDK内置动态代理,通过MapperProxy类实现InvocationHandler接口,定义方法执行拦截逻辑后,还需要调用java.lang.reflect.Proxy类的newProxyInstance()方法创建代理对象。

MyBatis对这一过程做了封装,使用MapperProxyFactory创建Mapper动态代理对象。

MapperProxyFactory类对jdk动态代理做了进一步的封装

如上面的代码所示,MapperProxyFactory类的工厂方法newInstance()是非静态的。也就是说,使用MapperProxyFactory创建Mapper动态代理对象首先需要创建MapperProxyFactory实例。MapperProxyFactory实例是什么时候创建的呢?

Configuration对象中有一个mapperRegistry属性,创建Configuration对象过程中,具体看这篇文章,解析<mappers>标签的时候会调用mapperRegistry中的addMapper(Class<T> type),创建MapperProxyFactory实例。

MyBatis通过mapperRegistry属性注册Mapper接口与MapperProxyFactory对象之间的对应关系。下面是MapperRegistry类的代码

public class MapperRegistry {  // Configuration对象引用  private final Configuration config;  // 用于注册Mapper接口Class对象,和MapperProxyFactory对象对应关系  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();  public MapperRegistry(Configuration config) {    this.config = config;  }  // 根据Mapper接口Class对象获取Mapper动态代理对象  @SuppressWarnings("unchecked")  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);    if (mapperProxyFactory == null) {      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");    }    try {      return mapperProxyFactory.newInstance(sqlSession);    } catch (Exception e) {      throw new BindingException("Error getting mapper instance. Cause: " + e, e);    }  }    public <T> boolean hasMapper(Class<T> type) {    return knownMappers.containsKey(type);  }  // 根据Mapper接口Class对象,创建MapperProxyFactory对象,并注册到knownMappers属性中  public <T> void addMapper(Class<T> type) {    if (type.isInterface()) {      if (hasMapper(type)) {        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");      }      boolean loadCompleted = false;      try {        knownMappers.put(type, new MapperProxyFactory<T>(type));        // It's important that the type is added before the parser is run        // otherwise the binding may automatically be attempted by the        // mapper parser. If the type is already known, it won't try.        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);        parser.parse();        loadCompleted = true;      } finally {        if (!loadCompleted) {          knownMappers.remove(type);        }      }    }  }  /**   * @since 3.2.2   */  public Collection<Class<?>> getMappers() {    return Collections.unmodifiableCollection(knownMappers.keySet());  }  /**   * @since 3.2.2   */  public void addMappers(String packageName, Class<?> superType) {    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();    for (Class<?> mapperClass : mapperSet) {      addMapper(mapperClass);    }  }  /**   * @since 3.2.2   */  public void addMappers(String packageName) {    addMappers(packageName, Object.class);  }  }

如上面的代码所示,MapperRegistry类有一个knownMappers属性,用于注册Mapper接口对应的Class对象和MapperProxyFactory对象之间的关系。另外,MapperRegistry提供了addMapper()方法,用于向knownMappers属性中注册Mapper接口信息。在addMapper()方法中,为每个Mapper接口对应的Class对象创建一个MapperProxyFactory对象,然后添加到knownMappers属性中。

MapperRegistry还提供了getMapper()方法,能够根据Mapper接口的Class对象获取对应的MapperProxyFactory对象,然后就可以使用MapperProxyFactory对象创建Mapper动态代理对象了。

MyBatis框架在应用启动时会解析所有的Mapper接口,然后调用MapperRegistry对象的addMapper()方法将Mapper接口信息和对应的MapperProxyFactory对象注册到MapperRegistry对象中。

标签: #xml文件的命名空间在哪里看