MyBatis好好学二(重要对象、注解和源码分析)

MyBatis好好学二(重要对象、注解和源码分析)

大纲

  • 1、MyBatis中的重要对象

  • 2、MyBatis中的重要注解

  • 3、MyBatis源码分析

1、MyBatis重要对象

        MyBatis完成一次数据库操作需要经过的步骤,如下:

1、加载配置文件 2、获取SqlSessionFactoryBuiler对象 3、通过SqlSessionFactoryBuiler和配置文件流来获取SqlSessionFactory对象 4、利用SqlSessionFactory对象来打开一个SqlSession 5、通过SqlSession来获得对应的Mapper对象 6、通过Mapper对象调用对应接口来查询数据库 从这些步骤我们可以看到,MyBatic完成一次数据库操作主要有4大核心对象:

SqlSessionFactoryBuiler,SqlSessionFactory,SqlSession、Mapper。

  • SqlSessionFactoryBuiler

SqlSessionFactoryBuilder的唯一作用就是用来创建SqlSessionFactory,创建完成之后就不会用到它了,
所以SqlSessionFactoryBuiler生命周期极短。

SqlSessionFactoryBuiler中提供了9个方法,返回的都是SqlSessionFactory对象。

SqlSessionFactoryBuiler内使用到了建造者模式.
  • SqlSessionFactory

SqlSessionFactory是一个接口,默认的实现类,主要是用来生成SqlSession对象,而SqlSession对象是需要不断被创建的,所以SqlSessionFactory是全局都存在的,也没有必要重复创建,所以这是一个单例对象。

SqlSessionFactory看名字就可以很容易想到,用到的是工厂设计模式。

SqlSessionFactory只有两个实现类:DefaultSqlSessionFactory和SqlSessionManager。
  • SqlSession
SqlSession是用来操作xml文件中我们写好的sql语句,每次操作数据库我们都需要一个SqlSession对象,SqlSession是用来和数据库中的事务进行对接的,所以SqlSession里面是包含了事务隔离级别等信息的。

SqlSession实例是线程不安全的,故最佳的请求范围是请求(request)或者方法(method)。

SqlSession也是一个接口,有两个实现类:DefaultSqlSession和SqlSessionManager。
  • Mapper

Mapper是一个接口,没有任何实现类。主要作用就是用来映射Sql语句的接口,映射器的接口实例从SqlSession对象中获取,所以说Mapper实例作用域是和SqlSession相同或者更小。
Mapper实例的作用范围最好是保持在方法范围,否则会难以管理。

Mapper接口的名称要和对应sql语句的xml文件同名,Mapper接口中定义的方法名称对应了xml文件中的语句id.
  • SqlSessionFactoryBuilder
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

build方法中会调用生成一个XMLConfigBuilder对象:
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);  
该对象中调用parser.parse()返回一个Configuration对象。
  • 四大核心对象生命周期
SqlSessionFactoryBuiler只需要在创建SqlSessionFactory对象的时候使用,创建完成之后即可被丢弃。

SqlSessionFactory全局唯一,是一个单例对象,但是需要全局存在。

SqlSession一般对应了一个request,

Mapper一般控制在方法内
1、MyBatis四大内置对象
  • ParameterHandler:处理SQL的参数对象
  • ResultSetHandler:处理SQL的返回结果集
  • StatementHandler:数据库的处理对象,用于执行SQL语句
  • Executor:MyBatis的执行器,用于执行增删改查操作

2、MyBatis中的重要注解

  • @Select
作用:标记查询语句。
@Select用于标记查询语句。该注解可以在接口方法上使用,也可以在XML文件中使用。使用@Select注解时,需要在注解中指定SQL语句。
示例:
@Select("SELECT * FROM users WHERE id = #{id}")
User getUserById(@Param("id") Long id);
  • @Insert
