quartz动态任务
发表于:2018-07-28 11:18:47 分类:JAVA 阅读:679次
目前采用的方式是启动核心调试系统,然后在系统本地硬盘指定一个动态class读取文件夹。
有任务时,在原核心项目下新建一个任务包开始编码。编码完成后编译,取到当前任务的包下的所有class,上传至上一步的文件夹。
然后通过web管理端新建任务,任务class名填入当前任务的全包路径,任务名填入任务描述,填入时间表达式。
保存后,控制器收到请求就会去指定的文件夹查找class并加载。接着,创建仓库实例和sericce实例并加入spring容器管理。最后由调度器调度该任务。
添加任务代码:
public void addJob(String jarName, String jobClassName, String jobGroupName, String cronExpression,HttpServletRequest request) throws Exception { // 启动调度器 scheduler.start(); //构建job信息 JobDetail jobDetail = JobBuilder.newJob(getClass(rootPath, jarName,request).getClass()).withIdentity(jobClassName, jobGroupName).build(); //表达式调度构建器(即任务执行的时间) CronScheduleBuilder scheduleBuilder = cronSchedule(cronExpression); //按新的cronExpression表达式构建一个新的trigger Trigger trigger = newTrigger().withIdentity(jobClassName, jobGroupName).withSchedule(scheduleBuilder).build(); try { scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException e) { throw new Exception("创建定时任务失败"); } }
/** * 加载指定路径下的所有class文件,并返回指定名称的class对象 * * @param rootPath 指定的路径 * @param classFileName 需要返回的class文件名 * @return * @throws Exception */ private Class<?> loadClassFromPath(String rootPath, String classFileName,HttpServletRequest request) throws Exception { // 设置class文件所在根路径 File clazzPath = new File(rootPath); Class<?> res = null; if (clazzPath.exists() && clazzPath.isDirectory()) { Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); boolean accessible = method.isAccessible(); try { if (accessible == false) { method.setAccessible(true); } // 设置类加载器 URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader().getParent(); // 将当前类路径加入到类加载器中 method.invoke(classLoader, clazzPath.toURI().toURL()); } finally { method.setAccessible(accessible); } // 获取路径长度 int clazzPathLen = clazzPath.getAbsolutePath().length() + 1; Stack<File> stack = new Stack<>(); stack.push(clazzPath); // 遍历类路径 while (stack.isEmpty() == false) { File path = stack.pop(); File[] classFiles = path.listFiles(new FileFilter() { public boolean accept(File pathname) { return pathname.isDirectory() || pathname.getName().endsWith(".class"); } }); for (File subFile : classFiles) { if (subFile.isDirectory()) { stack.push(subFile); } else { // 文件名称 String className = subFile.getAbsolutePath(); className = className.substring(clazzPathLen, className.length() - 6); className = className.replace(File.separatorChar, '.'); // 加载Class类 Class<?> clazz = Class.forName(className); if (className.endsWith("Repository")) { repositoryClasses.add(clazz); } else if (className.endsWith("ServiceImpl")) { serviceClasses.add(clazz); } System.out.println("读取应用程序类文件[class=" + className + "]"); if (className.equals(classFileName)) { res = clazz; } } } } } autowiredReposotoryAndSevice(request); return res; }
private void autowiredReposotoryAndSevice(HttpServletRequest request) throws Exception { for (Class clazz : repositoryClasses) { registerBean(clazz,request); } repositoryClasses.clear(); for (Class clazz : serviceClasses) { registerBean(clazz,request); } serviceClasses.clear(); } private void registerBean(Class clazz,HttpServletRequest request) throws Exception { WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext()); DefaultListableBeanFactory factory = (DefaultListableBeanFactory) context.getAutowireCapableBeanFactory(); DataSource dataSource = (DataSource) factory.getBean("dataSource"); Object bean = null; String name = clazz.getName(); try { context.getBean(name); } catch (Exception e) { bean = factory.createBean(clazz, AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false); if (name.contains("Repository")) { Field field = clazz.getDeclaredField("dataSource"); field.setAccessible(true); field.set(bean, dataSource); } context.getAutowireCapableBeanFactory().applyBeanPostProcessorsAfterInitialization(bean, name); factory.registerSingleton(name, bean); } }
最坑的地方应该就是指定了文件读取class时经常会报找不到核心项目中依赖的class。
查了好久资料最终找到了这篇博客:
https://blog.csdn.net/briblue/article/details/54973413
最后通用反射当前线程的上下文加载器的父加载器,终于解决问题——基础薄弱,这块卡了很久。
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); boolean accessible = method.isAccessible(); try { if (accessible == false) { method.setAccessible(true); } // 设置类加载器 URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader().getParent(); // 将当前类路径加入到类加载器中 method.invoke(classLoader, clazzPath.toURI().toURL()); } finally { method.setAccessible(accessible); }
关键词:quartz