Javassist简介
Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京技术学院的数学和计算机科学系的 Shigeru Chiba 所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态AOP框架。
Javassist(JAVA编程ASSISTant)使Java字节码操作变得简单。 它是一个用Java编辑字节码的类库; 它使Java程序能够在运行时定义新类,并在JVM加载时修改类文件。 与其他类似的字节码编辑器不同,Javassist提供两个级别的API:源级别和字节码级别。 如果用户使用源级API,他们可以在不知道Java字节码规范的情况下编辑类文件。 整个API仅使用Java语言的词汇表进行设计。 您甚至可以以源文本的形式指定插入的字节码; Javassist即时编译它。 另一方面,字节码级API允许用户直接编辑类文件作为其他编辑器。
同ASM比较
同类型的字节码编辑框架还有如cjlib使用的ASM动态字节码技术,二者对比如下,目前主流的还是使用的ASM,因为生成字节码速度更快,所以运行起来ASM是会比javassist快很多,但是一般不会直接使用ASM,因为ASM是需要手写字节码。
高并发环境下,javassist能明显提升反射的效率,通过手写一段ORM代码实现反射
1.创建表结构
生成10000条模拟数据
CREATE TABLE `t_user` (
`user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`user_id` int(11) NOT NULL,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
##创建存储过程批量生成1w条数据
CREATE PROCEDURE callback()
BEGIN
DECLARE num INT;
SET num = 1;
WHILE
num <= 10000 DO
INSERT INTO t_user(user_id, user_name, `password`)
VALUES( num,CONCAT("user_name", num),CONCAT("password", num));
SET num = num + 1;
END WHILE;
END;
CALL callback;
2.加入maven依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
3.创建字段注解和实体类
创建数据库实体映射注解
/**
* 数据表中的列
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String name();
}
/**
* @program: ormtest
* @description: 用户实体
* @author: xml
* @create: 2021-01-08 09:04
**/
public class UserEntity {
@Column(name = "user_id")
private Integer userId;
@Column(name = "user_name")
private String userName;
@Column(name = "password")
private String password;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
3.反射和javassist对比
3.1反射实现
/**
* @program: ormtest
* @description: 应用002
* @author: xml
* @create: 2021-01-08 09:11
**/
public class App001 {
/**
* 应用程序主函数
*
* @param argvArray 参数数组
* @throws Exception
*/
static public void main(String[] argvArray) throws Exception {
(new App001()).start();
}
/**
* 测试开始
*/
private void start() throws Exception {
// 加载 Mysql 驱动
Class.forName("com.mysql.cj.jdbc.Driver").newInstance();
// 数据库连接地址
String dbConnStr = "jdbc:mysql://192.168.81.129:3306/temp20210108?user=root&password=shineCoding&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC";
// 创建数据库连接
Connection conn = DriverManager.getConnection(dbConnStr);
// 简历陈述对象
final Statement stmt = conn.createStatement();
// 获取开始时间
final UserEntity_Helper userEntity_helper = new UserEntity_Helper();
// 创建 SQL 查询
// ormtest 数据库中有个 t_user 数据表,
// t_user 数据表包括三个字段: user_id、user_name、password,
// t_user 数据表有 20 万条数据
String sql = "select * from t_user limit 200000";
long t0 = System.currentTimeMillis();
// 执行查询
ResultSet rs = null;
try {
rs = stmt.executeQuery(sql);
while (rs.next()) {
userEntity_helper.create(UserEntity.class, rs);
//
// 关于上面这段代码,
// 我们是否可以将其封装到一个助手类里??
// 这样做的好处是:
// 当实体类发生修改时, 只需要改助手类就可以了...
//
}
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
// 获取结束时间
long t1 = System.currentTimeMillis();
// 关闭数据库连接
stmt.close();
conn.close();
// 打印实例化花费时间
System.out.println("实例化花费时间 = " + (t1 - t0) + "ms");
}
}
实体转换工具类
/**
* 用户实体助手类
*/
public class UserEntity_Helper {
/**
* 将数据集装换为实体对象
*
* @param rs 数据集
* @return
* @throws Exception
*/
public <TEntity> TEntity create(Class<TEntity> classType,ResultSet rs) throws Exception {
if (null == rs) {
return null;
}
// 创建新的实体对象
UserEntity ue = new UserEntity();
Field[] fields = ue.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Column annotation = field.getAnnotation(Column.class);
if(null==annotation){
continue;
}
String name = annotation.name();
if(null==name||"".equals(name)){
continue;
}
Object object = rs.getObject(name);
if(null==object){
continue;
}
field.set(ue, object);
}
return (TEntity)ue;
}
}
3.2.javassist动态字节码实现
/**
* @program: ormtest
* @description: 应用002
* @author: xml
* @create: 2021-01-08 09:11
**/
public class App002 {
/**
* 应用程序主函数
*
* @param argvArray 参数数组
* @throws Exception
*/
static public void main(String[] argvArray) throws Exception {
(new App002()).start();
}
/**
* 测试开始
*/
private void start() throws Exception {
// 加载 Mysql 驱动
Class.forName("com.mysql.cj.jdbc.Driver").newInstance();
// 数据库连接地址
String dbConnStr = "jdbc:mysql://192.168.81.129:3306/temp20210108?user=root&password=shineCoding&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC";
// 创建数据库连接
Connection conn = DriverManager.getConnection(dbConnStr);
// 简历陈述对象
final 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 200000";
// 执行查询
ResultSet rs = null;
AbstractEntityHelper abstractEntityHelper = EntityHelperFactory.getEntityHelper(UserEntity.class);
// 获取开始时间
long t0 = System.currentTimeMillis();
try {
rs = stmt.executeQuery(sql);
while (rs.next()) {
abstractEntityHelper.create(rs);
}
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
// 获取结束时间
long t1 = System.currentTimeMillis();
// 关闭数据库连接
stmt.close();
conn.close();
// 打印实例化花费时间
System.out.println("实例化花费时间 = " + (t1 - t0) + "ms");
}
}
抽奖实体转换工具类
/**
* @program: ormtest
* @description: 抽象助手类
* @author: xml
* @create: 2021-01-08 10:13
**/
public abstract class AbstractEntityHelper {
/**
* 将数据集转换为实体对象
*
* @param rs 数据集
* @return
*
*/
public abstract Object create(ResultSet rs) throws Exception;
}
javassist具体实现:实体转换工厂EntityHelperFactory
/**
* @program: ormtest
* @description: 实体工厂类
* @author: xml
* @create: 2021-01-08 10:14
**/
public class EntityHelperFactory {
private static final Map<Class<?>,AbstractEntityHelper> HELPER_MAP=new HashMap<Class<?>, AbstractEntityHelper>();
/**
* 私有化类默认构造器
*/
private EntityHelperFactory() {
}
/**
* 获取帮助
*
* @param entityClazz 实体类
* @return
*/
public static AbstractEntityHelper getEntityHelper(Class<?> entityClazz) {
// 这里需要全新设计,
if(null==entityClazz){
return null;
}
if(null!=HELPER_MAP.get(entityClazz)){
return HELPER_MAP.get(entityClazz);
}
// 接下来就该请出 javassist 了!
ClassPool pool = ClassPool.getDefault();//获取类池 用于创建CtClass
pool.appendSystemPath();
//导入包
// import com.shine.entity.Column;
// import java.lang.reflect.Field;
// import java.sql.ResultSet;
pool.importPackage("com.shine.entity.Column");
pool.importPackage("java.lang.reflect.Field");
pool.importPackage("java.sql.ResultSet");
//获取助手抽象类
try {
CtClass abstractEntityHelper = pool.getCtClass(AbstractEntityHelper.class.getName());
//助手实现类名称
final String extClassName=entityClazz.getName()+"_Helper";
//创建实现类
CtClass ctClass = pool.makeClass(extClassName, abstractEntityHelper);
//创建无参构造器
CtConstructor ctConstructor = new CtConstructor(new CtClass[0], ctClass);
ctConstructor.setBody("{}");
//加入构造器
ctClass.addConstructor(ctConstructor);
//创建函数代码
final StringBuffer sb=new StringBuffer();
sb.append("public Object create(java.sql.ResultSet rs) throws Exception {\n");
sb.append("if (null == rs) {\n" +
" return null;\n" +
" }\n");
sb.append(entityClazz.getName()+" ue = new "+entityClazz.getName()+"();\n");
Field[] fields = entityClazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Column column = field.getAnnotation(Column.class);
if(null==column){
continue;
}
String columnName = column.name();
if(null==columnName||"".equals(columnName)){
continue;
}
String fileName=field.getName();
sb.append("ue.set"+fileName.substring(0, 1).toUpperCase()+fileName.substring(1)+"(");
if(field.getType().equals(Integer.class)){
sb.append("Integer.valueOf(rs.getInt(\""+columnName+"\"))");
}else if(field.getType().equals(String.class)){
sb.append("rs.getString(\""+columnName+"\")");
}
sb.append( ");\n");
}
sb.append("return ue;\n");
sb.append("}");
CtMethod newMethod = CtNewMethod.make(sb.toString(), ctClass);
ctClass.addMethod(newMethod);
ctClass.writeFile("G:\\MySource");
Class aClass = ctClass.toClass();
AbstractEntityHelper destObj = (AbstractEntityHelper)aClass.newInstance();
HELPER_MAP.put(entityClazz, destObj);
return destObj;
} catch (NotFoundException e) {
e.printStackTrace();
return null;
} catch (CannotCompileException e) {
e.printStackTrace();
return null;
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
} catch (InstantiationException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
标签:反射,return,String,rs,效率,user,null,public,javassisit From: https://blog.csdn.net/ximaiyao1984/article/details/142303189对比之下每次执行查询时是用反射的方式,每次都会单独去跟实体匹配映射,是用javassist生成了代理类后被放入Map容器中缓存,效率大大提升,尤其在高并发的应用场景