作用:标记插入语句。
@Insert用于标记插入语句。该注解可以在接口方法上使用,也可以在XML文件中使用。使用@Insert注解时,需要在注解中指定SQL语句。
示例:
@Insert("INSERT INTO users(name, age) VALUES(#{name}, #{age})")
int addUser(User user);
  • @Update
作用:标记更新语句。
@Update用于标记更新语句。该注解可以在接口方法上使用,也可以在XML文件中使用。使用@Update注解时,需要在注解中指定SQL语句。

示例:
@Update("UPDATE users SET name = #{name}, age = #{age} WHERE id = #{id}")
int updateUser(User user);
  • @Delete
作用:标记删除语句。
@Delete用于标记删除语句。该注解可以在接口方法上使用,也可以在XML文件中使用。使用@Delete注解时,需要在注解中指定SQL语句。

示例:
@Delete("DELETE FROM users WHERE id = #{id}")
int deleteUserById(@Param("id") Long id);
  • @Results
作用:用于指定多个@Result注解。

@Results用于标记结果集映射,该注解可以用于接口方法或XML文件中,通常与@Select注解一起使用。使用@Results注解时,需要指定映射规则。

示例:
@Select("SELECT * FROM users WHERE id = #{id}")
@Results(id = "userResultMap", value = {
    @Result(property = "id", column = "id"),
    @Result(property = "name", column = "name"),
    @Result(property = "age", column = "age")
})
User getUserById(@Param("id") Long id); 

-- @Result
作用:用于指定查询结果集的映射关系。
@Result用于标记单个属性与结果集中的列之间的映射关系。该注解可以用于接口方法或XML文件中,
通常与@Results注解一起使用。使用@Result注解时,需要指定映射规则。
  • @ResultMap
作用:用于指定查询结果集的映射关系。

@ResultMap用于标记结果集映射规则。该注解可以用于接口方法或XML文件中,通常与@Select注解一起使用。使用@ResultMap注解时,需要指定映射规则。

示例:
@Select("SELECT * FROM users WHERE id = #{id}")
@ResultMap("userResultMap")
User getUserById(@Param("id") Long id);
  • @Options
作用:用于指定插入语句的选项。

@Options用于指定一些可选的配置项。该注解可以用于接口方法或XML文件中,通常与@Insert、@Update、@Delete等注解一起使用。使用@Options注解时,可以指定一些可选的配置项。

