zoukankan      html  css  js  c++  java
  • java 通过注解和泛型,反射获取泛型,并实例化

    JAVA 的泛型加大了 编程的灵活性,在配合上反射,可以让我们省去大量的重复代码,当你用 SpringBoot 整合 JPA 的时候

    你会发现,你的 DAO 层只需要继承 BaseDao,在显示标明泛型的时候,你定义的接口就可以有一些常用的增删改查的方法

    笔者也想实现的类似的功能,但是在使用泛型的时候发现 T.calss 的语法是不成立的,这涉及到JVM虚拟机对于泛型的擦除,

    我们要怎么样拿到 T 这个泛型的字节码和实例呢?

    1,其实 JDK 给我们提供了相应的方法如下:

    Type type = this.getClass().getGenericSuperclass();
    Type[] typeArr = ((ParameterizedType) type).getActualTypeArguments();
    Class<T> mtClass = (Class<T>) (typeArr[0]);

    2,以上代码我们就获得了 T 这个泛型在运行时的类型了,有了字节码,实例化,获取字段,获取注解什么的就不在话下了

    3,需要注意的是,只有满足以下的申明方式,在父类 A 中才能正常使用以上方法

    class A<T> {}

    class B extends A<User>{}

    5,这就导致我们对每个类进行操作是都需要创建一个对应的类来继承A类,好处就在于子类不需要写任何代码,

    6,不知道 JDK 什么时候能实现 T.class 的功能就好了 (个人浅见)

    《 二 》下面做一个简单的列子

    1,声明两个注解 @KeyId  @TableName

    package com.hwq.annotate;
    import java.lang.annotation.*;
    
    @Documented                          // 是否出现在文档        
    @Target(ElementType.FIELD)           // 作用范围,常用 FIELD 字段等上 TYPE 类上
    @Retention(RetentionPolicy.RUNTIME)  // 作用周期,一般为运行时
    public @interface KeyID {
        String value();
    }
    package com.hwq.annotate;
    import java.lang.annotation.*;
    
    @Documented
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TableName {
        String value();
    }

    2,声明一个实体类,贴上自己写的注解,这里由于懒得写 get set 方法,笔者使用了 lombok 库

    package com.hwq.reflex;
    
    import com.hwq.annotate.KeyID;
    import com.hwq.annotate.TableName;
    import lombok.Getter;
    import lombok.Setter;
    
    @Getter
    @Setter
    @TableName("user")
    public class User {
    
        @KeyID("ID")
        Integer id;
        String name;
        Integer sex;
    
    }

    3,写一个基础类,就是那个实现公共方法的类,我在里面写了几个拼接 增删改语句 的方法, 大家主要看 getTableInfo 这个核心方法

    package com.hwq.reflex;
    
    
    import com.hwq.annotate.KeyID;
    import com.hwq.annotate.TableName;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.util.ArrayList;
    import java.util.List;
    
    public class SqlHelper<T> {
    
        protected String tableName = null;                          // 表名
        protected String id;                                        // 主键在 实体类的 名称
        protected String key;                                       // 主键在 数据库的 名称
        protected List<String> fieldList = new ArrayList<String>(); // 字段数据
        Field[] fieldArr;                                           // 反射出来的字段对象,在取值的时候有用
    
    
        /**
         * 获取泛型类的方法,由于 JAVA 虚拟机的泛型擦除机制,我们无法获取对应的实体类
         * 该方法可以在子类明确表示实体类的情况下,获取到实体类注解以及字段
         * @return
         */
        private synchronized void getTableInfo() {
            if (tableName == null) {
                Type type = this.getClass().getGenericSuperclass();
                Type[] typeArr = ((ParameterizedType) type).getActualTypeArguments();
                Class<T> mtClass = (Class<T>) (typeArr[0]);
                TableName table = (TableName) mtClass.getAnnotation(TableName.class); // 获取类上 @TableName 注解
                tableName = table.value();
                fieldArr = mtClass.getDeclaredFields();
                List<String> keyList = new ArrayList<String>();
                List<String> idList = new ArrayList<String>();
                for (int i = 0; i < fieldArr.length; i++) {
                    fieldArr[i].setAccessible(true);
                    KeyID keyID = fieldArr[i].getAnnotation(KeyID.class);  // 获取类字段上的 @KeyId 注解
                    if (keyID != null) {
                        key = keyID.value();
                        id = fieldArr[i].getName();
                    }
                    fieldList.add(fieldArr[i].getName());                  // 获取类的所有字段
                }
            }
        }
    
        /**
         * 新增的方法
         */
        public void save(T t) {
            getTableInfo();
            StringBuilder insertSql = new StringBuilder(192);
            StringBuilder filedStr = new StringBuilder(64);
            StringBuilder valueStr = new StringBuilder(64);
            insertSql.append("INSERT INTO ");
            insertSql.append(tableName);
            filedStr.append(" (");
            valueStr.append(" VALUE (");
            try {
                for (int i = 0; i < fieldList.size(); i++) {
                    String field = fieldList.get(i);
                    if (field == id) continue;
                    filedStr.append(field);
                    filedStr.append(",");
                    valueStr.append("'");
                    valueStr.append(fieldArr[i].get(t));
                    valueStr.append("',");
                }
            } catch (Exception ex) {
                throw new RuntimeException(ex.getMessage());
            }
            insertSql.append(filedStr.substring(0, filedStr.length() - 1));
            insertSql.append(")");
            insertSql.append(valueStr.substring(0 ,valueStr.length() - 1));
            insertSql.append(")");
            String sql = insertSql.toString();
            System.out.println(insertSql.toString());
    
        }
    
        /**
         * 编辑的方法,根据主键编辑
         */
        public void edit(T t) {
            getTableInfo();
            StringBuilder updateSql = new StringBuilder(192);
            updateSql.append("UPDATE ");
            updateSql.append(tableName);
            updateSql.append(" SET ");
            int count = 0;
            String idValue = null;
            try {
                for (int i = 0; i < fieldList.size(); i++) {
                    Object value = fieldArr[i].get(t);
                    if (value == null) continue;
                    String field = fieldList.get(i);
                    if (field == id) {
                        idValue = value.toString();
                    } else {
                        updateSql.append(field);
                        updateSql.append("='");
                        updateSql.append(value);
                        updateSql.append("',");
                        count ++;
                    }
                }
            } catch (Exception ex) {
                throw new RuntimeException(ex.getMessage());
            }
            if (count == 0 || idValue == null) {
                throw new RuntimeException("主键为空或实体类没有需要修改的字段");
            }
            String sql = updateSql.substring(0, updateSql.length() - 1);
            sql += " WHERE " + key + "='" + idValue + "'";
            System.out.println(sql);
    
        }
    
        /**
         * 删除的方法,根据主键,单个删除
         */
        public void removeById(int id) {
            getTableInfo();
            StringBuilder deleteSql = new StringBuilder(128);
            deleteSql.append("DELETE FROM ");
            deleteSql.append(tableName);
            deleteSql.append(" WHERE ");
            deleteSql.append(key);
            deleteSql.append("=");
            deleteSql.append(id);
            String sql = deleteSql.toString();
            System.out.println(sql);
        }
        
    }

    4,写一个子类继承上面的类,子类不需要任何代码,记住一定要标明泛型的具体类型

    package com.hwq.reflex;
    
    public class Service extends SqlHelper<User> {
        
    }

    5,测试类

    package com.hwq.reflex;
    
    import java.lang.reflect.Field;
    
    public class Index {
    
        public static void main(String[] args) {
    
            Service sqlHelper = new Service();  // 记住使用继承了基类的子类,不要直接使用基类
    
            User user = new User();
            user.setId(1);
            user.setName("张三");
            user.setSex(1);
    
            sqlHelper.save(user);
            sqlHelper.edit(user);
            sqlHelper.removeById(12);
        }
    }

    6,运行结果

  • 相关阅读:
    第三次作业
    利用LINQ to SQL 增删改查本地数据库
    【转】C#,回车,换行
    【转】C#3.0编码习惯与命名规则
    Connect to the DSP on C6A8168/DM8168/DM8148 using CCS
    【转】OpenCV 移植学习EMCV
    目标跟踪小结
    利用TortoiseSVN获取最新版本的OpenCV源码
    转图像灰度化方法总结及其VC实现
    OpenCV读取视频的格式注意点
  • 原文地址:https://www.cnblogs.com/lovling/p/9995701.html
Copyright © 2011-2022 走看看