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,运行结果