この記事では、主にmybaitsのコア実行プロセスのソースコード分析を紹介します。xml構成方法に従ってクエリデモを作成します。テストコードは次のとおりです。
@Test
public void test2() throws Exception {
InputStream in = Resources.getResourceAsStream("custom/sqlMapConfig2.xml");
SqlSessionFactory factory2 = new SqlSessionFactoryBuilder().build(in);
SqlSession openSession = factory2.openSession();
UserMapper mapper = openSession.getMapper(UserMapper.class);
User user = mapper.findUserById(1);
// User user = openSession.selectOne("com.wj.source_two.demo.mapper.UserMapper.findUserById", 1);
System.out.println(user);
openSession.close();
}
上記のコード実行プロセスに従って、実行プロセスを次の部分に分割して分析します: 構成の読み込み、マッパープロキシの作成、SqlSessionの実行
1つは、構成の読み込みです
1.関連クラス
SqlSessionFactoryBuilder:SqlSessionFactoryオブジェクトを作成し、構成ファイルから解析された構成を渡す役割を果たします。
XMLConfigBuilder:コア解析クラスとその親クラスBaseBuilderのサブクラスは、さまざまなxml構成をConfigurationオブジェクトに解析する役割を果たします。
XPathParser:
XNode:nodeのパッケージを解析するxpath
2.プロセス分析
1)SqlSessionFactoryBuilderはXMLConfigBuilderを呼び出して、構成への入力ストリームを解析します
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
2)parseメソッドは、sqlMapConfig.xmlの分析であり、後でXPathParserとXNodeの使用法によって補完されます。
public Configuration parse() {
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
propertiesElement(root.evalNode("properties")); //issue #117 read properties first
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
settingsElement(root.evalNode("settings"));
environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
3)構築プロセスの一部をインターセプトし
、構成のキーと値のペアを構成にロードします
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
Properties defaults = context.getChildrenAsProperties(); // 得到子节点加载Properties
String resource = context.getStringAttribute("resource"); // 得到resource属性
String url = context.getStringAttribute("url"); // 得到url属性
// ....
if (resource != null) { // 从资源文件加载Properties
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) { // 从url属性加载Properties
defaults.putAll(Resources.getUrlAsProperties(url));
}
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
parser.setVariables(defaults);
configuration.setVariables(defaults); // 设置到configuration
}
}
インターセプターをロードし、プラグインを解析し、インターセプターのクラス名に従ってインスタンス化し、子要素の構成をロードします
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
configuration.addInterceptor(interceptorInstance);
}
}
}
2つ目は、マッパープロキシを作成することです。
1.主な作業クラス:
MapperRegistry:構成属性(1対1)、作成されたMapperProxyFactoryの管理に使用されます
MapperProxyFactory:プロキシによって作成されたファクトリクラス、プロキシ実装クラスはMapperProxy、プロキシメソッドの実際の呼び出しクラスはMapperMethodです。
MapperProxy:InvocationHandlerの拡張機能を実装し、MapperMethodをキャッシュします
。MapperMethod:元のインターフェースメソッド、構成、セッション、およびその他のパラメーターをラップし、一般的なexcuteメソッドの実行を実現し、それをMapperProxyに使用します。
2.実行プロセス分析
1)Mapper、UserMapper mapper = openSession.getMapper(UserMapper.class)を検索します。これは、実際にはMap <Class <?>、MapperProxyFactory <?>> knownMappersでMapperRegistryを検索します。
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
// 上面那个方法只是找到已经创建了Mapper接口并缓存到MapperRegistry,没找到报错
return mapperProxyFactory.newInstance(sqlSession); // 此方法才是Mapper动态代理的核心
}
2)newInstanceメソッドはmapperProxyを作成することであり、このmapperProxyはInvocationHandlerを実装します
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
3)上記はプロキシクラスを作成するプロセスです。MapperProxyの呼び出し実装に焦点を当てましょう。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try { // Object方法直接放行。
return method.invoke(this, args);
} catch (Throwable t) {
}
} // 创建MapperMethod并且缓存(缓存到MapperProxy对象中)
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
4)MapperMethodの分析
4.1) execute方法一览
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
if (SqlCommandType.INSERT == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
} else if (SqlCommandType.SELECT == command.getType()) {
if (method.returnsVoid() && method.hasResultHandler()) {
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args); // 查询方法
} else if (method.returnsMap()) {
} else {
}
} else {
throw new BindingException("Unknown execution method for: " + command.getName());
}
return result;
}
4.2) 我们从 execute方法看到 insert方法是调用session.insert(command.getName,param),这个name是什么?
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
String statementName = mapperInterface.getName() + "." + method.getName(); //就是接口名+方法名
4.3) 在看看查询的方法 result = executeForMany(sqlSession, args);
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
// 转换参数
Object param = method.convertArgsToSqlCommandParam(args);
// 执行查询
result = sqlSession.<E>selectList(command.getName(), param);
// .....
return result;
}
5.要約:
Mybaits動的プロキシマッパーは、それ自体が設定したマッパーインターフェイスに従って、タイプごとに動的プロキシクラスを作成します。このプロキシクラスによって実際に呼び出されるメソッドは、sqlsessionがインターフェイス+メソッドのIDを呼び出して操作することです。一部のキャッシュ、パラメータ処理、その他の操作。
3、SqlSession実行プロセス
1.主な職種
DefaultSqlSession:Wrapping Executor
Executor:ラッピングトランザクション、コア呼び出しクラス、追加、削除、変更、およびチェック操作はすべて、このクラスによって呼び出されます。
MappedStatement:パッケージsql、sqlタイプ、戻り値タイプ、パラメーターマッピング(構成ファイルのselectなどのラベルに対応)。
BoundSql:SQLとパラメーターのマッピングおよびパラメーター値をラップします。(同じタイプのBoundSqlが異なるSqlSourceによって生成されます)。
StatementHandler:パラメーター処理、SQL実行、戻り値処理を含む、パラメーターの処理と実行。
ParameterHandler:パラメーター処理
ResultSetHandler:結果処理
TypeHandler:型変換
2.実行プロセス
1)selectOneは実際にselectListを呼び出し、パラメーターIDは構成のMappedStatementにあります。
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
// 找mapperStatement
MappedStatement ms = configuration.getMappedStatement(statement);
// 调用executor执行查询方法query。
List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
return result;
}
2)キャッシュレイヤーのいくつかの呼び出しは無視されますが、queryFromDatabaseは実際に呼び出されます
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
return list;
}
3)doQueryメソッドは、StatementHandlerを作成し、パラメーターの事前分析を実行し、SQLを実行し、最後に戻り値を処理します。
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
4)パラメーター分析と戻り値処理は、実際にはパラメーター処理を含むStatementHandlerのParameterHandlerとResultSetHandlerの関数であり、TypeHandlerが使用されます。プロセスの省略
インターネット上で盗まれた写真。この写真は、実行プロセスを非常に直感的に示しています。
終わり!