【Java代码】反射机制处理传递给mapper文件的非Map类型参数对象(指定属性为空则设置默认值)

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);
    }
  • 18
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 18
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

シ風箏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值