MapperFactoryBean

配置


生成 DAO 层接口实现类的配置文件使用方式如下:

1
2
3
4
<bean id="accountMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="mySqlSessionFactory"/>
<property name="mapperInterface" value="cn.lizhaoloveit.aop.mapper.AccountMapper"/>
</bean>

对于每个 DAO 层接口,都需要配置一个工厂类 MapperFactoryBean(老套路,Spring 中 FactoryBean,注入对象都是调用 getObject()方法) 需要注入两个参数,一个是被代理的接口,一个是 SqlSessionFactory

初始化


  • mapperInterface : mapper 接口生命,用于生成代理类(JDK 动态代理)
  • sqlSessionFactory : 数据库连接池

SqlSessionDaoSupport 继承自 DaoSupport 实现了 InitializingBean,MapperFactoryBean 初始化回调方法是 DaoSupport#afterPropertiesSet

MapperFactoryBean 重写了父类的回调方法 checkDaoConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected void checkDaoConfig() {
super.checkDaoConfig();

notNull(this.mapperInterface, "Property 'mapperInterface' is required");

// 获取配置元数据对象Configuration
Configuration configuration = getSqlSession().getConfiguration();

// 判断当前mapper声明是否在Configuration中注册
// (mapperRegistry -- Configuration中又一个关键成员变量,
// 用于维护mapper接口声明和代理类的映射)
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {

// 当前mapper声明未在Configuration中注册,则执行注册逻辑
configuration.addMapper(this.mapperInterface);
} catch (Throwable t) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", t);
throw new IllegalArgumentException(t);
} finally {
ErrorContext.instance().reset();
}
}
}

判断 mapper 声明是否在 Configuration 中,也就是主配置文件中,如果在的话,就会执行注册 mapper 的逻辑。

注册 mapper 接口


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 {
// 维护mapper接口声明和代理类的映射,代理类的工厂类为MapperProxyFactory(工厂类的初始化只是设置接口,代理类的生成后面再看)
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.
//以下是使用注解的方式声明sql的解析(目前没使用这种方式,暂不关注)
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}

mapper 是否在缓存中,如果没有,就会使用解析器去解析 mapper。并添加到 knowMappers 中。

生成代理类


1
2
3
4
public T getObject() throws Exception {
//getSqlSession返回为SqlSessionTemplate,详见SqlSessionDaoSupport类
return getSqlSession().getMapper(this.mapperInterface);
}

mapper 只是接口, 实现在 xml 中,需要生成一个 java 对象才能对其进行操作。这个匿名对象就是一个实现了 mapper 接口的动态代理对象。下面是创建代理工厂类。

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
}
}

代理工厂类获取实例。

1
2
3
4
5
6
7
8
9
public T newInstance(SqlSession sqlSession) {
// 代理类的拦截器,重点关注拦截器的回调方法MapperProxy#invoke
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);
}

代理使用拦截器来处理方法调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//目标对象和方法声明对象是同一个,说明不是自动生成的代理类,直接调用目标方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}

//解析、封装被调用方法,并缓存
final MapperMethod mapperMethod = cachedMapperMethod(method);

//执行方法
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}

使用 mapper 代理对象调用接口方法时,会被拦截器拦截,如果目标对象和方法声明对象是一个,说明不是自动生成的代理类,会直接调用方法,否则会解析、封装被调用方法,并缓存

MapperMethod,根据被调用的方法,获取配置元数据对象 Configuration 中的配置信息,决定调用 SqlSession 的哪个方法进行数据库操作。

文章作者: Ammar
文章链接: http://lizhaoloveit.cn/2019/08/01/MapperFactoryBean/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ammar's Blog
打赏
  • 微信
  • 支付宝

评论