热部署定时任务管理实现(上传jar+动态加载jar中接口类文件+hessian远程调用执行业务)
1.新增定时任务
主要是配置定时任务的执行周期,涉及到热部署的就是关于接口jar上传,要调用的远程接口类名称,接口中要被调用的方法名称、远程调用的hessianURL,这样子就配置好了一个定时任务,信息存放到数据库,这样就成功添加了一个定时任务
用到了loadJar这个方法,这里用到了动态加载,刚上传的jar文件要被动态的加载到JVM中,使用了URLClassLoader根据类名称读取jar中指定的类,并且返回加载相应jar的这个URLClassLoader,因为后面用到这个加载的类的时候,必须要用加载它的类加载器才行,否则会报ClassNotFoundException
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import org.apache.struts2.ServletActionContext; public class JarUtil { // public final static String PATH = ServletActionContext.getServletContext().getRealPath("/")+"uploadJar"+File.separator; public final static String PATH = "d:/uploadJar"+File.separator; private static void uploadJar(File jar,String filePathName) { try { FileInputStream fin = new FileInputStream(jar);//获得上传的jar文件 File dir = new File(PATH); if(!dir.exists()){ dir.mkdir(); } FileOutputStream fout = new FileOutputStream(new File(filePathName));//输出jar到指定位置 byte[] buffer = new byte[1024]; int count = fin.read(buffer); while (count > 0) { fout.write(buffer,0,count);//这里一定要标注0,count,否则jar实际上是有问题的。导致后面的jar无法加载 count = fin.read(buffer); } fout.flush(); fout.close(); fin.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * * @param jar 需要加载的jar文件 * @param jarFileName jar文件的名称 * @param className 需要加载的jar中的class的二进制名称 * @return */ public static URLClassLoader loadJar(File jar,String jarFileName,String className){ String filePathName = PATH + jarFileName; //得到服务器上要放置的文件路径,给jar添加一个加入时刻防止名称重复 uploadJar(jar,filePathName);//上传jar return loadClassOfJar(filePathName,className) ;//加载jar中的执行class文件 } /** * * @param url 指向指定jar文件的URL * @param cls 加载jar文件中的class文件的二进制名称 * @return 加载这个class对象的classloader */ public static URLClassLoader loadClassOfJar(String filePathName, String cls) { URLClassLoader loader = null; try { File file = new File(filePathName);//加载这个刚上传的jar文件 if(file != null){ URL url = file.toURI().toURL(); loader = new URLClassLoader(new URL[] { url },Thread.currentThread().getContextClassLoader()); loader.loadClass(cls); // 动态装载class } } catch (Exception ex) { ex.printStackTrace(); } return loader; } }
这就是web加载的时候就要启动所有已经添加的定时任务
这里要注意的是因为上传的jar文件是在一个特定的地方,不在war包中,需要启动的时候去动态加载,首先从数据库读出之前配置的定时任务,然后就是使用jarUtil的loadClassOfJar,把之前上传的jar文件里面的指定类加载,并把加载该类的相应的类加载器classLoader放到jobDetail的jobDataMap中,后面定时任务的执行的时候会用到,这里要注意这个TempleTask类,它是一个模板,只要是通过上传jar包来进行配置的定时任务都用这个高度抽象的模板,它就时一个job
import java.net.URLClassLoader; import java.text.ParseException; import java.util.ArrayList; import java.util.List; import org.quartz.CronTrigger; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import com.tt.TimeTaskArgDao; import com.tt.model.TimeTaskArg; import com.tt.TempleTask; /** * 初始化trigger * @author tongwenhuan * */ public class SchedulerInit { private Scheduler scheduler; private TimeTaskArgDao timeTaskArgDao; public void init() { try { String[] groupNames = scheduler.getJobGroupNames(); for(String groupName : groupNames) { String[] jobNames = scheduler.getJobNames(groupName); if(jobNames.length > 0){ List jns = new ArrayList(); for(String jobName : jobNames) { jns.add(jobName); } List tta_list = timeTaskArgDao.listTimeTaskArgByJobName(jns); for(TimeTaskArg tta : tta_list) { CronTrigger cronTrigger = new CronTrigger(tta.getTriggerName(),groupName,tta.getJobName(),groupName,tta.getCronexpression()); scheduler.scheduleJob(cronTrigger); if(!tta.getTriggerStatus().equals("0")) { scheduler.pauseTrigger(tta.getTriggerName(), tta.getGroupName()); } } } } List list = timeTaskArgDao.getAutoTask(); for(TimeTaskArg tta : list) { String filePathName = JarUtil.PATH + tta.getJarName();//获取指定到该jar的文件路径 URLClassLoader loader = JarUtil.loadClassOfJar(filePathName, tta.getClassName()); //动态加载jar中的接口 if(loader != null){ JobDetail jobDetail = new JobDetail(tta.getJobName(),tta.getGroupName(),TempleTask.class); JobDataMap jm = new JobDataMap(); jm.put("hessianUrl", tta.getHessianUrl()); jm.put("methodName", tta.getMethodName()); jm.put("methodName", tta.getMethodName()); jm.put("classLoaderName", loader);//每个上传的jar包对应各自的一个classLoader,绑定各自的job jobDetail.setJobDataMap(jm); CronTrigger cronTrigger = new CronTrigger(tta.getTriggerName(),tta.getGroupName(),tta.getJobName(),tta.getGroupName(),tta.getCronexpression()); scheduler.scheduleJob(jobDetail,cronTrigger); if(!tta.getTriggerStatus().equals("0")) { scheduler.pauseTrigger(tta.getTriggerName(), tta.getGroupName()); } } } } catch (SchedulerException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } } public Scheduler getScheduler() { return scheduler; } public void setScheduler(Scheduler scheduler) { this.scheduler = scheduler; } public TimeTaskArgDao getTimeTaskArgDao() { return timeTaskArgDao; } public void setTimeTaskArgDao(TimeTaskArgDao timeTaskArgDao) { this.timeTaskArgDao = timeTaskArgDao; } }
import java.net.URLClassLoader; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.scheduling.quartz.QuartzJobBean; import com.caucho.hessian.client.HessianProxyFactory; public class TempleTask extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { try { HessianProxyFactory factory = new HessianProxyFactory(); JobDetail jb = context.getJobDetail(); JobDataMap jm = jb.getJobDataMap(); String hessianUrl = (String) jm.get("hessianUrl"); String methodName = (String) jm.get("methodName"); URLClassLoader loader = (URLClassLoader)jm.get("classLoaderName");//获取加载这个class的loader ClassLoader systemClassLoader = Thread.currentThread().getContextClassLoader();//记住系统当前的webAppClassLoader Thread.currentThread().setContextClassLoader(loader);//临时设置加载当前任务class的类加载器为当前线程的加载器,否则无法找到类 factory.setReadTimeout(50000); Object o = factory.create(hessianUrl); o.getClass().getMethod(methodName).invoke(o,null); Thread.currentThread().setContextClassLoader(systemClassLoader);//定时任务执行完之后,重新归还当前线程为系统的线程 }catch(Exception e) { e.printStackTrace(); } } }
这个是一个公共的job模板,也就是定时任务执行的时候要真正调用的任务,这个任务没有写在本地,而是用了远程调用,使用的是hessian的代理工厂,根据上传jar包填的hessianURL就可以找到接口,也就是返回的object o,根据方法名通过反射调用业务方法,从而实现执行定时任务
这里要注意的问题是:之前放到jobDataMap中的classLoader这里起重大作用了,因为这个接口class类对象是由自定义的URLClassLoader来加载的,而此时该线程的classLoader为webAppClassLoader,是自定义的URLClassLoader的父加载器,根据全盘委托原则,webAppClassLoader会先向上去寻找这个类,一级一级往上找,没有的话才自己的范围下来找这个类,因为不是它加载的,所以他找不到,会报ClassNotFoundException的错误,此时把该线程的classLoader改为加载这个类的加载器的时候,这个加载器就可以在他自己的范围内找到这个Class,可以顺利的对这个Class对象实例化,从而返回Object对象,让后面可以反射调用(看似简单的代码中往往有很大的学问!)
相关推荐
【标题】"基于SpringBoot+Vue的轻量级定时任务管理系统"揭示了这是一个结合了SpringBoot后端框架和Vue前端框架的项目,主要用于实现轻量级的定时任务管理。SpringBoot以其快速开发、内置依赖管理和简化配置的特点,...
- 在IDEA中,我们可以使用Spring Boot的`spring-boot-devtools`模块,它支持热部署,便于在开发过程中快速测试和调试定时任务的变更。 - 可以通过日志输出或使用监控工具(如Spring Boot Actuator)来观察和验证...
JobPlus项目是一个集成SpringBoot和Vue.js技术的轻量级定时任务管理系统,旨在提供一个高效、易用且可扩展的平台来管理和调度各种任务。这个系统的核心特点是将后端的强大力量与前端的交互性相结合,使得任务的创建...
总的来说,这个SpringBoot入门项目涵盖了SpringBoot基本架构的搭建,以及与Mybatis的整合,实现了热部署和多线程定时任务,对于初学者来说,这是一个很好的学习资源,能够帮助他们快速理解和掌握这些核心概念和技术...
基于Spring Boot框架的简单任务调度系统 内容概要 ... 动态代理支持动态加载和替换代理类,实现热部署功能,提高开发和调试效率。 API文档集成Swagger,自动生成API文档,方便前后端开发人员对接。
3. 定时任务:通过集成Quartz或Spring Task,SpringBoot可以实现库存检查、订单处理等定时任务,确保物流流程的自动化。 4. 异步处理:SpringBoot的异步处理能力可以提高系统响应速度,例如批量入库、出库操作,...
- **任务调度**:通过Quartz或Spring Task实现定时任务。 - **邮件服务**:集成JavaMailSender发送邮件。 - **微服务架构**:SpringCloud扩展了SpringBoot,提供了服务发现、配置中心、熔断器等功能,构建大型...
Spring Boot支持热部署,方便开发过程中实时查看效果。上线时,可以利用Docker容器化技术进行部署,提高环境一致性。 在开发过程中,版本控制工具如Git用于团队协作,Maven或Gradle管理项目依赖,IDEA或Eclipse等...
5. **定时任务**:为了实现水电费的定期计算和通知,系统可能会集成Quartz或Spring Task等定时任务框架。这些框架允许开发者设定任务执行规则,定时执行水电费用的统计和推送工作。 6. **API接口设计**:考虑到可能...
定时任务: 配置定时任务,使用动态脚本编写任务内容.系统监控: 监控系统资源使用情况.缓存监控: 监控缓存情况.访问日志: 记录用户每次操作情况未来:组织架构管理: 地区-机构-部门-职务-人员.工作流管理: activiti...
- **高级部署**:可能涉及集群和热部署,确保高可用性和快速更新。 - **企业版**:提供更多的功能和服务,适合商业使用,可能包含数据权限、角色权限等功能。 3. **模块介绍**: - **用户管理**:管理系统用户,...
- **冷部署**:一种部署方式,与热部署相对,是指在应用服务器停止的状态下进行部署更新。这种方式适用于应用较大、更新频繁的情况,可以避免因热部署导致的内存溢出等问题。 #### 实施步骤详解 ##### 一、安装...
在报修管理中,可以设置状态流转,如待处理、处理中、已完成,通过事件驱动或者定时任务来更新维修状态。 此外,系统还应具备通知功能,如通过邮件或短信通知学生报修处理进度,这需要用到SpringBoot的集成第三方...
在居家养老健康管理系统中,SpringBoot提供了强大的依赖注入机制,允许开发者轻松管理和服务于各类组件,例如数据库连接池、定时任务、缓存等。这在处理老年人健康数据的存储和分析时显得尤为重要,可以快速地集成...
5. 数据统计:系统需要提供数据统计功能,例如每日销售额、最受欢迎的菜品等,这可以通过SpringBoot的定时任务和数据分析工具实现。 在开发过程中,前端可以使用React、Vue等现代化的前端框架,配合SpringBoot的...
- **定时任务**:使用SpringBoot的`@Scheduled`注解可以方便地实现定时任务,例如定期更新宿舍状态,或发送通知邮件。 - **日志管理**:SpringBoot默认集成了Logback或Log4j,方便记录系统日志,便于问题排查。 4...
4. 赛事日程:通过集成Quartz或SpringTask定时任务,系统可以自动化处理赛事日程的提醒、公告发布等任务。 5. 数据分析:通过对参赛者数据的统计分析,提供可视化报表,有助于管理者了解赛事状况。这里可以利用...
Spring Boot 项目可以热部署,可以使用 Spring Boot DevTools 来实现热部署。 Spring Boot 项目可以使用 Maven 依赖项来管理依赖关系,starter 项目可以帮助开发者快速构建应用程序。 Spring Boot 项目可以使用 jar ...
5. 热部署:通过DevTools支持热更新,提高开发效率。 三、社团管理系统架构 基于SpringBoot的社团管理系统通常采用微服务架构,将系统拆分为多个独立的服务,每个服务专注于特定的业务功能。这种架构有以下优点: ...
同时,SpringBoot内置的DevTools可以实现实时热部署,提高开发效率。 安全性方面,SpringBoot集成了Spring Security,可以轻松实现用户认证和授权。对于选课系统,可能需要实现登录验证、角色权限控制,确保只有...