mybatis源碼分析之mapper動態代理

上一篇: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
相關文章
相關標籤/搜索