美文网首页
使用javassisit提升反射效率

使用javassisit提升反射效率

作者: 小偷阿辉 | 来源:发表于2021-01-08 17:11 被阅读0次

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: yczjy
 * @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: yczjy
 * @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: yczjy
 * @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: yczjy
 * @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: yczjy
 * @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;
        }
    }

}

4.执行结果

反射


反射执行结果

javassist执行结果


javassist执行结果

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

相关文章

  • 使用javassisit提升反射效率

    Javassist简介 Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京技术学院的数学...

  • 使用dynamic简化反射

    通常我们这样使用反射来调用一个函数 而使用dynamic可以简化这个流程,而且效率更高 当然优化后的反射效率更高,...

  • 怎么提升反射的效率

    缓存重复用到的对象 setAccessible(true) 之前我们说过当遇到私有变量和方法的时候,会用到setA...

  • 反射机制

    实例: 使用反射会使程序变慢(大约30倍)提高反射的执行效率:setAccessible(true) 禁用安全检...

  • 一位资深WorkNC大师的编程技巧分享,助你高效轻松高品质编程

    ★提升WORKNC编程效率的方法 使用WORKNC编程如能做到以下几点,将会有效提升编程效率 1.使用WORKNC...

  • ButterKnife实现原理

    代码自动生成 使用代码自动生成,一是为了提高编码的效率,二十避免在运行期大量使用反射,通过在编译期利用反射生成辅助...

  • 五招教你如何效率提升

    效率提升的包含五个方面:个人效率提升、组织效率提升、资产效率提升、战略效率提升、创新效率提升。 一、个人效率的提升...

  • IOC(二)-注解注入

    介绍 上一节中介绍了IOC的概念,以及使用反射注入对象,但是反射的效率比较低效,所以butterKnife换成了注...

  • Hbase相关组件分析

    Hbase hbase默认只有rowkey主键索引,所以使用其他字段查询效率并不高,若想提升查询效率,使用以下两种...

  • 精进管理人生,提升人生效率

    【书籍】《人生效率手册》 【标题】精进管理人生,提升效能 使用《人生效率手册》,确定人生目标,提升自己时间管理能力...

网友评论

      本文标题:使用javassisit提升反射效率

      本文链接:https://www.haomeiwen.com/subject/xgrfaktx.html