您的位置:首页 > 移动开发

Mybatis 源码解析三、Mapper接口与mapper.xml文件绑定

2017-03-23 17:45 791 查看
一、流程图介绍整体过程





1、首先根据MapperScannerConfigurer进行包扫描,扫描Mapper接口,生成Spring特定的描述,并将其交由MapperProxyFactory管理,后期会由其生成动态代理对象。

ClassOathMapperScanner 中的doScan()方法。

GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();


if (logger.isDebugEnabled()) {

logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()

+ "' and '" + definition.getBeanClassName() + "' mapperInterface");

}


// the mapper interface is the original class of the bean

// but, the actual class of the bean is MapperFactoryBean

definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());

definition.setBeanClass(MapperFactoryBean.class);


definition.getPropertyValues().add("addToConfig", this.addToConfig);

[/code]

2、初始化SqlSessionFactoryBean。首先判定mybatis.xml中的mapper属性是否配置如果配置,解析Mapper接口添加到Configuration中。

XMLConfigBuilder中的mapperElement()方法

private void mapperElement(XNode parent) throws Exception {

if (parent != null) {

for (XNode child : parent.getChildren()) {

if ("package".equals(child.getName())) {

String mapperPackage = child.getStringAttribute("name");

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) {

ErrorContext.instance().resource(resource);

InputStream inputStream = Resources.getResourceAsStream(resource);

XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());

mapperParser.parse();

} else if (resource == null && url != null && mapperClass == null) {

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<?> 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.");

}

}

}

}

}

[/code]

3、判定SqlSessionFactoryBean节点中是否配置mapperLocations属性,也即mapper.xml文件所在位置。会根据mapper.xml文件中配置的命名空间,获取Mapper接口,并添加到Configuration中。

XMLMapperBuilder中的bindMapperForNamespace()方法。

private void bindMapperForNamespace() {

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) {

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);

configuration.addMapper(boundType);

}

}

}

}

[/code]

4、如果以上都没有配置,需要使用Mapper的代理对象时,就会使用包扫描获得的Mapper接口信息,解析Mapper接口,添加到Configuration中。

5、Configuration添加Mapper接口过程。

MapperRegistry总的addMapper()方法

public <T> void addMapper(Class<T> type) {

if (type.isInterface()) {

if (hasMapper(type)) {

throw new BindingException("Type " + type + " is already known to the MapperRegistry.");

}

boolean loadCompleted = false;

try {

knownMappers.put(type, new MapperProxyFactory<T>(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.

MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);

parser.parse();

loadCompleted = true;

} finally {

if (!loadCompleted) {

knownMappers.remove(type);

}

}

}

}

[/code]

5.1 根据接口信息获取mapper.xml文件,校验mapper.xml文件配置是否正确

MapperAnnotationBuilder中的parse()方法

public void parse() {

String resource = type.toString();

if (!configuration.isResourceLoaded(resource)) {

loadXmlResource();

configuration.addLoadedResource(resource);

assistant.setCurrentNamespace(type.getName());

parseCache();

parseCacheRef();

Method[] methods = type.getMethods();

for (Method method : methods) {

  try {

if (!method.isBridge()) { // issue #237

parseStatement(method);

}

} catch (IncompleteElementException e) {

configuration.addIncompleteMethod(new MethodResolver(this, method));

}

}

}

parsePendingMethods();

}

[/code]

MapperAnnotationBuilder中的loadXmlResource()方法

private void loadXmlResource() {

// Spring may not know the real resource name so we check a flag

// to prevent loading again a resource twice

// this flag is set at XMLMapperBuilder#bindMapperForNamespace

if (!configuration.isResourceLoaded("namespace:" + type.getName())) {

String xmlResource = type.getName().replace('.', '/') + ".xml";

InputStream inputStream = null;

try {

inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);

} catch (IOException e) {

// ignore, resource is not required

}

if (inputStream != null) {

XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());

xmlParser.parse();

}

}

}

[/code]

可以看到最后又回到了XMLMapperBuilder中的parse()方法。

5.2 解析Mapper接口以及mapper.xml文件,将方法与sql文件进行绑定。

5.3 使用JDK动态代理技术生成Mapper接口的代理对象,添加到Configuration中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