mybatis+SpringMVC中mybatis实例化接口Dao原理
今天早上有个疑惑,在mybatis+SPRingMVC的工程中,我只定义了一个接口UserMapper,而spring就产生了一个UserMapper的对象,这个是我所不明白的,一个接口如何实例化。查了一下,发现java 动态代理机制分析及扩展,估计mybatis也是这样实现的。看了看mybatis的源码,就有下文了。
本文主要跟踪mybatis的源码,了解mapper接口的动态代理对象的生成
mybatis的部分Spring配置文件如下,跟踪其中id=”userMapper”的bean是如何创建的
这里写代码片 <!-- mybatis配置 --> <bean id="sqlsessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:sqlMapConfig.xml" /> </bean> <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.tqd.dao.UserMapper"></property> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean>此处是通过工厂方法创建Bean 首先我们定位到类 org.mybatis.spring.mapper.MapperFactoryBean
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { private Class<T> mapperInterface; private boolean addToConfig;可以看到该类实现了org.springframework.beans.factory.FactoryBean接口,通过调用 org.mybatis.spring.mapper.MapperFactoryBean.getObject()方法来获得Bean
public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); }从这个方法中,可以看到先调用了其父类SqlSessionDaoSupport 的getSqlSession()方法,然后返回一个SqlSession对象,再调用这个对象的getMapper方法
public SqlSession getSqlSession() { return this.sqlSession; }在这个方法中直接返回陪父类成员变量sqlSession,sqlSession是一个接口SqlSession的对象,要找到它的实现类。在类SqlSessionDaoSupport 的代码中
public abstract class SqlSessionDaoSupport extends DaoSupport { private SqlSession sqlSession; private boolean externalSqlSession; public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { if (!(this.externalSqlSession)) this.sqlSession = new SqlSessionTemplate(sqlSessionFactory); } public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this.sqlSession = sqlSessionTemplate; this.externalSqlSession = true; } public SqlSession getSqlSession() { return this.sqlSession; } protected void checkDaoConfig() { Assert.notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required"); } }我们可以找到两个方法,它们当中都包含了sqlSession的实例化代码
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { if (!(this.externalSqlSession)) this.sqlSession = new SqlSessionTemplate(sqlSessionFactory); } public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this.sqlSession = sqlSessionTemplate; this.externalSqlSession = true; }根据bean的配置文件
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.tqd.dao.UserMapper"></property> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean>Spring要将属性sqlSessionFactory注入到MapperFactoryBean的对象中,那么会调用setSqlSessionFactory方法,然后sqlSession 就被实例化。我们此时肯定知道 sqlSession的实现类是org.mybatis.spring.SqlSessionTemplate,我们定位到它的getMapper方法。
public <T> T getMapper(Class<T> type) { return getConfiguration().getMapper(type, this); }可以看到,它首先调用了自身的getConfiguration()方法返回一个Configuration对象,然后再调用Configuration对象的getMapper方法
public Configuration getConfiguration() { return this.sqlSessionFactory.getConfiguration(); }我们直接定位到Configuration的getMapper方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }可以看到,在这里调用了对象mapperRegistry的getMapper方法,直接定位到该方法
@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方法创建我们需要的bean,定位到newInstance方法
public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }在这里,首先构造出一个MapperProxy对象,然后调用自身的newInstance(重载的另一个方法)方法
@SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }Bean最终在这里创建的。关于这里推荐大家去看下Java 动态代理机制分析及扩展