示例:
@Insert("INSERT into users(name, age) VALUES(#{name}, #{age})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insertUser(User user); 
  • @SelectKey
作用:用于指定查询语句的主键生成方式。

@SelectKey用于在执行INSERT语句后获取自动生成的主键值。该注解可以用于接口方法或XML文件中,通常与@Insert注解一起使用。
使用@SelectKey注解时,需要指定生成主键的SQL语句和将主键值赋给Java对象的哪个属性。

示例:
@Insert("INSERT INTO users(name, age) VALUES(#{name}, #{age})")
@SelectKey(statement = "SELECT LAST_INSERT_ID()", keyProperty = "id", before = false, resultType = Long.class)
int insertUser(User user);
  • @Param
作用:用于指定方法参数名称。
@Param用于为SQL语句中的参数指定参数名称。该注解可以用于接口方法或XML文件中,通常与@Select、@Insert、@Update、@Delete等注解一起使用。使用@Param注解时,需要指定参数名称。

示例:
@Select("SELECT * FROM users WHERE name = #{name} AND age = #{age}")
List<User> getUsersByNameAndAge(@Param("name") String name, @Param("age") Integer age);
  • @One
作用:用于指定一对一关联关系。

@One用于在一对一关联查询中指定查询结果的映射方式。该注解可以用于XML文件中,通常与和标签一起使用。使用@One注解时,需要指定查询结果映射的Java对象类型和查询结果映射的属性。

<resultMap id="userResultMap" type="User">
  <id column="id" property="id"/>
  <result column="name" property="name"/>
  <result column="age" property="age"/>
  <association property="department" resultMap="departmentResultMap"/>
</resultMap>


<resultMap id="departmentResultMap" type="Department">
  <id column="id" property="id"/>
  <result column="name" property="name"/>
  <result column="description" property="description"/>
  <one property="manager" resultMap="userResultMap"/>
/resultMap>

        上述代码中,@One注解用于指定查询结果的映射方式,这里使用了嵌套的标签实现了一对一关联查询。在departmentResultMap中,使用@One注解指定了查询结果映射的Java对象类型为User,查询结果映射的属性为manager,resultMap参数指定了查询结果映射的结果集映射规则为userResultMap。

        除了使用@One注解之外,还可以使用@Many注解来指定一对多关联查询的映射方式。总之,@One注解是MyBatis中用于在一对一关联查询中指定查询结果的映射方式的注解之一,可以方便地实现一对一关联查询的结果映射。

  • @Many

        作用:用于指定一对多关联关系。

        @Many用于在一对多关联查询中指定查询结果的映射方式。该注解可以用于XML文件中,通常与和标签一起使用。使用@Many注解时,需要指定查询结果映射的Java对象类型和查询结果映射的属性。

示例:

<resultMap id="departmentResultMap" type="Department">
  <id column="id" property="id"/>
  <result column="name" property="name"/>
  <result column="description" property="description"/>
  <collection property="members" ofType="User" resultMap="userResultMap"/>
</resultMap>


<resultMap id="userResultMap" type="User">
  <id column="id" property="id"/>
  <result column="name" property="name"/>
  <result column="age" property="age"/>
</resultMap>

        上述代码中,@Many注解用于指定查询结果的映射方式,这里使用了嵌套的标签实现了一对多关联查询。在departmentResultMap中,使用@Many注解指定了查询结果映射的Java对象类型为User,查询结果映射的属性为members,ofType参数指定了集合中元素的类型为User,resultMap参数指定了查询结果映射的结果集映射规则为userResultMap。

        除了使用@Many注解之外,还可以使用@One注解来指定一对一关联查询的映射方式。总之,@Many注解是MyBatis中用于在一对多关联查询中指定查询结果的映射方式的注解之一,可以方便地实现一对多关联查询的结果映射。

  • @ResultType

    作用:用于指定查询结果集的类型。

    @ResultType用于指定查询结果的类型。该注解可以用于接口方法或XML文件中,通常与@Select、@Insert、@Update、@Delete等注解一起使用。使用@ResultType注解时,需要指定查询结果的类型。

    示例:

    @Select("SELECT name, age FROM users WHERE id = #{id}")
    @ResultType(User.class)
    User getUserById(Long id);
    
  • @TypeDiscriminator

    作用:用于指定类型鉴别器,用于根据查询结果集的不同类型映射到不同的Java对象。

    @TypeDiscriminator用于在自动映射时指定不同子类型的映射方式。该注解可以用于XML文件中,通常与和标签一起使用。使用@TypeDiscriminator注解时,需要指定类型列的名称和不同子类型的映射方式。

  • @ConstructorArgs

    作用:用于指定Java对象的构造方法参数。

    @ConstructorArgs用于指定查询结果映射到Java对象时使用的构造函数和构造函数参数。该注解可以用于XML文件中,通常与标签一起使用。使用@ConstructorArgs注解时,需要指定构造函数参数的映射关系。

    示例:

    <resultMap id="userResultMap" type="User">
      <id column="id" property="id"/>
      <constructor>
        <arg column="name" javaType="String"/>
        <arg column="age" javaType="int"/>
      </constructor>
    </resultMap>
    
  • @Arg

    作用:用于指定Java对象的构造方法参数。

    @Arg用于指定查询结果映射到Java对象时构造函数或工厂方法的参数映射关系。该注解可以用于接口方法或XML文件中,通常与@Select、@Insert、@Update、@Delete等注解一起使用。使用@Arg注解时,需要指定参数的映射关系。

    示例:

    @Select("SELECT name, age FROM users WHERE id = #{id}")
    User getUserById(@Arg("name") String name, @Arg("age") int age);
    
  • @Discriminator

    作用:用于指定类型鉴别器的查询结果。

    @Discriminator用于在自动映射时指定不同子类型的映射方式。该注解可以用于接口方法或XML文件中,通常与@Select、@Insert、@Update、@Delete等注解一起使用。使用@Discriminator注解时,需要指定类型列的名称和不同子类型的映射方式。

    示例:

    @Select("SELECT * FROM vehicle WHERE type = #{type}")
    @Discriminator(column = "type", javaType = String.class, cases = {
      @Case(value = "car", type = Car.class),
      @Case(value = "truck", type = Truck.class),
      @Case(value = "bus", type = Bus.class)
    })
    List<Vehicle> getVehiclesByType(String type);
    
  • @CacheNamespace

    作用:用于指定缓存的命名空间。

    @CacheNamespace用于指定Mapper接口中的查询结果是否进行缓存。该注解可以用于Mapper接口上,用于指定Mapper接口中所有方法默认的缓存配置。使用@CacheNamespace注解时,需要指定缓存配置的属性。

    示例:

    @CacheNamespace(
      implementation = MyBatisRedisCache.class,
      eviction = MyBatisRedisCache.Eviction.LRU,
      flushInterval = 60000,
      size = 10000,
      readWrite = true,
      blocking = true
    )
    public interface UserMapper {
      @Select("SELECT * FROM users WHERE id = #{id}")
      User getUserById(Long id);
      // ...
    }
    
  • @Flush

作用:用于在插入、更新或删除操作之后自动清空缓存。

@Flush是用于在Mapper接口中指定在执行方法前或方法后刷新缓存。该注解可以用于Mapper接口方法上,通常与@Select、@Insert、@Update、@Delete等注解一起使用。使用@Flush注解时,需要指定刷新缓存的时机。

示例:

@Select("SELECT * FROM users WHERE id = #{id}")
@Flush(flushCache = FetchType.AFTER)
User getUserById(Long id);
  • @MappedJdbcTypes

    作用:用于指定Java对象属性与数据库列的映射关系。

    @MappedJdbcTypes用于将Java类型映射到JDBC类型。该注解可以用于JavaBean属性或ResultMap中,用于指定Java类型对应的JDBC类型。使用@MappedJdbcTypes注解时,需要指定Java类型和对应的JDBC类型。

    示例:

    public class User {
      private Long id;
      @MappedJdbcTypes(JdbcType.VARCHAR)
      private String name;
      private Integer age;
      // ...
    }
    
  • @MappedTypes

    作用:用于指定Java对象与数据库类型的映射关系。

    @MappedTypes用于将Java类型映射到JDBC类型。该注解可以用于JavaBean属性或ResultMap中,用于指定Java类型对应的JDBC类型。使用@MappedTypes注解时,需要指定Java类型。

    示例:

    @MappedTypes(User.class)
    public interface UserMapper {
      @Select("SELECT * FROM users WHERE id = #{id}")
      User getUserById(Long id);
      // ...
    }
    
  • @SelectProvider

    作用:用于指定动态生成SQL语句的提供者。

    @SelectProvider是用于在Mapper接口中动态生成查询SQL语句。该注解可以用于Mapper接口方法上,用于指定一个提供SQL语句的Provider类。使用@SelectProvider注解时,需要指定Provider类和Provider方法。

    示例:

    @SelectProvider(type = UserSqlProvider.class, method = "getUserByIdSql")
    User getUserById(Long id);
    
  • @InsertProvider

    作用:用于指定动态生成SQL语句的提供者。

    @InsertProvider用于在Mapper接口中动态生成插入SQL语句。该注解可以用于Mapper接口方法上,用于指定一个提供SQL语句的Provider类。使用@InsertProvider注解时,需要指定Provider类和Provider方法。

    示例:

    @InsertProvider(type = UserSqlProvider.class, method = "insertUserSql")
    int insertUser(User user);
    
  • @UpdateProvider

作用:用于指定动态生成SQL语句的提供者。

@UpdateProvider用于在Mapper接口中动态生成更新SQL语句。该注解可以用于Mapper接口方法上,用于指定一个提供SQL语句的Provider类。使用@UpdateProvider注解时,需要指定Provider类和Provider方法。

示例:

@UpdateProvider(type = UserSqlProvider.class, method = "updateUserSql")
int updateUser(User user);
  • @DeleteProvider

    作用:用于指定动态生成SQL语句的提供者。

    @DeleteProvider用于在Mapper接口中动态生成删除SQL语句。该注解可以用于Mapper接口方法上,用于指定一个提供SQL语句的Provider类。使用@DeleteProvider注解时,需要指定Provider类和Provider方法。

    示例:

    @DeleteProvider(type = UserSqlProvider.class, method = "deleteUserSql")
    int deleteUser(Long id);
    

3、MyBatis源码分析

1、源码分析流程图

2、源码分析流程
  • 读取resources获取对应的InputStream
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
        InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
        if (in == null) {
            throw new IOException("Could not find resource " + resource);
        } else {
            return in;
        }
    }
  • 使用SqlSessionFactoryBuilder获取SqlSessionFactory

