MyBatis(MP)增强SQL注入原理

1.扫描识别

项目启动时,我们所写的继承了BaseMapper的接口(XxMapper extends BaseMapper)会被MP中的组件所识别。而这个识别的核心组件就是 IsqlInjector,它是一个接口,表示具有sql注入功能的接口。
这个IsqlInjector接口的实现由AbstractSqlInjector类实现,AbstractSqlInjector是一个抽象类,它里面实现了接口定义的inspectInject()方法:

@Override
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?>
mapperClass) {
	Class<?> modelClass = extractModelClass(mapperClass);

	if (modelClass != null) {
	String className = mapperClass.toString();
	Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
	if (!mapperRegistryCache.contains(className)) {
		List<AbstractMethod> methodList = this.getMethodList();
		if (CollectionUtils.isNotEmpty(methodList)) {
			TableInfo tableInfo =
			TableInfoHelper.initTableInfo(builderAssistant, modelClass);
			// 循环注入自定义方法
			methodList.forEach(m -> m.inject(builderAssistant, mapperClass,modelClass, tableInfo));
		} else {
			logger.debug(mapperClass.toString() + ", No effective injection
			method was found.");
		}
		mapperRegistryCache.add(className);
	}
	}
}

2.注入sql

在实现方法中, methodList.forEach(m -> m.inject(builderAssistant, mapperClass,modelClass, tableInfo)); 是关键,循环遍历方法,进行注入。最终调用抽象方法injectMappedStatement进行真正的注入,这个方法返回一个MappedStatement对象。
当我们获取到了MappedStatement这个对象时,就说明mybatis的sql注入已经完成。

3.注入了哪些方法,方法具体怎么注入的

injectMappedStatement抽象方法由多个具体类实现,这些类按照查询类别的不同而分类,例如:SelectById.java、SelectBatchIds.java、DeleteByMap.java,每个类中都实现了injectMappedStatement方法,这样在顶层调用时,通过多态,可以让不同的查询实现了不同的sql语句的注册。而这些类都是MP默认实现的,它们全部都会被调用并生成sql注入进来。
MP里面有个枚举类型的类叫SqlMethod(如下图),这是个Sql模板的集合,里面写好了各种CURD的sql模板,以及查询类别的标识,在injectMappedStatement方法里不同的实现类会引用它对应的枚举类型,并和我们定义的BaseMapper的泛型,来生成相对应的sql语句。

public enum SqlMethod {
    INSERT_ONE("insert", "插入一条数据(选择字段插入)", "<script>\nINSERT INTO %s %s VALUES %s\n</script>"),
    UPSERT_ONE("upsert", "Phoenix插入一条数据(选择字段插入)", "<script>\nUPSERT INTO %s %s VALUES %s\n</script>"),
    DELETE_BY_ID("deleteById", "根据ID 删除一条数据", "<script>\nDELETE FROM %s WHERE %s=#{%s}\n</script>"),
    DELETE_BY_MAP("deleteByMap", "根据columnMap 条件删除记录", "<script>\nDELETE FROM %s %s\n</script>"),
    DELETE("delete", "根据 entity 条件删除记录", "<script>\nDELETE FROM %s %s %s\n</script>"),
    DELETE_BATCH_BY_IDS("deleteBatchIds", "根据ID集合,批量删除数据", "<script>\nDELETE FROM %s WHERE %s IN (%s)\n</script>"),
    LOGIC_DELETE_BY_ID("deleteById", "根据ID 逻辑删除一条数据", "<script>\nUPDATE %s %s WHERE %s=#{%s} %s\n</script>"),
    LOGIC_DELETE_BY_MAP("deleteByMap", "根据columnMap 条件逻辑删除记录", "<script>\nUPDATE %s %s %s\n</script>"),
    LOGIC_DELETE("delete", "根据 entity 条件逻辑删除记录", "<script>\nUPDATE %s %s %s %s\n</script>"),
    LOGIC_DELETE_BATCH_BY_IDS("deleteBatchIds", "根据ID集合,批量逻辑删除数据", "<script>\nUPDATE %s %s WHERE %s IN (%s) %s\n</script>"),
    UPDATE_BY_ID("updateById", "根据ID 选择修改数据", "<script>\nUPDATE %s %s WHERE %s=#{%s} %s\n</script>"),
    UPDATE("update", "根据 whereEntity 条件,更新记录", "<script>\nUPDATE %s %s %s %s\n</script>"),
    LOGIC_UPDATE_BY_ID("updateById", "根据ID 修改数据", "<script>\nUPDATE %s %s WHERE %s=#{%s} %s\n</script>"),
    SELECT_BY_ID("selectById", "根据ID 查询一条数据", "SELECT %s FROM %s WHERE %s=#{%s} %s"),
    SELECT_BY_MAP("selectByMap", "根据columnMap 查询一条数据", "<script>SELECT %s FROM %s %s\n</script>"),
    SELECT_BATCH_BY_IDS("selectBatchIds", "根据ID集合,批量查询数据", "<script>SELECT %s FROM %s WHERE %s IN (%s) %s</script>"),
    SELECT_ONE("selectOne", "查询满足条件一条数据", "<script>%s SELECT %s FROM %s %s %s\n</script>"),
    SELECT_COUNT("selectCount", "查询满足条件总记录数", "<script>%s SELECT COUNT(%s) FROM %s %s %s\n</script>"),
    SELECT_LIST("selectList", "查询满足条件所有数据", "<script>%s SELECT %s FROM %s %s %s\n</script>"),
    SELECT_PAGE("selectPage", "查询满足条件所有数据(并翻页)", "<script>%s SELECT %s FROM %s %s %s\n</script>"),
    SELECT_MAPS("selectMaps", "查询满足条件所有数据", "<script>%s SELECT %s FROM %s %s %s\n</script>"),
    SELECT_MAPS_PAGE("selectMapsPage", "查询满足条件所有数据(并翻页)", "<script>\n %s SELECT %s FROM %s %s %s\n</script>"),
    SELECT_OBJS("selectObjs", "查询满足条件所有数据", "<script>%s SELECT %s FROM %s %s %s\n</script>");

以上就是一个比较初步的MP注入sql的原理介绍。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值