【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);
    }
目录
相关文章
|
9月前
|
安全 IDE Java
Java记录类型(Record):简化数据载体类
Java记录类型(Record):简化数据载体类
615 143
|
9月前
|
Java 测试技术
Java浮点类型详解:使用与区别
Java中的浮点类型主要包括float和double,它们在内存占用、精度范围和使用场景上有显著差异。float占用4字节,提供约6-7位有效数字;double占用8字节,提供约15-16位有效数字。float适合内存敏感或精度要求不高的场景,而double精度更高,是Java默认的浮点类型,推荐在大多数情况下使用。两者都存在精度限制,不能用于需要精确计算的金融领域。比较浮点数时应使用误差范围或BigDecimal类。科学计算和工程计算通常使用double,而金融计算应使用BigDecimal。
3245 102
|
7月前
|
存储 算法 安全
Java集合框架:理解类型多样性与限制
总之,在 Java 题材中正确地应对多样化与约束条件要求开发人员深入理解面向对象原则、范式编程思想以及JVM工作机理等核心知识点。通过精心设计与周密规划能够有效地利用 Java 高级特征打造出既健壮又灵活易维护系统软件产品。
189 7
|
7月前
|
Java Unix Go
【Java】(8)Stream流、文件File相关操作,IO的含义与运用
Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。!但本节讲述最基本的和流与 I/O 相关的功能。我们将通过一个个例子来学习这些功能。
289 2
|
8月前
|
Java 开发者
Java 函数式编程全解析:静态方法引用、实例方法引用、特定类型方法引用与构造器引用实战教程
本文介绍Java 8函数式编程中的四种方法引用:静态、实例、特定类型及构造器引用,通过简洁示例演示其用法,帮助开发者提升代码可读性与简洁性。
|
9月前
|
安全 算法 Java
Java泛型编程:类型安全与擦除机制
Java泛型详解:从基础语法到类型擦除机制,深入解析通配符与PECS原则,探讨运行时类型获取技巧及最佳实践,助你掌握泛型精髓,写出更安全、灵活的代码。
|
11月前
|
安全 Java 数据库连接
让我们讲解一下 Map 集合遍历的方式
我是小假 期待与你的下一次相遇 ~
375 43
|
存储 前端开发 API
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
该文章详细介绍了ES6中Set和Map数据结构的特性和使用方法,并探讨了它们在前端开发中的具体应用,包括如何利用这些数据结构来解决常见的编程问题。
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
|
存储 安全 Java
java集合框架复习----(4)Map、List、set
这篇文章是Java集合框架的复习总结,重点介绍了Map集合的特点和HashMap的使用,以及Collections工具类的使用示例,同时回顾了List、Set和Map集合的概念和特点,以及Collection工具类的作用。
java集合框架复习----(4)Map、List、set