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

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();

......

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
概要介绍: 本课程主要是介绍并实战一款java中间件~redisson,介绍redisson相关的核心技术栈及其典型的应用场景,其中的应用场景就包括布隆过滤器、限流器、短信发送、实时/定时邮件发送、数据字典、分布式服务调度等等,在业界号称是在java项目里正确使用redis的姿势。本课程的目标就在于带领各位小伙伴一起学习、攻克redisson,更好地巩固自己的核心竞争力,而至于跳槽涨薪,自然不在话下!  课程内容: 说起redisson,可能大伙儿不是很熟悉,但如果说起redis,想必肯定很多人都晓得。没错,这家伙字如其名,它就是架设在redis基础上的一款综合性的、新型的中间件,号称是java企业级应用开发中正确使用redis的姿势/客户端实例。 它是架设在redis基础之上,但拥有的功能却远远多于原生Redis 所提供的,比如分布式对象、分布式集合体系、分布式锁以及分布式服务调度等一系列具有分布式特性的对象实例… 而这些东西debug将在本门课程进行淋漓尽致的介绍并实战,除此之外,我们将基于spring boot2.0搭建的多模块项目实战典型的应用场景:对象存储、数据字典、短信发送、实时/定时邮件发送、布隆过滤器、限流组件、分布式服务调度....课程大纲如下所示: 下面罗列一下比较典型的核心技术栈及其实际业务场景的实战,如下图所示为redisson基于订阅-发布模式的核心技术~主题Topic的实际业务场景,即实时发送邮件: 而下图则是基于“多值映射MultiMap”数据结构实战实现的关于“数据字典”的缓存管理: 除此之外,我们还讲解了可以与分布式服务调度中间件dubbo相媲美的功能:分布式远程服务调度,在课程中我们动手搭建了两个项目,用于分别充当“生产者”与“消费者”角色,最终通过redisson的“服务调度组件”实现服务与服务之间、接口与接口之间的调用!  课程收益: (1)认识并掌握redisson为何物、常见的几种典型数据结构-分布式对象、集合、服务的应用及其典型应用场景的实战; (2)掌握如何基于spring boot2.0整合redisson搭建企业级多模块项目,并以此为奠基,实战企业级应用系统中常见的业务场景,巩固相应的技术栈! (3)站在项目管理与技术精进的角度,掌握对于给定的功能模块进行业务流程图的绘制、分析、模块划分、代码实战与性能测试和改进,提高编码能力与其他软实力; (4)对于Java微服务、分布式、springboot精进者而言,学完本课程,不仅可以巩固提高中间件的实战能力,其典型的应用场景更有助于面试、助力相关知识点的扫盲! 如下图所示: 关键字:Spring Boot,Redis,缓存穿透,缓存击穿,缓存雪崩,红包系统,Mybatis,高并发,多线程并发编程,发送邮件,列表List,集合Set,排行榜,有序集合SortedSet,哈希Hash ,进阶实战,面试,微服务、分布式 适用人群:redisson学习者,分布式中间件实战者,微服务学习者,java学习者,spring boot进阶实战者,redis进阶实战者
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值