在我们使用mybatis开发dao层的时候,我们可以直接写好dao层接口,然后借助MapperFactoryBean直接代理,这样我们就不需要显示的实现dao接口,刚接触到这个技术点的时候就对这个MapperFactoryBean十分感兴趣,所以在了解了背后的实现机制时,就写下我的所见所得。
先来看看MapperFactoryBean的继承关系
从类的的继承图中可以看出来实现了FactoryBean,这是一个spring提供的工厂接口,其次继承了SqlSessionDaoSupport,SqlSessionSurpport最顶层实现了InitializingBean,InitializingBean接口定义了afterProper'tiesSet()方法,当一个类实现这个接口,spring容器在初始化一个Bean时,在初始化Bean并设置好相关的属性后就会调用这个方法,我们就可以利用这个接口来实现我们的一些初始化工作。
在DaoSupport中实现afterPropertiesSet()调用了CheckDaoConfig()
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
this.checkDaoConfig();
try {
this.initDao();
} catch (Exception var2) {
throw new BeanInitializationException("Initialization of DAO failed", var2);
}
}
所以在MapperFactoryBean中重写了afterPropertiesSet()方法
protected void checkDaoConfig() {
super.checkDaoConfig();
notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Exception e) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
throw new IllegalArgumentException(e);
} finally {
ErrorContext.instance().reset();
}
}
}
在Configuration类中添加代理的接口,所以在初始化Bean时,spring通过getObject()获得代理的接口。
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
通过SqlSession获得代理类,我们继续在DefaultSqlSession中查看
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
在代码中可以按到代理接口通过SqlSession所对应的Configuration获得,Configuration是一个保存XML文件相关信息的类,与SqlSession绑定。
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
Configuration是保存mybatis中XML的信息,要获得接口的代理需要通过MapperRegistry获得
@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);
}
}
看到这里终于找到了代理的终点,通过MapperProxyFactory类,从类名中就可以看出来这是一个代理工厂,最终mapperProxyFactory.newInstance()获得接口的代理
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
newInstance方法通过new 一个MapperProxy,然后通过JDK中Proxy获得接口的代理,我们知道常用的代理有两种,一种是JDK代理,一种是cglib代理,那么mybatis是使用哪种代理呢?我们可以查看MapperProxy类来看看到底是使用哪种代理
public class MapperProxy<T> implements InvocationHandler, Serializable
从代码中可以明确看出来是使用JDK动态代理的,到这里我们就分析完了mybatis的接口自动代理了。