1. why
为什么要拦截传递给 mapper 文件的参数对象呢?因为要对指定属性设置默认值。如何拦截传递给 mapper 文件的参数对象可以参考《使用(org.apache.ibatis.plugin.Interceptor)拦截器实现全局参数注入》这里我们只贴出处理拦截对象的核心方法。
2. code
既然是核心方法,无关的@Override
方法不再贴出, 参数对象是Map类型的不是重点,这里主要看一下封装对象借助反射机制通过 getter 和 setter 方法获取和设置指定属性值的操作。
public class SchemaInterceptor implements Interceptor { /** * mapper.xml 使用SCHEMA时的参数名称 */ private static final String SCHEMA = "schemaName"; /** * 设置默认的schema */ private String schema = "public"; /** * 拦截到的动态SQL */ private Set<Integer> sourceStorage = new HashSet<>(); @Override public Object intercept(Invocation invocation) throws Throwable { Object[] args = invocation.getArgs(); MappedStatement mappedStatement = (MappedStatement) args[0]; SqlSource sqlSource = mappedStatement.getSqlSource(); // 只拦截动态SQL if (sqlSource instanceof DynamicSqlSource) { // 获取到sqlNode对象 Field field = DynamicSqlSource.class.getDeclaredField("rootSqlNode"); field.setAccessible(true); SqlNode sqlNode = (SqlNode) field.get(sqlSource); Object argParameter = args[1]; // 获取传递给 mapper 的参数对象 if (!sourceStorage.contains(sqlSource.hashCode()) && argParameter != null) { if (argParameter instanceof HashMap) { // 处理 map 类型的参数 Map<String, Object> argMap = (Map<String, Object>) argParameter; // 判断是否传递 schemaName或schema 如果已经传递则使用用户传递的值 否则使用默认值 String schemaNameStr = "schemaName", schemaStr = "schema"; if (StringUtils.isEmpty(MapUtils.getString(argMap, schemaNameStr)) && StringUtils.isEmpty(MapUtils.getString(argMap, schemaStr))) { SqlNode proxyNode = proxyNode(sqlNode); field.set(sqlSource, proxyNode); } } else { // 处理封装成请求对象类型的参数 Class<?> clz = argParameter.getClass(); // 获取实体类的所有属性 Field[] fields = clz.getDeclaredFields(); for (Field f : fields) { String fieldName = f.getName(); Class<?> fieldType = f.getType(); // 处理 schemaName 属性 if (SCHEMA.equals(fieldName)) { // 获取 schemaName 属性的 getter 方法 String methodName = getMethodName(fieldName); String getName = "get" + methodName; Method getFieldValue = clz.getMethod(getName); String val = (String) getFieldValue.invoke(argParameter); // schemaName 属性值为空则使用默认值 if (StringUtils.isBlank(val)) { String setName = "set" + methodName; Method setFieldValue = clz.getMethod(setName, fieldType); setFieldValue.invoke(argParameter, schema); } } } } sourceStorage.add(sqlSource.hashCode()); } } return invocation.proceed(); } /** * 通过动态代理对象 添加schema参数 * * @param sqlNode SQL节点 * @return SqlNode */ private SqlNode proxyNode(SqlNode sqlNode) { return (SqlNode) Proxy.newProxyInstance(sqlNode.getClass().getClassLoader(), new Class[]{SqlNode.class}, new SqlNodeInvocationHandler(sqlNode)); } private class SqlNodeInvocationHandler implements InvocationHandler { private SqlNode target; SqlNodeInvocationHandler(SqlNode target) { super(); this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { DynamicContext context = (DynamicContext) args[0]; setSchema(schema); context.getBindings().put(SCHEMA, schema); return method.invoke(target, args); } } /** * 给schema 添加. * * @param schema schemaName */ private void setSchema(String schema) { String pointStr = "."; if (StringUtils.isNotBlank(schema)) { if (!schema.endsWith(pointStr)) { schema += pointStr; } } this.schema = schema; } /** * 将首字母变成大写 * * @param fieldName 字段名称 * @return 首字母大写的字段名称 */ private static String getMethodName(String fieldName) { byte[] items = fieldName.getBytes(); items[0] = (byte) ((char) items[0] - 'a' + 'A'); return new String(items); }
方法解析
拦截到的参数对象是 Object 类型,可以通过方法名获取其 getter 和 setter 方法,使用获取到的方法执行即可获取或设置指定的属性值:
Object argParameter = args[1]; // 处理封装成请求对象类型的参数 Class<?> clz = argParameter.getClass(); // 获取实体类的所有属性 Field[] fields = clz.getDeclaredFields(); for (Field f : fields) { // 属性的名称 String fieldName = f.getName(); // 属性的类型 Class<?> fieldType = f.getType(); // 处理 schemaName 属性 if (SCHEMA.equals(fieldName)) { // 获取 schemaName 属性的 getter 方法 String methodName = getMethodName(fieldName); // 方法名 String getName = "get" + methodName; // 获取到的 getter 方法 Method getFieldValue = clz.getMethod("get" + methodName); // 执行 getter 方法 String val = (String) getFieldValue.invoke(argParameter); // schemaName 属性值为空则使用默认值 if (StringUtils.isBlank(val)) { // 方法名 String setName = "set" + methodName; // 获取 setter 方法 需要传递当前属性的类型 Method setFieldValue = clz.getMethod(setName, fieldType); // 执行 setter 方法 setFieldValue.invoke(argParameter, schema); } } }
将首字母大写:
/** * 将首字母变成大写 * * @param fieldName 字段名称【字段的第一位必须是小写的字母】 * @return 首字母大写的字段名称 */ private static String getMethodName(String fieldName) { byte[] items = fieldName.getBytes(); items[0] = (byte) ((char) items[0] - 'a' + 'A'); return new String(items); }