`
coco_hxq
  • 浏览: 1892 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

热部署定时任务管理实现

    博客分类:
  • java
阅读更多

热部署定时任务管理实现(上传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对象,让后面可以反射调用(看似简单的代码中往往有很大的学问!)

 

分享到:
评论
2 楼 kissek08 2013-11-05  
URLCLASSLOADER,对资源的占用消耗很大,一个处理不好,容易出现内存溢出的可能。还有一个问题就是针对数据库驱动jar的加载问题。目前不不知道该怎么解决。上网看了很多资料,都没有找到连接池有ClassLoader的加载方式。各种心碎的心情
1 楼 hello.world! 2013-02-21  
顶一个,最近正在弄这个,十分恼火~ 先学习~

相关推荐

    基于SpringBoot+Vue的轻量级定时任务管理系统.zip

    【标题】"基于SpringBoot+Vue的轻量级定时任务管理系统"揭示了这是一个结合了SpringBoot后端框架和Vue前端框架的项目,主要用于实现轻量级的定时任务管理。SpringBoot以其快速开发、内置依赖管理和简化配置的特点,...

    Spring SchedulingConfigurer 实现动态定时任务【完整源码+数据库】

    - 在IDEA中,我们可以使用Spring Boot的`spring-boot-devtools`模块,它支持热部署,便于在开发过程中快速测试和调试定时任务的变更。 - 可以通过日志输出或使用监控工具(如Spring Boot Actuator)来观察和验证...

    JobPlus项目是基于SpringBoot+Vue的轻量级定时任务管理系统.zip

    JobPlus项目是一个集成SpringBoot和Vue.js技术的轻量级定时任务管理系统,旨在提供一个高效、易用且可扩展的平台来管理和调度各种任务。这个系统的核心特点是将后端的强大力量与前端的交互性相结合,使得任务的创建...

    SpringBoot-入门级简单源码

    总的来说,这个SpringBoot入门项目涵盖了SpringBoot基本架构的搭建,以及与Mybatis的整合,实现了热部署和多线程定时任务,对于初学者来说,这是一个很好的学习资源,能够帮助他们快速理解和掌握这些核心概念和技术...

    基于Spring Boot框架的简单任务调度系统.zip

    基于Spring Boot框架的简单任务调度系统 内容概要 ... 动态代理支持动态加载和替换代理类,实现热部署功能,提高开发和调试效率。 API文档集成Swagger,自动生成API文档,方便前后端开发人员对接。

    基于springboot的中小型仓库物流管理系统.zip

    3. 定时任务:通过集成Quartz或Spring Task,SpringBoot可以实现库存检查、订单处理等定时任务,确保物流流程的自动化。 4. 异步处理:SpringBoot的异步处理能力可以提高系统响应速度,例如批量入库、出库操作,...

    springboot

    - **任务调度**:通过Quartz或Spring Task实现定时任务。 - **邮件服务**:集成JavaMailSender发送邮件。 - **微服务架构**:SpringCloud扩展了SpringBoot,提供了服务发现、配置中心、熔断器等功能,构建大型...

    基于Springboot的酒店宾馆管理系统源码.zip

    Spring Boot支持热部署,方便开发过程中实时查看效果。上线时,可以利用Docker容器化技术进行部署,提高环境一致性。 在开发过程中,版本控制工具如Git用于团队协作,Maven或Gradle管理项目依赖,IDEA或Eclipse等...

    springboot大学城水电管理系统.zip

    5. **定时任务**:为了实现水电费的定期计算和通知,系统可能会集成Quartz或Spring Task等定时任务框架。这些框架允许开发者设定任务执行规则,定时执行水电费用的统计和推送工作。 6. **API接口设计**:考虑到可能...

    企业后台管理基础框架 hsweb.zip

    定时任务: 配置定时任务,使用动态脚本编写任务内容.系统监控: 监控系统资源使用情况.缓存监控: 监控缓存情况.访问日志: 记录用户每次操作情况未来:组织架构管理: 地区-机构-部门-职务-人员.工作流管理: activiti...

    JeeSpringCloud-部署文档1

    - **高级部署**:可能涉及集群和热部署,确保高可用性和快速更新。 - **企业版**:提供更多的功能和服务,适合商业使用,可能包含数据权限、角色权限等功能。 3. **模块介绍**: - **用户管理**:管理系统用户,...

    jenkins持续集成冷部署到tomcat教程

    - **冷部署**:一种部署方式,与热部署相对,是指在应用服务器停止的状态下进行部署更新。这种方式适用于应用较大、更新频繁的情况,可以避免因热部署导致的内存溢出等问题。 #### 实施步骤详解 ##### 一、安装...

    (免费分享)基于springboot的高校宿舍管理系统带论文-034

    在报修管理中,可以设置状态流转,如待处理、处理中、已完成,通过事件驱动或者定时任务来更新维修状态。 此外,系统还应具备通知功能,如通过邮件或短信通知学生报修处理进度,这需要用到SpringBoot的集成第三方...

    springboot358智慧社区居家养老健康管理系统pf.zip

    在居家养老健康管理系统中,SpringBoot提供了强大的依赖注入机制,允许开发者轻松管理和服务于各类组件,例如数据库连接池、定时任务、缓存等。这在处理老年人健康数据的存储和分析时显得尤为重要,可以快速地集成...

    基于springboot的食堂管理系统 Javaee项目,springboot项目

    5. 数据统计:系统需要提供数据统计功能,例如每日销售额、最受欢迎的菜品等,这可以通过SpringBoot的定时任务和数据分析工具实现。 在开发过程中,前端可以使用React、Vue等现代化的前端框架,配合SpringBoot的...

    SpringBoot515新生宿舍管理系统.zip

    - **定时任务**:使用SpringBoot的`@Scheduled`注解可以方便地实现定时任务,例如定期更新宿舍状态,或发送通知邮件。 - **日志管理**:SpringBoot默认集成了Logback或Log4j,方便记录系统日志,便于问题排查。 4...

    springboot431校园竞赛管理系统pf.zip

    4. 赛事日程:通过集成Quartz或SpringTask定时任务,系统可以自动化处理赛事日程的提醒、公告发布等任务。 5. 数据分析:通过对参赛者数据的统计分析,提供可视化报表,有助于管理者了解赛事状况。这里可以利用...

    Java Spring Boot面试题

    Spring Boot 项目可以热部署,可以使用 Spring Boot DevTools 来实现热部署。 Spring Boot 项目可以使用 Maven 依赖项来管理依赖关系,starter 项目可以帮助开发者快速构建应用程序。 Spring Boot 项目可以使用 jar ...

    基于SpringBoot的社团管理系统.zip

    5. 热部署:通过DevTools支持热更新,提高开发效率。 三、社团管理系统架构 基于SpringBoot的社团管理系统通常采用微服务架构,将系统拆分为多个独立的服务,每个服务专注于特定的业务功能。这种架构有以下优点: ...

    基于springboot+mybatis的选课管理系统.zip

    同时,SpringBoot内置的DevTools可以实现实时热部署,提高开发效率。 安全性方面,SpringBoot集成了Spring Security,可以轻松实现用户认证和授权。对于选课系统,可能需要实现登录验证、角色权限控制,确保只有...

Global site tag (gtag.js) - Google Analytics