@Test
public void test2() throws Exception{
// 1.获取配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
// 2.加载解析配置文件并获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3.根据SqlSessionFactory对象获取SqlSession对象
SqlSession sqlSession = factory.openSession();
// 4.通过SqlSession中提供的 API方法来操作数据库
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectUserById(1);
System.out.println(user);
// 5.关闭会话
sqlSession.close();
}
Mybatis有自己的资源加载工具Resources,从classpath加载mybatis-config.xml配置文件。
SqlSessionFactoryBuilder从名称末尾的Builder可知,这是一个运用建造者模式的类,专门用来创建SqlSessionFactory。SqlSessionFactoryBuilder只是用来创建SqlSessionFactory,完成创建后就可以销毁。
进入build(in)方法查看:
public SqlSessionFactory build(InputStream inputStream) {
// test 6666 --> 999 0000
return build(inputStream, null, null);
}
这里重载了build方法:
/**
* 1.创建了一个XMLConfigBuilder对象 会完成很多初始化操作 最终的是创建了 Configuration 对象
* 2.parser.parse() 完成全局配置文件的加载解析 并将相关的信息封装到 Configuration 对象中
* 同时会完成映射文件(UserMapper.xml)的加载解析,相关的信息同样的会被保存到 Configuration对象中
* select/insert/udpate/delete 标签的信息会被封装到 MapperdStatement对象中
* 3.build(parser.parse()) ==》直接创建了 DefaultSqlSessionFactory对象
*
*
* 1.创建了一个解析器
* 2.通过解析器解析全局配置文件
* 3.通过得到的Configuration对象来创建DefaultSqlSessionFactory实例
* @param inputStream
* @param environment
* @param properties
* @return
*/
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 用于解析 mybatis-config.xml,同时创建了 Configuration 对象 >> ok 完成了很多的初始化操作
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 解析XML,最终返回一个 DefaultSqlSessionFactory >>
// 全局配置文件中的信息都被封装到了 Configuration对象中
// 映射文件中的 配置信息 同样的也被封装到了 Configuration 对象中
// 一个具体的 CRUD 标签的信息 被封装到 MappedStatment 对象中
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
总结:XMLConfigBuilder对象完成全局配置文件和映射文件的解析,得到Configuration对象,Configuration作为入参创建DefaultSqlSessionFactory对象。
从XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties)按F7进入。
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
// EntityResolver的实现类是XMLMapperEntityResolver 来完成配置文件的校验,根据对应的DTD文件来实现
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
然后重载构造函数,这个函数是private,非常有意思的设计,其他构造函数最终都是调用这个函数。
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration()); // 完成了Configuration的初始化 类型别名的注册
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props); // 设置对应的Properties属性
this.parsed = false; // 设置 是否解析的标志为 false
this.environment = environment; // 初始化environment
this.parser = parser; // 初始化 解析器
}
// 标识是否解析过mybatis-config.xml文件
private boolean parsed;
// 用于解析mybatis-config.xml 配置文件的 XPathParser对象
private final XPathParser parser;
// 标签定义的环境名称
private String environment;
// 心功能是实现对 Reflector 对象的创建和缓存
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
super(new Configuration())
XMLConfigBuilder继承了抽象类BaseBuilder,这里super就是调用父类的构造函数。F7跟入:
// Configuration 是MyBatis初始化过程的核心对象,MyBatis中的几乎全部配置信息会保存在 Configuration 对象中
protected final Configuration configuration;
// 别名的注册器
protected final TypeAliasRegistry typeAliasRegistry;
// 类型处理器的注册器
protected final TypeHandlerRegistry typeHandlerRegistry;
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
BaseBuilder持有三个成员变量:
super(new Configuration()); // 完成了Configuration的初始化 类型别名的注册
Configuration持有很多变量,其创建过程值得探究。
parser.parse()
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// XPathParser,dom 和 SAX 都有用到 >>
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
/**
* 全局配置文件中的配置信息都被加载到 Configuration 对象中
* @param root
*/
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// 对于全局配置文件各种标签的解析 都被保存到了 Configuration 对象中。
propertiesElement(root.evalNode("properties"));
// 解析 settings 标签 返回的结果就是 settings 中定义的信息 自定义的属性
Properties settings = settingsAsProperties(root.evalNode("settings"));
// 读取文件
loadCustomVfs(settings);
// 日志设置
loadCustomLogImpl(settings);
// 类型别名
typeAliasesElement(root.evalNode("typeAliases"));
// 插件
pluginElement(root.evalNode("plugins"));
// 用于创建对象
objectFactoryElement(root.evalNode("objectFactory"));
// 用于对对象进行加工
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 反射工具箱
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// settings 子标签赋值,默认值就是在这里提供的 >> 加载默认设置
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// 创建了数据源 >>
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 自定义的类型处理器的解析
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析引用的Mapper映射器 ===》 映射文件的加载解析
mapperElement(root.evalNode("mappers")); // 映射文件中的信息 加载解析出来后保存到了哪个对象中?
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
安装顺序解析标签:
/**
* 加载解析 properties 标签 并且将相关的属性信息更新保存到了 Configuration 对象中了
* @param context
* @throws Exception
*/
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
// 创建了一个 Properties 对象,后面可以用到
Properties defaults = context.getChildrenAsProperties();
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url");
if (resource != null && url != null) {
// url 和 resource 不能同时存在
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
// 加载resource或者url属性中指定的 properties 文件
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
// 配置文件中 已经有的 属性信息
Properties vars = configuration.getVariables();
if (vars != null) {
// 和 Configuration中的 variables 属性合并
defaults.putAll(vars);
}
// 更新对应的属性信息
parser.setVariables(defaults);
// 解析出来的 属性信息 被保存到了 configuration 对象中
configuration.setVariables(defaults);
}
}
/**
* 将 sttings 标签中配置的 信息封装到了 Properties 对象中,并且返回了
* @param context
* @return
*/
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
}
// 获取settings节点下的所有的子节点
Properties props = context.getChildrenAsProperties();
// Check that all settings are known to the configuration class 用户到了 反射工具箱 中的内容 实现反射处理
// 创建Configuration对应的MetaClass对象
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
for (Object key : props.keySet()) {
// 检验配置的拼写是否准确,是否有setter方法
if (!metaConfig.hasSetter(String.valueOf(key))) {
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
return props;
}
// loadCustomVfs是获取Vitual File System的自定义实现类,比如要读取本地文件,
// 或者FTP远程文件的时候,就可以用到自定义的VFS类。 (用到的机会很少)
loadCustomVfs(settings);
loadCustomLogImpl(settings);
private void loadCustomLogImpl(Properties props) {
// 获取 logImpl设置的 日志 类型
Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
// 设置日志 这块代码是我们后面分析 日志 模块的 关键代码
configuration.setLogImpl(logImpl);
}
typeAliasesElement(root.evalNode("typeAliases"));
private void typeAliasesElement(XNode parent) {
// 放入 TypeAliasRegistry
if (parent != null) {
for (XNode child : parent.getChildren()) {
// 解析包下面的类,默认别名的类名小写,如果类上有@Alias,就取注释的value
if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
Class<?> clazz = Resources.classForName(type);
if (alias == null) {
// 扫描 @Alias 注解使用
typeAliasRegistry.registerAlias(clazz);
} else {
// 直接注册
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
pluginElement(root.evalNode("plugins"));
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
// 获取 节点的 interceptor 属性的值
String interceptor = child.getStringAttribute("interceptor");
// 获取 下的所有的properties子节点
Properties properties = child.getChildrenAsProperties();
// 初始化 Interceptor 对象
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
// 设置 interceptor的 属性
interceptorInstance.setProperties(properties);
// 将Interceptor对象添加到Configuration的插件链中保存,等待后续使用
configuration.addInterceptor(interceptorInstance);
}
}
}
private void objectFactoryElement(XNode context) throws Exception {
if (context != null) {
// 获取 节点的 type 属性
String type = context.getStringAttribute("type");
// 获取 节点下的配置信息
Properties properties = context.getChildrenAsProperties();
// 获取ObjectFactory 对象的对象 通过反射方式
ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();
// ObjectFactory 和 对应的属性信息关联
factory.setProperties(properties);
// 将创建的ObjectFactory对象绑定到Configuration中
configuration.setObjectFactory(factory);
}
}
private void objectWrapperFactoryElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();
configuration.setObjectWrapperFactory(factory);
}
}
private void reflectorFactoryElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance();
configuration.setReflectorFactory(factory);
}
}
// settings 中的属性信息都是直接保存在 Configuration 对象的属性中的
private void settingsElement(Properties props) {
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
configuration.setLogPrefix(props.getProperty("logPrefix"));
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
// 事务工厂
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// 数据源工厂(例如 DruidDataSourceFactory )
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
// 数据源
DataSource dataSource = dsFactory.getDataSource();
// 包含了 事务工厂和数据源的 Environment
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
// 放入 Configuration Environment 存储了 DataSourceFactory TransactionFactory
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
解析databaseIdProvider标签,生成DatabaseIdProvider对象(用来支持不同厂商的数据库)。
在 MyBatis 中编写的都是原生的 SQL 语句,而很多数据库产品都会有一些 SQL 方言,这些方言与标准 SQL 不兼容。
在 mybatis-config.xml 配置文件中,我们可以通过 标签定义需要支持的全部数据库的 DatabaseId,在后续编写 Mapper 映射配置文件的时候,就可以为同一个业务场景定义不同的 SQL 语句(带有不同的 DataSourceId),来支持不同的数据库,这里就是靠 DatabaseId 来确定哪个 SQL 语句支持哪个数据库的。
typeHandlerElement(root.evalNode("typeHandlers"));
private void typeHandlerElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) { // 处理全部子标签
if ("package".equals(child.getName())) {
// 如果指定了package属性,则扫描指定包中所有的类,
// 并解析@MappedTypes注解,完成TypeHandler的注册
String typeHandlerPackage = child.getStringAttribute("name");
typeHandlerRegistry.register(typeHandlerPackage);
} else {
// 如果没有指定package属性,则尝试获取javaType、jdbcType、handler三个属性
String javaTypeName = child.getStringAttribute("javaType");
String jdbcTypeName = child.getStringAttribute("jdbcType");
String handlerTypeName = child.getStringAttribute("handler");
// 根据属性确定TypeHandler类型以及它能够处理的数据库类型和Java类型
Class<?> javaTypeClass = resolveClass(javaTypeName);
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
// 调用TypeHandlerRegistry.register()方法注册TypeHandler
if (javaTypeClass != null) {
if (jdbcType == null) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) { // 循环遍历 标签的所有的子节点
// 不同的定义方式的扫描,最终都是调用 addMapper()方法(添加到 MapperRegistry)。这个方法和 getMapper() 对应
// package 包
if ("package".equals(child.getName())) { //
String mapperPackage = child.getStringAttribute("name");
// 扫描指定的包,并向MapperRegistry注册Mapper接口
// 每一个类型 创建一个对应的 MapperProxyFactory 对象
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
// resource 相对路径
ErrorContext.instance().resource(resource);
// 读取映射文件
InputStream inputStream = Resources.getResourceAsStream(resource);
// XMLMapperBuilder 解析映射文件 XMLConfigurationBuilder
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// 解析 Mapper.xml,总体上做了两件事情 >>
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
// url 绝对路径
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
// class 单个接口
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
mapperParser.parse();
public void parse() {
// 总体上做了两件事情,对于语句的注册和接口的注册
// 判断是否已经加载过了 映射文件
if (!configuration.isResourceLoaded(resource)) {
// 1、具体增删改查标签的解析。
// 一个标签一个MappedStatement。 >>
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
// 2、把namespace(接口类型)和工厂类绑定起来,放到一个map。
// 一个namespace 一个 MapperProxyFactory >>
bindMapperForNamespace(); // 注册 Mapper 接口
}
// 处理 configurationElement 方法中解析失败的 节点
parsePendingResultMaps();
// 处理 configurationElement 方法中解析失败的 节点
parsePendingCacheRefs();
// 处理 configurationElement 方法中解析失败的 SQL 语句节点
parsePendingStatements();
}
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
// 添加缓存对象 如果我们希望多个 namespace 共用同一个二级缓存 就可以使用
cacheRefElement(context.evalNode("cache-ref"));
// 解析 cache 属性,添加缓存对象
cacheElement(context.evalNode("cache"));
// 创建 ParameterMapping 对象 以废弃 不推荐
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// 创建 List
resultMapElements(context.evalNodes("/mapper/resultMap"));
// 解析可以复用的SQL includ
sqlElement(context.evalNodes("/mapper/sql"));
// 解析增删改查标签,得到 MappedStatement >> 一个 CRUD标签中的信息都被封装到了 MappedStatement 对象
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
在buildStatementFromContext()方法中,创建了用来解析增删改查标签的XMLStatementBuilder,并且把创建的MappedStatement添加到mappedStatements中。
private void buildStatementFromContext(List<XNode> list) {
// 多数据库
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
// 解析 Statement >>
buildStatementFromContext(list, null);
}
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
// 用来解析增删改查标签的 XMLStatementBuilder
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
// 解析 Statement,添加 MappedStatement 对象 >>
// 解析具体的 insert/update/delete/select
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}
关键步骤:
MappedStatement statement = statementBuilder.build();
// 最关键的一步,在 Configuration 添加了 MappedStatement >> 对应的就是一个 CRUD 标签
configuration.addMappedStatement(statement);
return statement;
private void bindMapperForNamespace() {
// 约定 namespace 要和对应的接口的全类路径名称保持一致
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
// 根据名称空间加载对应的接口类型
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
// 判断 在MapperRegistry中是否注册的有当前类型的 MapperProxyFactory对象
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
// 添加到 MapperRegistry,本质是一个 map,里面也有 Configuration >>
configuration.addMapper(boundType);
}
}
}
}
通过源码分析发现主要是是调用了addMapper()。addMapper()方法中,把接口类型注册到MapperRegistry中:
实际上是为接口创建一个对应的MapperProxyFactory(用于为这个type提供工厂类,创建MapperProxy)。
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) { // 检测 type 是否为接口
if (hasMapper(type)) { // 检测是否已经加装过该接口
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// !Map, MapperProxyFactory>> 存放的是接口类型,和对应的工厂类的关系
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
// 注册了接口之后,根据接口,开始解析所有方法上的注解,例如 @Select >>
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
再进入parse()方法:
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
// 先判断 Mapper.xml 有没有解析,没有的话先解析 Mapper.xml(例如定义 package 方式)
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
// 处理 @CacheNamespace
parseCache();
// 处理 @CacheNamespaceRef
parseCacheRef();
// 获取所有方法
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
// 解析方法上的注解,添加到 MappedStatement 集合中 >>
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
总结: