在通过MyBatis
操作数据库之前我们一定先通过Session
对象获取指定Mappper
接口的代理对象。如下代码所示:
public class UserMapper{
@Select(value="SELECT * FROM user")
public List<User> findAll();
}
public static void main(String [] args){
Configuration configuration = new Configuration();
configuration.addMapper(UserMapper.class);
SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(configuration);
// 获取SqlSession对象
try(SqlSession sqlSession = sqlSessionFactory.openSession();){
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.findAll();
}catch(Exception e){
//TODO 异常信息
}
}
调用链
1.DefaultSqlSession.调用getMapper方法 -> 2.Configuration.调用getMapper方法 -> 3.MapperRegistry调用getMapper方法
|
|
|-------------------------------------------------------------------------------------|
|
|
|-->4.判断knownMappers缓存是否已经解析过指定的Mapper接口,没解析过抛错(在调用addMapper方法时就将接口添加到缓存中并对接口中的方法进行解析).解析过创建代理对象
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (Objects.isNull(mapperProxyFactory)){
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);
}
}
|
|
|--> 5.创建代理对象,MapperProxy实现了InvocationHandler接口,使用JDK动态代理创建
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
// 用JDK自带的动态代理生成映射器
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}
注意第5代逻辑:
// 已解析过的Mapper接口及对应的代理工厂缓存
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (Objects.isNull(mapperProxyFactory)) {
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> void addMapper(Class<T> type) {
// mapper必须是接口!才会添加
if (type.isInterface()) {
if ( knownMappers.containsKey((type)) {
// 如果重复添加了,报错
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 将mapper接口放入缓存并为其创建独立的mapper接口代理对象工厂
// 每个Mapper接口有一个独立的代理工厂
knownMappers.put(type, new MapperProxyFactory<T>(type));
// 在运行分析器之前添加类型非常重要
// 否则,绑定可能会由
// 映射器解析器。如果类型已知,则不会尝试。
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
// 解析mapper接口及相应的mapper.xml文件
parser.parse();
loadCompleted = true;
} finally {
// 如果加载过程中出现异常需要再将这个mapper从mybatis中删除,这种方式比较丑陋吧,难道是不得已而为之?
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
在注册Mapper
接口时,会向缓存中添加接口对应的代理工厂,以便后面通过接口获取代理对象调用方法。