上一篇:mybatis源碼分析之SqlSession的建立過程java
http://www.javashuo.com/article/p-scnnjjum-na.htmlsql
重點分析了SqlSession的建立過程.SqlSession建立成功後:mybatis
String resource = "com/analyze/mybatis/mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); Map map = sqlSession.selectOne("com.analyze.mybatis.mapper.UserMapper.getUA"); sqlSession.close(); String resource = "com/analyze/mybatis/mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); Map map = userMapper.getUA(); sqlSession.close();
以上兩段代碼最終將獲得相同的結果.app
對比能夠發現兩段代碼不一樣之處爲:源碼分析
Map map = sqlSession.selectOne("com.analyze.mybatis.mapper.UserMapper.getUA"); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); Map map = userMapper.getUA();
根據sqlSession.selectOne("com.analyze.mybatis.mapper.UserMapper.getUA");測試
能夠想像selectOne會用com.analyze.mybatis.mapper.UserMapper.getUA查找相應的配置,而後執行sql.ui
接着分析sqlSession.selectOne()this
DefaultSqlSession.java public <T> T selectOne(String statement) { return this.<T>selectOne(statement, null); } public <T> T selectOne(String statement, Object parameter) { // Popular vote was to return null on 0 results and throw exception on too many. List<T> list = this.<T>selectList(statement, parameter); if (list.size() == 1) { return list.get(0); } else if (list.size() > 1) { throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); } else { return null; } } public <E> List<E> selectList(String statement, Object parameter) { return this.selectList(statement, parameter, RowBounds.DEFAULT); } public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { //從配置中獲取statement信息 MappedStatement ms = configuration.getMappedStatement(statement); //調用執行器 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
但UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map map = userMapper.getUA();這兩行代碼可以看出一些問題,在代碼裏並無實現UserMapper這個接口,也就是說userMapper.getUA();並沒實現,最終卻可以被調用.那麼這究竟是怎麼實現的呢?.net
進一步分析源碼:代理
DefaultSqlSession.java public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); } Configuration.java public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } MapperRegistry.java 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.newInstance()
接着分析MapperProxyFactory的源碼
public class MapperProxyFactory<T> { private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); public MapperProxyFactory(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } public Class<T> getMapperInterface() { return mapperInterface; } public Map<Method, MapperMethod> getMethodCache() { return methodCache; } @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裏的MapperProxy很容易就能夠看出使用了動態代理.
再來看看MapperProxy裏的invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //當執行的方法是繼承自Object時執行this裏的相應方法 if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } final MapperMethod mapperMethod = cachedMapperMethod(method); //最終執行的是execute方法 return mapperMethod.execute(sqlSession, args); }
從以上代碼能夠看出調用mapper裏的接口的時候執行的都是mapperMethod.execute(sqlSession, args);
繼續跟進代碼會發現上面的例子最終執行的是
sqlSession.selectOne(command.getName(), param);
可謂異曲同工.
mapper動態代理涉及到的類有MapperRegistry,MapperProxyFactory,MapperProxy,MapperMethod
MapperRegistry的數據源頭Configuration.java
MapperRegistry mapperRegistry = new MapperRegistry(this);
XMLConfigBuilder中的方法parseConfiguration()調用mapperElement(root.evalNode("mappers"));
mapperElement()會調用addMapper()最後將數據添加到MapperRegistry中的knownMappers
分析完源碼能夠仿照以上代碼來實現本身的功能:
public interface UserService { Map getUser(); } public class ServiceProxy implements InvocationHandler { public <T> T newInstance(Class<T> clz) { return (T) Proxy.newProxyInstance(clz.getClassLoader(), new Class[]{clz}, this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } System.out.println("----proxy----" + method.getName()); return null; } } //測試代碼 public static void main(String[] args) { UserService userService = new ServiceProxy().newInstance(UserService.class); userService.toString(); userService.getUser(); } //輸出結果 ----proxy----getUser