使用asm动态为redisson定时任务生成目标类

本文介绍了一种使用ASM字节码操作库,在Redisson框架中动态生成定时任务类的方法,以支持任意类的无参方法作为定时任务的目标。通过自定义ClassLoader和ClassWriter,实现了类的动态构建,包括字段、注解、构造方法和业务方法的生成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Redisson定时任务目标类只接收Runnable实例,我想要的是它可以使用任意类的无参方法作为目标类,所以选择用asm实现了一个动态生成目标类的方法。


public class TaskGenerator  extends ClassLoader{


    private TaskGenerator() {
        super(TaskGenerator.class.getClassLoader());
    }

    public final static TaskGenerator classLoader=new TaskGenerator();

    private static ConcurrentMap<String,byte[]> clzBytesCache=new ConcurrentHashMap<>();


    private Class loadTemp(String generatedClass,String className,String method){
        synchronized (getClassLoadingLock(generatedClass)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(generatedClass);
            if (c == null) {
                String description=generatedClass.replace('.','/');
                byte[] bytes=make(description,className,method);
                clzBytesCache.put(description+".class",bytes);
                c=defineClass(generatedClass,bytes,0,bytes.length);
            }
            return c;
        }
    }

    /**
    * Redisson序列化目标类时会调用getResourceAsStream获取原始class byte数组,
    *  因为是动态生成的找不到class文件,所以需要覆盖下
    */
    @Override
    public InputStream getResourceAsStream(String name) {
        //
        if(clzBytesCache.containsKey(name)){
            return new ByteArrayInputStream(clzBytesCache.get(name));
        }
        return super.getResourceAsStream(name);
    }

    /**
     * 构造一个类,形如
     *
     * public class RedissonTaskXXXXX implements Runnable, Serializable {
     *     public static final long serialVersionUID = -45329617L;
     *
     *     \\@Autowired
     *     public ScheduleTest task;//不手工实例化对象,依赖调用时Redisson自动注入
     *
     *     public RedssionTaskMBean460b2509() {
     *     }
     *
     *     public void run() {
     *         try {
     *             this.task.test123();
     *         } catch (Exception var2) {
     *             var2.printStackTrace();
     *         }
     *
     *     }
     * }
     *
     * @param generatedClass 生成类类名
     * @param className 目标类名
     * @param method 目标类方法
     * @return
     */
    private static byte[] make(String generatedClass, String className, String method) {

        String nest=className.replace('.','/');
        String nestDescriptor="L"+nest+";";
        ClassWriter classWriter = new ClassWriter(0);

        classWriter.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC ,
                generatedClass, null, "java/lang/Object", new String[]{"java/lang/Runnable","java/io/Serializable"});

        classWriter
                .visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_STATIC+Opcodes.ACC_FINAL, "serialVersionUID","J", null, (long)(className+":"+method).hashCode())
                .visitEnd();
        classWriter
                .visitField(Opcodes.ACC_PUBLIC, "task",nestDescriptor, null, null)
                .visitAnnotation("Lorg.springframework.beans.factory.annotation.Autowired;".replaceAll("\\.","/"),true)
                .visitEnd();
        // 添加空构造器
        MethodVisitor mw = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V",
                null, null);

        mw.visitVarInsn(Opcodes.ALOAD, 0); // this 入栈
        mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>",
                "()V",false);
        mw.visitInsn(Opcodes.RETURN);
        mw.visitMaxs(1, 1);
        mw.visitEnd();



        mw =classWriter
                .visitMethod(Opcodes.ACC_PUBLIC,"run","()V",null,null);

        Label start=new Label(), ret=new Label(), exception=new Label();

        mw.visitCode();

        mw.visitTryCatchBlock(start,exception,exception,"java/lang/Exception");
        mw.visitLabel(start);
        mw.visitVarInsn(Opcodes.ALOAD, 0);
        //task 入栈
        mw.visitFieldInsn(Opcodes.GETFIELD,generatedClass,"task",nestDescriptor);

        mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, nest,
                method, "()V",false);

        mw.visitJumpInsn(Opcodes.GOTO,ret);

        mw.visitLabel(exception);

        mw.visitFrame(Opcodes.F_FULL,1,new Object[]{generatedClass},1,new Object[]{"java/lang/Exception"});

        mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Exception",
                "printStackTrace", "()V",false);

        mw.visitLabel(ret);
        mw.visitFrame(Opcodes.F_SAME,0,null,0,null);
        mw.visitInsn(Opcodes.RETURN);

        mw.visitMaxs(2, 2);
        mw.visitEnd();
        classWriter.visitEnd();

        byte[] bytes=classWriter.toByteArray();
        return bytes;

    }

    public static Class<Runnable> loadClass(String className,String methodName) throws CodeGenerateException {
        Class clz;
        try {
            clz=Class.forName(className);
            if(!Modifier.isPublic(clz.getModifiers())){
                throw new CodeGenerateException("任务类不可为非public类型 "+className);
            }
        } catch (ClassNotFoundException e) {
            throw new CodeGenerateException("不存在此任务类:"+className);
        }

        try {
            Method method=clz.getDeclaredMethod(methodName);
            if(Modifier.isStatic(method.getModifiers())){
                throw new CodeGenerateException("任务类不可使用静态方法 "+className+"."+methodName);
            }
            if(!Modifier.isPublic(method.getModifiers())){
                throw new CodeGenerateException("任务类不可使用非public方法 "+className+"."+methodName);
            }
        } catch (NoSuchMethodException e) {
            throw new CodeGenerateException("任务类不存在无参方法 "+className+"."+methodName);
        }

        String generatedClass="com.xxxx.yyyyy.autotask.RedissonTask"+Integer.toString(Math.abs((className+methodName).hashCode()),16);

        return classLoader.loadTemp(generatedClass,className,methodName);
    }

    public static class CodeGenerateException extends Exception {
        public CodeGenerateException(String message) {
            super(message);
        }

        public CodeGenerateException(String message, Throwable cause) {
            super(message, cause);
        }
    }

}

Redisson任务创建代码


Runnable target=TaskGenerator.loadClass(task.beanClass,task.method).newInstance();
                
RScheduledFuture scheduledFuture=service.schedule(target,CronSchedule.of(task.cronExpression));
                    String taskId=scheduledFuture.getTaskId();

......

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值