SqlSessionFactoryBuilder 通过XMLConfigBuilder 去解析我们传入的mybatis的配置文件

//  第一步
public SqlSessionFactory build(InputStream inputStream) {
        return this.build((InputStream)inputStream, (String)null, (Properties)null);
    }
// 第二步
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            var5 = this.build(parser.parse());
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
        } finally {
            ErrorContext.instance().reset();

            try {
                inputStream.close();
            } catch (IOException var13) {
            }

        }

        return var5;
    }
  • XMLConfigBuilder 部分源码
// 第一步 
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
        this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
// 第二步 
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
        super(new Configuration());
        this.localReflectorFactory = new DefaultReflectorFactory();
        ErrorContext.instance().resource("SQL Mapper Configuration");
        this.configuration.setVariables(props);
        this.parsed = false;
        this.environment = environment;
        this.parser = parser;
    }

返回parser,调用 build(parser.parse())这个方法去解析配置文件内容,我们去看看parse()方法源码。

public Configuration parse() {
        if (this.parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        } else { 
            // 防止多次调用
            this.parsed = true;
            this.parseConfiguration(this.parser.evalNode("/configuration"));
            return this.configuration;
        }
    }

解析配置:parseConfiguration(parseConfiguration)

 private void parseConfiguration(XNode root) {
        try {
            this.propertiesElement(root.evalNode("properties"));
            Properties settings = this.settingsAsProperties(root.evalNode("settings"));
            this.loadCustomVfs(settings);
            this.loadCustomLogImpl(settings);
            this.typeAliasesElement(root.evalNode("typeAliases"));
            this.pluginElement(root.evalNode("plugins"));
            this.objectFactoryElement(root.evalNode("objectFactory"));
            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
            this.settingsElement(settings);
            this.environmentsElement(root.evalNode("environments"));
            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            this.typeHandlerElement(root.evalNode("typeHandlers"));
            this.mapperElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }

通过以上源码,我们就能看出,在mybatis的配置文件中:

  1. configuration节点为根节点。

  2. 在configuration节点之下,我们可以配置10个子节点, 分别为:properties、typeAliases、plugins、objectFactory、objectWrapperFactory、settings、environments、databaseIdProvider、typeHandlers、mappers。

解析配置文件完成了之后,都会装配到configuration

Configuration作用:mybatis核心的配置文件内容 ,使用xml转换bean

  • 解析配置文件中配置的mappers: 进入这个方法源码看下如何解析的:mapperElement(root.evalNode("mappers"));
private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
            Iterator var2 = parent.getChildren().iterator();

            while(true) {
                while(var2.hasNext()) {
                    XNode child = (XNode)var2.next();
                    String resource;
                    if ("package".equals(child.getName())) {
                        resource = child.getStringAttribute("name");
                        this.configuration.addMappers(resource);
                    } else {
                        resource = child.getStringAttribute("resource");
                        String url = child.getStringAttribute("url");
                        String mapperClass = child.getStringAttribute("class");
                        XMLMapperBuilder mapperParser;
                        InputStream inputStream;
                        if (resource != null && url == null && mapperClass == null) {
                            ErrorContext.instance().resource(resource);
                            inputStream = Resources.getResourceAsStream(resource);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else if (resource == null && url != null && mapperClass == null) {
                            ErrorContext.instance().resource(url);
                            inputStream = Resources.getUrlAsStream(url);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else {
                            if (resource != null || url != null || mapperClass == null) {
                                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                            }

                            Class<?> mapperInterface = Resources.classForName(mapperClass);
                            this.configuration.addMapper(mapperInterface);
                        }
                    }
                }

                return;
            }
        }
    }

mapperElement方法就是将解析到的mapper,调用addMappers方法添加mapper。

  public void addMappers(String packageName) {
        this.mapperRegistry.addMappers(packageName);
    }
  • MapperRegistry 类部分代码

        MapperRegistry类里面封装了一个map集合,用来存放mappers接口

public class MapperRegistry {
    private final Configuration config;
    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap(); 
    
    public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {
            if (this.hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }

            boolean loadCompleted = false;

            try {
                this.knownMappers.put(type, new MapperProxyFactory(type));
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
                parser.parse();
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    this.knownMappers.remove(type);
                }

            }
        }

    }
}

        通过上面的源码分析我们可以知道,使用map集合来装接口:再用configuration来接受配置文件所有信息。

        最后,回到build方法:

