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

     一、流程图介绍整体过程
        
 
     
    1、首先根据MapperScannerConfigurer进行包扫描,扫描Mapper接口,生成Spring特定的描述,并将其交由MapperProxyFactory管理,后期会由其生成动态代理对象。
              ClassOathMapperScanner 中的doScan()方法。
                
     
  1. GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
  2. if (logger.isDebugEnabled()) {
  3. logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
  4. + "' and '" + definition.getBeanClassName() + "' mapperInterface");
  5. }
  6. // the mapper interface is the original class of the bean
  7. // but, the actual class of the bean is MapperFactoryBean
  8. definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
  9. definition.setBeanClass(MapperFactoryBean.class);
  10. definition.getPropertyValues().add("addToConfig", this.addToConfig);


    2、初始化SqlSessionFactoryBean。首先判定mybatis.xml中的mapper属性是否配置如果配置,解析Mapper接口添加到Configuration中。
            XMLConfigBuilder中的mapperElement()方法
            
     
  1. private void mapperElement(XNode parent) throws Exception {
  2. if (parent != null) {
  3. for (XNode child : parent.getChildren()) {
  4. if ("package".equals(child.getName())) {
  5. String mapperPackage = child.getStringAttribute("name");
  6. configuration.addMappers(mapperPackage);
  7. } else {
  8. String resource = child.getStringAttribute("resource");
  9. String url = child.getStringAttribute("url");
  10. String mapperClass = child.getStringAttribute("class");
  11. if (resource != null && url == null && mapperClass == null) {
  12. ErrorContext.instance().resource(resource);
  13. InputStream inputStream = Resources.getResourceAsStream(resource);
  14. XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
  15. mapperParser.parse();
  16. } else if (resource == null && url != null && mapperClass == null) {
  17. ErrorContext.instance().resource(url);
  18. InputStream inputStream = Resources.getUrlAsStream(url);
  19. XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
  20. mapperParser.parse();
  21. } else if (resource == null && url == null && mapperClass != null) {
  22. Class<?> mapperInterface = Resources.classForName(mapperClass);
  23. configuration.addMapper(mapperInterface);
  24. } else {
  25. throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
  26. }
  27. }
  28. }
  29. }
  30. }


    3、判定 SqlSessionFactoryBean节点中是否配置mapperLocations属性,也即mapper.xml文件所在位置。会根据mapper.xml文件中配置的命名空间,获取Mapper接口,并添加到Configuration中。
        XMLMapperBuilder中的bindMapperForNamespace()方法。
        
     
  1. private void bindMapperForNamespace() {
  2. String namespace = builderAssistant.getCurrentNamespace();
  3. if (namespace != null) {
  4. Class<?> boundType = null;
  5. try {
  6. boundType = Resources.classForName(namespace);
  7. } catch (ClassNotFoundException e) {
  8. //ignore, bound type is not required
  9. }
  10. if (boundType != null) {
  11. if (!configuration.hasMapper(boundType)) {
  12. // Spring may not know the real resource name so we set a flag
  13. // to prevent loading again this resource from the mapper interface
  14. // look at MapperAnnotationBuilder#loadXmlResource
  15. configuration.addLoadedResource("namespace:" + namespace);
  16. configuration.addMapper(boundType);
  17. }
  18. }
  19. }
  20. }


    4、如果以上都没有配置,需要使用Mapper的代理对象时,就会使用包扫描获得的Mapper接口信息,解析Mapper接口,添加到Configuration中。
    5、Configuration添加Mapper接口过程。
        MapperRegistry总的addMapper()方法
        
     
  1. public <T> void addMapper(Class<T> type) {
  2. if (type.isInterface()) {
  3. if (hasMapper(type)) {
  4. throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
  5. }
  6. boolean loadCompleted = false;
  7. try {
  8. knownMappers.put(type, new MapperProxyFactory<T>(type));
  9. // It's important that the type is added before the parser is run
  10. // otherwise the binding may automatically be attempted by the
  11. // mapper parser. If the type is already known, it won't try.
  12. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
  13. parser.parse();
  14. loadCompleted = true;
  15. } finally {
  16. if (!loadCompleted) {
  17. knownMappers.remove(type);
  18. }
  19. }
  20. }
  21. }


    5.1 根据接口信息获取mapper.xml文件,校验mapper.xml文件配置是否正确
        MapperAnnotationBuilder中的parse()方法
        
     
  1. public void parse() {
  2. String resource = type.toString();
  3. if (!configuration.isResourceLoaded(resource)) {
  4. loadXmlResource();
  5. configuration.addLoadedResource(resource);
  6. assistant.setCurrentNamespace(type.getName());
  7. parseCache();
  8. parseCacheRef();
  9. Method[] methods = type.getMethods();
  10. for (Method method : methods) {
  11. try {
  12. if (!method.isBridge()) { // issue #237
  13. parseStatement(method);
  14. }
  15. } catch (IncompleteElementException e) {
  16. configuration.addIncompleteMethod(new MethodResolver(this, method));
  17. }
  18. }
  19. }
  20. parsePendingMethods();
  21. }
     MapperAnnotationBuilder中的loadXmlResource()方法

      
  1. private void loadXmlResource() {
  2. // Spring may not know the real resource name so we check a flag
  3. // to prevent loading again a resource twice
  4. // this flag is set at XMLMapperBuilder#bindMapperForNamespace
  5. if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
  6. String xmlResource = type.getName().replace('.', '/') + ".xml";
  7. InputStream inputStream = null;
  8. try {
  9. inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
  10. } catch (IOException e) {
  11. // ignore, resource is not required
  12. }
  13. if (inputStream != null) {
  14. XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
  15. xmlParser.parse();
  16. }
  17. }
  18. }
        可以看到最后又回到了XMLMapperBuilder中的parse()方法。

    5.2 解析Mapper接口以及mapper.xml文件,将方法与sql文件进行绑定。
            
    5.3 使用JDK动态代理技术生成Mapper接口的代理对象,添加到Configuration中。
    


©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页