Java 反射

手写代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* 测试开始
*/
private void start() throws Exception {
// 加载 Mysql 驱动
Class.forName("com.mysql.cj.jdbc.Driver").newInstance();
// 数据库连接地址
String dbConnStr = "jdbc:mysql://localhost:3306/ormtest?user=root&password=root";
// 创建数据库连接
Connection conn = DriverManager.getConnection(dbConnStr);
// 简历陈述对象
Statement stmt = conn.createStatement();

// 创建 SQL 查询
// ormtest 数据库中有个 t_user 数据表,
// t_user 数据表包括三个字段: user_id、user_name、password,
// t_user 数据表有 20 万条数据
String sql = "select * from t_user limit 20000";

// 执行查询
ResultSet rs = stmt.executeQuery(sql);

// 获取开始时间
long t0 = System.currentTimeMillis();

while (rs.next()) {
// 创建新的实体对象
UserEntity ue = new UserEntity();

ue._userId = rs.getInt("user_id");
ue._userName = rs.getString("user_name");
ue._password = rs.getString("password");
//
// 关于上面这段代码,
// 我们是否可以将其封装到一个助手类里??
// 这样做的好处是:
// 当实体类发生修改时, 只需要改助手类就可以了...
//
}

// 获取结束时间
long t1 = System.currentTimeMillis();

// 关闭数据库连接
stmt.close();
conn.close();

// 打印实例化花费时间 大概 790ms左右
System.out.println("实例化花费时间 = " + (t1 - t0) + "ms");
}