-- SqlSessionFactoryBuilder的build方法
public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
} 

-- DefaultSqlSessionFactory 类部分代码
public class DefaultSqlSessionFactory implements SqlSessionFactory {
    private final Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    public SqlSession openSession() {
        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
    } 
}

        通过SqlSessionFactoryBuilder类拿到SqlSessionFactory对象,SqlSessionFactory对象调用openSession方法即可拿到SqlSession对象,然后可以通过SqlSession拿到Configuration对象,从而可以拿到装在的配置和mapper。

  • 操作Mapper接口
-- 获取mapper接口
TsCompareDetailLogMapper tsCompareDetailLogMapper = sqlSession.getMapper(TsCompareDetailLogMapper.class); 


-- 最后底层是调用MapperRegistry类的getMapper方法获取对应mapper接口
public class MapperRegistry {
    private final Configuration config;
    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();

    public MapperRegistry(Configuration config) {
        this.config = config;
    }

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    } 
} 

-- MapperProxyFactory通过Java动态代理拿到mapper接口。
public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Class<T> getMapperInterface() {
        return this.mapperInterface;
    }

    public Map<Method, MapperMethodInvoker> getMethodCache() {
        return this.methodCache;
    }

    protected T newInstance(MapperProxy<T> mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

    public T newInstance(SqlSession sqlSession) {
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }
}

通过上述源码分析,我们知道了Mapper接口绑定原理(代理设计模式)

1.获取本地InputStream对象(mybatis配置文件)

2.调用SqlSessionFactoryBuilder

再使用XMLConfigBuilder解析mybatis配置文件,装配到Configuration中。
将配置文件中的Mapper添加到Configuration mapperRegistry实现注册。
备注:mapperRegistry存放当前所有的mapper文件。

3.使用configuration获取默认的DefaultSqlSessionFactory 
end
  • 作者:旭仔(联系作者)
  • 发表时间:2024-03-10 20:37
  • 版权声明:自由转载-非商用-非衍生-保持署名
  • 转载声明:如果是转载栈主转载的文章,请附上原文链接
  • 公众号转载:请在文末添加作者公众号二维码(公众号二维码见右边,欢迎关注)
  • 评论