答案:反射。
首先了解反射:
参考地址:https://blog.csdn.net/mydesss/article/details/115498726
一、反射的基本概念
1、反射的概念
将类的各个组成部分封装成其他对象,这就是反射的机制.
通过上图,我们可以通过反射获得Person类的Class对象,通过获得Class对象来获得其中的成员变量,构造方法和成员方法,这个也是我们使用反射的目的。
2、使用反射的好处
- 可以在程序的运行过程中操作这些对象,获得类对象的属性,方法等.
- 可以解耦,以此来提高程序的可扩展性
3、补充
反射:框架设计的灵魂,反射是框架设计的关键点
那么知道了反射,框架又是什么呢
框架:半成品软件,我们在开发的过程中可以在框架的基础上使用一些封装好的类进行软件开发,这样就可以简化编码了.
二、获取Class对象
1、获取Class对象的方式
Class.forname(“全类名”):将字节码文件加载进内存,返回class对象
类名.class:通过类名的属性class获取
对象.getclass():因为getclass()方法在object类中定义,又因为所有类都会继承object类,所以所有类都有这种方法
2、演示上面三种获取对象的方式
Person类
public class Person {
}
Main类
public class Main {
public static void main(String[] args) throws Exception {
Class person = Class.forName("test.Person");
Class person1 = Person.class;
Person p = new Person();
Class person2 = p.getClass();
System.out.println(person);
System.out.println(person1);
System.out.println(person2);
}
}
程序运行结果
结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的class对象都是同一个
三、使用class对象功能
1、class对象具体功能方法介绍
2、具体功能演示
因为所有的方法使用方式类似,在这里我就只演示获取成员变量的方法了
Person类
public class Person {
public String name;
private int age;
public String sex;
}
Main类
public class Main {
public static void main(String[] args) throws Exception {
Class person = Class.forName("test.Person");
Field[] fields = person.getFields();
System.out.println("getFields():只能获得公有的属性");
for(int i = 0;i<fields.length;i++){
System.out.println(fields[i]);
}
System.out.println("-----------------------------");
Field[] fields1 = person.getDeclaredFields();
System.out.println("getDeclaredFields():能获得所有的属性");
for(int i = 0;i<fields1.length;i++){
System.out.println(fields1[i]);
}
System.out.println("-----------------------------");
Field field = person.getField("name");
System.out.println("getField():获得单个公有属性");
System.out.println(field);
}
}
程序运行结果
getFields():只能获得公有的属性
public java.lang.String test.Person.name
public java.lang.String test.Person.sex
-----------------------------
getDeclaredFields():能获得所有的属性
public java.lang.String test.Person.name
private int test.Person.age
public java.lang.String test.Person.sex
-----------------------------
getField():获得单个公有属性
public java.lang.String test.Person.name
反射到这里基本就了解差不多了,那么如何实现动态添加任务?
请先阅读:https://blog.csdn.net/qq_21454973/article/details/81979660
基本的CRUD模块就不多说了,主要就是几个utils工具类。
InitQuartzJob 是程序初始化定时任务以及动态加入计划的工具类。
如何理解计划?
首先从库里读出的是数据,不是真正的定时任务,就是Init方法的查询部分,当查询到数据后,把数据加入到scheduler中,就是addjob方法。
如何理解ScheduleJob?
库与scheduler的中间类,从库里读出的数据封装成ScheduleJob,然后解读ScheduleJob加入到scheduler中。
QuartzJobFactory 以及QuartzJobFactoryDisallowConcurrentExecution 是scheduler以代理工厂的方式执行任务。
TaskUtils就是本文的重点,反射调用方法。
先贴代码:
public class TaskUtils {
public final static Logger log = Logger.getLogger(TaskUtils.class);
private static STimetaskLogService sTimetaskLogService = (STimetaskLogService)
SpringUtils.getBean(STimetaskLogService.class);
/**
* 通过反射调用scheduleJob中定义的方法
*
* @param scheduleJob
*/
@SuppressWarnings("unchecked")
public static void invokMethod(ScheduleJob scheduleJob) {
Object object = null;
Class clazz = null;
boolean flag = true;
if (StringUtils.isNotBlank(scheduleJob.getSpringId())) {
object = SpringUtils.getBean(scheduleJob.getSpringId());
} else if (StringUtils.isNotBlank(scheduleJob.getBeanClass())) {
try {
clazz = Class.forName(scheduleJob.getBeanClass());
object = clazz.newInstance();
} catch (Exception e) {
flag = false;
STimetaskLog tlog = new STimetaskLog();
tlog.setId(UuidUtils.creatUUID());
tlog.setCreateDate(new Date());
tlog.setJobId(scheduleJob.getJobId().toString());
tlog.setReason("未找到"+scheduleJob.getBeanClass()+"对应的class");
tlog.setState("fail");
sTimetaskLogService.insertSelective(tlog);
e.printStackTrace();
}
}
if (object == null) {
flag = false;
log.error("任务名称 = [" + scheduleJob.getJobName() + "]---------------未启动成功,请检查是否配置正确!!!");
STimetaskLog tlog = new STimetaskLog();
tlog.setId(UuidUtils.creatUUID());
tlog.setCreateDate(new Date());
tlog.setJobId(scheduleJob.getJobId().toString());
tlog.setReason("未找到"+scheduleJob.getBeanClass()+"对应的class");
tlog.setState("fail");
sTimetaskLogService.insertSelective(tlog);
return;
}
clazz = object.getClass();
Method method = null;
try {
method = clazz.getDeclaredMethod(scheduleJob.getMethodName(), new Class[] { String.class });
} catch (NoSuchMethodException e) {
flag = false;
log.error("任务名称 = [" + scheduleJob.getJobName() + "]---------------未启动成功,方法名设置错误!!!");
STimetaskLog tlog = new STimetaskLog();
tlog.setId(UuidUtils.creatUUID());
tlog.setCreateDate(new Date());
tlog.setJobId(scheduleJob.getJobId().toString());
tlog.setReason("未找到"+scheduleJob.getBeanClass()+"类下"+scheduleJob.getMethodName()+"对应的方法");
tlog.setState("fail");
sTimetaskLogService.insertSelective(tlog);
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (method != null) {
try {
method.invoke(object, scheduleJob.getJobData());
} catch (IllegalAccessException e) {
flag = false;
STimetaskLog tlog = new STimetaskLog();
tlog.setId(UuidUtils.creatUUID());
tlog.setCreateDate(new Date());
tlog.setJobId(scheduleJob.getJobId().toString());
tlog.setReason("未找到"+scheduleJob.getBeanClass()+"类下"+scheduleJob.getMethodName()+"对应的方法参数设置错误");
tlog.setState("fail");
sTimetaskLogService.insertSelective(tlog);
e.printStackTrace();
} catch (IllegalArgumentException e) {
flag = false;
STimetaskLog tlog = new STimetaskLog();
tlog.setId(UuidUtils.creatUUID());
tlog.setCreateDate(new Date());
tlog.setJobId(scheduleJob.getJobId().toString());
tlog.setReason("未找到"+scheduleJob.getBeanClass()+"类下"+scheduleJob.getMethodName()+"对应的方法参数设置错误");
tlog.setState("fail");
sTimetaskLogService.insertSelective(tlog);
e.printStackTrace();
} catch (InvocationTargetException e) {
flag = false;
STimetaskLog tlog = new STimetaskLog();
tlog.setId(UuidUtils.creatUUID());
tlog.setCreateDate(new Date());
tlog.setJobId(scheduleJob.getJobId().toString());
tlog.setReason("未找到"+scheduleJob.getBeanClass()+"类下"+scheduleJob.getMethodName()+"对应的方法参数设置错误");
tlog.setState("fail");
sTimetaskLogService.insertSelective(tlog);
e.printStackTrace();
}
}
if(flag){
System.out.println("任务名称 = [" + scheduleJob.getJobName() + "]----------启动成功");
STimetaskLog tlog = new STimetaskLog();
tlog.setId(UuidUtils.creatUUID());
tlog.setCreateDate(new Date());
tlog.setJobId(scheduleJob.getJobId().toString());
tlog.setState("success");
sTimetaskLogService.insertSelective(tlog);
}
}
}
理一下invokMethod的逻辑:
这样就比较清晰明了了
然后就会发现博客的代码不完整 哈哈
其实理到这里理论基本上就结束了 然后就是自己改写逻辑的问题
比如可以从前端传入springId/beanclass 方法名 参数来完全自定义定时任务(参考博客里是把springId/beanclass给写死了)
局限性就是你执行的方法都要提前编译到容器才能执行,否则是找不到的。
如果说连方法里具体干啥都要自定义,截止到本文这里是实现不了的
但是可以提供思路:不要局限于一种语言,java是必须提前编码编译放到容器里(如Tomcat)才能执行,那么找一种不需要容器的语言要做这种事情比如python nodejs
套路还是一样的,前端传入springId/beanclass 方法名 参数,只不过这个方法是用来执行py文件或者js文件的 然后参数就是文件路径 前端还要上传py或者js文件保存到目标服务器,亦可以上传代码后台生成文件
这样就可以定时执行方法->执行py/js文件->具体内容
当然做这些比较危险,比如py文件里是关机命令,那样就定时关机了 比较尴尬 可以对文件里的字符进行过滤 包含危险字符时不入库就行
告辞!