反射代码

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 自定义注解,标识数据表中的列
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
/**
* 列名称
*
* @return
*/
String name();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 用户实体
*/
public class UserEntity {
/**
* 用户 Id
*/
@Column(name = "user_id")
public int _userId;

/**
* 用户名
*/
@Column(name = "user_name")
public String _userName;

/**
* 密码
*/
@Column(name = "password")
public String _password;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
* 实体助手类, 这个更通用
*/
public class XxxEntity_Helper {
/**
* 将数据集装换为实体对象
*
* @param entityClazz 实体类
* @param rs 数据集
* @param <TEntity> 实体类
* @return
* @throws Exception
*/
public <TEntity> TEntity create(Class<TEntity> entityClazz, ResultSet rs) throws Exception {
if (null == rs) {
return null;
}

//
// 更通用的助手类,
// 甭管实体类是哪个, 也甭管实体类有多少属性,
// 全灭!
// 但是,
// 就是性能太差了...
//
// 创建新的实体对象
Object newEntity = entityClazz.newInstance();

// 获取类的字段数组
Field[] fArray = entityClazz.getFields();

for (Field f : fArray) {
// 获取字段上注解
Column annoColumn = f.getAnnotation(Column.class);

if (annoColumn == null) {
// 如果注解为空,
// 则直接跳过...
continue;
}

// 获取列名称
String colName = annoColumn.name();
// 从数据库中获取列值
Object colVal = rs.getObject(colName);

if (colVal == null) {
// 如果列值为空,
// 则直接跳过...
continue;
}

// 设置字段值
f.set(newEntity, colVal);
}

return (TEntity) newEntity;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
// 创建助手类
XxxEntity_Helper helper = new XxxEntity_Helper();

while (rs.next()) {
// 反射创建新的实体对象
UserEntity ue = helper.create(UserEntity.class, rs);
}

// 打印实例化花费时间 1700ms
System.out.println("实例化花费时间 = " + (t1 - t0) + "ms");
// 注意: 到这里运行时间翻倍了!
// 利用反射确实可以获得良好的通用性,
// 但是却损失了性能...

javassist

1
2
3
4
5
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 抽象的实体助手
*/
public abstract class AbstractEntityHelper {
/**
* 将数据集转换为实体对象
*
* @param rs 数据集
* @return
*
*/
public abstract Object create(ResultSet rs);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/**
* 实体助手工厂
*/
public final class EntityHelperFactory {
/**
* 助手字典
*/
private static final Map<Class<?>, AbstractEntityHelper> _entityHelperMap = new HashMap<>();

/**
* 私有化类默认构造器
*/
private EntityHelperFactory() {
}

/**
* 获取帮助
*
* @param entityClazz 实体类
* @return 实体助手
* @throws Exception
*/
public static AbstractEntityHelper getEntityHelper(Class<?> entityClazz) throws Exception {
if (null == entityClazz) {
// 如果参数对象为空,
// 则直接退出!
return null;
}

// 获取帮助对象
AbstractEntityHelper helperObj = _entityHelperMap.get(entityClazz);

if (helperObj != null) {
// 如果帮助对象不为空,
// 则直接返回!
return helperObj;
}

// 使用 Javassist 动态生成 Java 字节码
///////////////////////////////////////////////////////////////////////
//
// 获取类池
ClassPool pool = ClassPool.getDefault();
pool.appendSystemPath();

//
// 导入相关类, 生成以下代码:
// import java.sql.ResultSet
// import org.ormtest.entity.UserEntity
// import ...
pool.importPackage(ResultSet.class.getName());
pool.importPackage(entityClazz.getName());

// 抽象的助手类
CtClass abstractEntityHelperClazz = pool.getCtClass(AbstractEntityHelper.class.getName());
// 助手实现类名称
final String helperImplClazzName = entityClazz.getName() + "_Helper";

//
// 创建助手类, 会生成如下代码:
// public class UserEntity_Helper extends AbstractEntityHelper { ...
CtClass helperClazz = pool.makeClass(helperImplClazzName, abstractEntityHelperClazz);

//
// 创建默认构造器, 会生成如下代码:
// public UserEntity_Helper() {}
CtConstructor constructor = new CtConstructor(new CtClass[0], helperClazz);
// 空函数体
constructor.setBody("{}");
// 添加默认构造器
helperClazz.addConstructor(constructor);

// 用于创建函数代码字符串
final StringBuffer sb = new StringBuffer();
// 添加一个函数, 也就是实现抽象类中的 create 函数
sb.append("public Object create(java.sql.ResultSet rs) throws Exception {\n");
// 生成以下代码:
// UserEntity obj = new UserEntity();
sb.append(entityClazz.getName())
.append(" obj = new ")
.append(entityClazz.getName())
.append("();\n");

// 通过反射方式获取类的字段数组,
// 并生成代码
///////////////////////////////////////////////////////////////////////
//
// 获取类的字段数组并生成代码
Field[] fArr = entityClazz.getFields();

for (Field f : fArr) {
// 获取字段上注解
Column annoColumn = f.getAnnotation(Column.class);

if (annoColumn == null) {
// 如果注解为空,
// 则直接跳过...
continue;
}

// 获取列名称
String colName = annoColumn.name();

if (f.getType() == Integer.TYPE) {
// 生成如下代码:
// obj._userId = rs.getInt("user_id");
sb.append("obj.")
.append(f.getName())
.append(" = rs.getInt(\"")
.append(colName)
.append("\");\n");
} else if (f.getType().equals(String.class)) {
// 生成如下代码:
// obj._userName = rs.getString("user_name");
sb.append("obj.")
.append(f.getName())
.append(" = rs.getString(\"")
.append(colName)
.append("\");\n");
} else {
// 不支持的类型...
// 如果需要支持 float、long、boolean 等类型,
// 接着往下写就可以了
}
}

sb.append("return obj;\n");
sb.append("}");

// 创建解析方法
CtMethod cm = CtNewMethod.make(
sb.toString(), helperClazz
);
// 添加方法
helperClazz.addMethod(cm);
// 调试文件
helperClazz.writeFile("C:/Data/Temp+Test/debug-java");
// 获取 Java 类
Class<?> javaClazz = helperClazz.toClass();

// 创建帮助对象实例
///////////////////////////////////////////////////////////////////////
//
// 创建帮助实例
helperObj = (AbstractEntityHelper) javaClazz.newInstance();
// 添加到字典
_entityHelperMap.put(entityClazz, helperObj);

return helperObj;
}
}
1
2
3
4
5
6
7
8
9
10
// 创建助手类, 这里采用全新设计的工厂类!
AbstractEntityHelper helper = EntityHelperFactory.getEntityHelper(UserEntity.class);

while (rs.next()) {
// 创建新的实体对象
UserEntity ue = (UserEntity) helper.create(rs);
}

// 打印赋值时间消耗 800ms
System.out.println("赋值花费时间 = " + (t1 - t0) + "ms");

最后更新: 2021年03月07日 15:59

原始链接: https://midkuro.gitee.io/2020/10/30/java-invoke/

× 请我吃糖~
打赏二维码