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

简介: 【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);
    }
目录
相关文章
|
2月前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
1325 1
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
2月前
|
JSON 前端开发 JavaScript
Java属性为什么不能是is开头的boolean
在Java实体类中,阿里规约要求boolean属性不应以is开头。文章通过实际案例分析了isUpdate字段在JSON序列化过程中变为update的问题,并提供了自定义get方法或使用@JSONField注解两种解决方案,建议遵循规约避免此类问题。
Java属性为什么不能是is开头的boolean
|
3月前
|
存储 Java 开发者
Java 中 Set 类型的使用方法
【10月更文挑战第30天】Java中的`Set`类型提供了丰富的操作方法来处理不重复的元素集合,开发者可以根据具体的需求选择合适的`Set`实现类,并灵活运用各种方法来实现对集合的操作和处理。
|
3月前
|
Java 编译器 开发者
Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面
本文探讨了Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面,帮助开发者提高代码质量和程序的健壮性。
111 2
|
3月前
|
存储 Java 编译器
Java泛型类型擦除以及类型擦除带来的问题
泛型擦除是指Java编译器在编译期间会移除所有泛型信息,使所有泛型类型在运行时都变为原始类型。例如,`List&lt;String&gt;` 和 `List&lt;Integer&gt;` 在JVM中都视为 `List`。因此,通过 `getClass()` 比较两个不同泛型类型的 `ArrayList` 实例会返回 `true`。此外,通过反射调用 `add` 方法可以向 `ArrayList&lt;Integer&gt;` 中添加字符串,进一步证明了泛型信息在运行时被擦除。
75 2
|
3月前
|
Java
Java中的反射机制与应用实例
【10月更文挑战第22天】Java作为一门面向对象的编程语言,提供了丰富的特性来支持对象的创建、操作和交互。其中,反射机制是Java的一项核心特性,它允许程序在运行时动态地获取类的信息、创建对象、调用方法、访问属性等。本文将从三个部分探讨Java中的反射机制及其应用实例:一是反射机制的基本概念和原理;二是反射机制在Java中的应用场景;三是通过实例深入理解反射机制的使用方法和技巧。
38 4
|
5月前
|
Go 定位技术 索引
Go 语言Map(集合) | 19
Go 语言Map(集合) | 19
|
5月前
|
存储 前端开发 API
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
该文章详细介绍了ES6中Set和Map数据结构的特性和使用方法,并探讨了它们在前端开发中的具体应用,包括如何利用这些数据结构来解决常见的编程问题。
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
|
6月前
|
存储 安全 Java
java集合框架复习----(4)Map、List、set
这篇文章是Java集合框架的复习总结,重点介绍了Map集合的特点和HashMap的使用,以及Collections工具类的使用示例,同时回顾了List、Set和Map集合的概念和特点,以及Collection工具类的作用。
java集合框架复习----(4)Map、List、set
|
6月前
|
Java
【Java集合类面试二十二】、Map和Set有什么区别?
该CSDN博客文章讨论了Map和Set的区别,但提供的内容摘要并未直接解释这两种集合类型的差异。通常,Map是一种键值对集合,提供通过键快速检索值的能力,而Set是一个不允许重复元素的集合。

热门文章

最新文章