javassisit提升反射效率

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;
        }
    }

}

对比之下每次执行查询时是用反射的方式,每次都会单独去跟实体匹配映射,是用javassist生成了代理类后被放入Map容器中缓存,效率大大提升,尤其在高并发的应用场景

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

讓丄帝愛伱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值