JavaWeb 知识点

mybatisPlus源码分析

2018-12-01  本文已影响55人  牛逼的小伙子

mp源码分析

1.MybatisSessionFactoryBuilder 中的build方法引入解析XML的类MybatisXMLConfigBuilder

MybatisSessionFactoryBuilder mf = new MybatisSessionFactoryBuilder();

MybatisSessionFactoryBuilder.java 调用 build方法

@Override
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
        MybatisXMLConfigBuilder parser = new MybatisXMLConfigBuilder(inputStream, environment, properties);
        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.
        }
    }
}

MybatisXMLConfigBuilder.java 构造方法

private MybatisXMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    //TODO 自定义 Configuration
    super(new MybatisConfiguration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
}

实例化MybatisConfiguration.java类,加载ISqlInjector和 MybatisMapperRegistry

/*
 * SQL 注入器,实现 ISqlInjector 或继承 AutoSqlInjector 自定义方法
 */
public static ISqlInjector SQL_INJECTOR = new AutoSqlInjector();
/*
 * Mapper 注册
 */
public final MybatisMapperRegistry mybatisMapperRegistry = new MybatisMapperRegistry(this);

上一个类调用的parse()方法

public Configuration parse() {
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}
private void parseConfiguration(XNode root) {
    try {
        Properties settings = settingsAsPropertiess(root.evalNode("settings"));
        // issue #117 read properties first
        propertiesElement(root.evalNode("properties"));
        loadCustomVfs(settings);
        typeAliasesElement(root.evalNode("typeAliases"));
        pluginElement(root.evalNode("plugins"));
        objectFactoryElement(root.evalNode("objectFactory"));
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        reflectorFactoryElement(root.evalNode("reflectorFactory"));
        settingsElement(settings);
        // read it after objectFactory and objectWrapperFactory issue #631
        environmentsElement(root.evalNode("environments"));
        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);
    }
}
private void mapperElement(XNode parent) throws Exception {
    /**
     * 定义集合 用来分类放置mybatis的Mapper与XML 按顺序依次遍历
     */
    if (parent != null) {
        //指定在classpath中的mapper文件
        Set<String> resources = new HashSet<String>();
        //指向一个mapper接口
        Set<Class<?>> mapperClasses = new HashSet<Class<?>>();
        setResource(parent, resources, mapperClasses);
        // 依次遍历 首先 resource 然后 mapper
        for (String resource : resources) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //TODO
            MybatisXMLMapperBuilder mapperParser = new MybatisXMLMapperBuilder(inputStream, configuration, resource,
                    configuration.getSqlFragments());
            mapperParser.parse();
        }
        for (Class<?> mapper : mapperClasses) {
            //TODO
            configuration.addMapper(mapper);
        }
    }
}

MybatisXMLMapperBuilder 调用parse()方法

public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
        configurationElement(parser.evalNode("/mapper"));
        configuration.addLoadedResource(resource);
        bindMapperForNamespace();
    }
    parsePendingResultMaps();
    parsePendingChacheRefs();
    parsePendingStatements();
}

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);
            }
            //TODO 注入 CURD 动态 SQL
            if (BaseMapper.class.isAssignableFrom(boundType)) {
                MybatisConfiguration.SQL_INJECTOR.inspectInject(configuration, builderAssistant, boundType);
            }
        }
    }
}

addMapper方法 MybatisMapperRegistry.java代理类方法

@Override
public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
        if (hasMapper(type)) {
            //TODO 如果之前注入 直接返回
            return;
            // throw new BindingException("Type " + type +
            // " is already known to the MybatisPlusMapperRegistry.");
        }
        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.
            //TODO 自定义无 XML 注入
            MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);
            parser.parse();
            loadCompleted = true;
        } finally {
            if (!loadCompleted) {
                knownMappers.remove(type);
            }
        }
    }
}

MybatisMapperAnnotationBuilder 中的parse()方法

@Override
public void parse() {
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) {
        boolean existXml = loadXmlResource();
        configuration.addLoadedResource(resource);
        assistant.setCurrentNamespace(type.getName());
        parseCache();
        parseCacheRef();
        Method[] methods = type.getMethods();
        //TODO 注入存在 xxMapper.xml CURD (应该在注解之前注入)
        inspectInject(existXml);
        for (Method method : methods) {
            try {
                // issue #237
                if (!method.isBridge()) {
                    parseStatement(method);
                }
            } catch (IncompleteElementException e) {
                configuration.addIncompleteMethod(new MethodResolver(this, method));
            }
        }
    }
    parsePendingMethods();
}

动态sql注入器

/*
 * 注入 CURD 动态 SQL(XML不存在时注入)
 */
private void inspectInject(boolean flag) {
    if (!flag && BaseMapper.class.isAssignableFrom(type)) {
        MybatisConfiguration.SQL_INJECTOR.inspectInject(configuration, assistant, type);
    }
}

AutoSqlInjector 开始注入动态sql语句

总结

mp也是遵循mybatis设计理念,通过设置注解的方法实现加载动态sql语句

mybatis 注解实现 MapperAnnotationBuilder

public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
    String resource = type.getName().replace('.', '/') + ".java (best guess)";
    this.assistant = new MapperBuilderAssistant(configuration, resource);
    this.configuration = configuration;
    this.type = type;
    sqlAnnotationTypes.add(Select.class);
    sqlAnnotationTypes.add(Insert.class);
    sqlAnnotationTypes.add(Update.class);
    sqlAnnotationTypes.add(Delete.class);
    sqlProviderAnnotationTypes.add(SelectProvider.class);
    sqlProviderAnnotationTypes.add(InsertProvider.class);
    sqlProviderAnnotationTypes.add(UpdateProvider.class);
    sqlProviderAnnotationTypes.add(DeleteProvider.class);
  }
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 {
          // issue #237
          if (!method.isBridge()) {
            parseStatement(method);
          }
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    parsePendingMethods();
  }

参考

注解实现sql注入:https://blog.csdn.net/wo_shi_LTB/article/details/79242889

上一篇下一篇

猜你喜欢

热点阅读