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