zoukankan      html  css  js  c++  java
  • Spring学习—Spring中定时器实现

    Spring学习—Spring中定时器实现

    在一些工作需要使用到定时器,Spring很好的集成了定时器的功能!
    在Spring 中使用Quartz,本文介绍Spring3.0以后自主开发的定时任务工具,spring task,可以将它比作一个轻量级的Quartz,而且使用起来很简单,除spring相关的包外不需要额外的包,
    下面介绍两种方式实现Spring定时器功能,一种是基于xml配置方式,一种是基于注解的方式,大家根据自己的项目选择适合自己的。

    一:基于xml配置的方式

    1:编写普通的pojo 类

    package com.aflyun.web.task;
    
    import org.springframework.stereotype.Component;
    import org.springframework.stereotype.Service;
    
    @Component
    //@Service 都可以 
    public class TaskCool {
        /**
         * 第一个定时器测试方法
         */
        public void testJob(){
            System.out.println("test first taskJob .... ");
        }
    }
    

    2:配置xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:tx="http://www.springframework.org/schema/tx" 
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:context="http://www.springframework.org/schema/context" 
        xmlns:task="http://www.springframework.org/schema/task"
        xmlns="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/task 
           http://www.springframework.org/schema/task/spring-task.xsd">
    
        <context:component-scan base-package="com.aflyun.web" />
        <aop:aspectj-autoproxy proxy-target-class="true" />
        <context:annotation-config />
        <!-- 在applicationContext.xml中进行配置,使用定时器
            ref : pojo类的名称
            method : 调用的方式名称
            cron : cronExpression表达式
            cron="0/5 * * * * ?"  //表示五秒钟执行一次
         -->
        <task:scheduled-tasks>
            <task:scheduled ref="taskCool" method="testJob" cron="0/5 * * * * ?"/>
        </task:scheduled-tasks>
    
    </beans>
    

    注:上面主要的配置文件中一定要加入task的命名空间和schema。

    上面 ref=”taskCool”,默认为这个TaskCool 类的首字母小写的值,
    若需要修改可以在@Component里面进行修改 ,例如下面
    @Component(“taskCoolJob”) 则此时 ref=”taskCoolJon”。
    到此基于xml配置完成,运行则可以看到效果!

    二:基于注解方式

    使用注解方式不需要再每写一个任务类还要在xml文件中配置下,方便了很多。使用Spring的@Scheduled,下面先看一注解@Scheduled在源文件中的定义:

    @Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.ANNOTATION_TYPE})  
    @Retention(RetentionPolicy.RUNTIME)  
    @Documented  
    public @interface Scheduled  
    {  
      public abstract String cron();  
    
      public abstract long fixedDelay();  
    
      public abstract long fixedRate();  
    }  
    

    cron:表示指定cron表达式。(cron类型表示 是指定时间触发器触发任务执行!)

    • fixedDelay:表示从上一个任务完成开始到下一个任务开始的间隔,单位是毫秒。
    • fixedRate:表示从上一个任务开始到下一个任务开始的间隔,单位是毫秒。

    下面进行一下具体的配置过程:

    1:编写pojo类

    package com.tclshop.cms.center.web.task;
    
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    
    @Component
    public class WebTask {
    
        // 每五秒执行一次
        @Scheduled(cron = "0/5 * * * * ?")
        public void TaskJob() {
            System.out.println("test second annotation style ...");
        }
    }
    

    2:配置xml文件

    下面贴出相关的配置文件内容:

    <!-- 开启这个配置,spring才能识别@Scheduled注解   -->  
    <task:annotation-driven scheduler="qbScheduler" mode="proxy"/>  
    <task:scheduler id="qbScheduler" pool-size="10"/>
    

    注:理论上只需要加上这句配置就可以了,其他参数都不是必须的。
    配置完成,运行就能看到效果!

    总结:这种定时器的使用,不需要集成其他父类定时器,使用简单方便!功能也很强大!

    附:cronExpression的配置说明

    字段 允许值 允许的特殊字符
    0-59 , - * /
    0-59 , - * /
    小时 0-23 , - * /
    日期 1-31 , - * ? / L W C
    月份 1-12 或者 JAN-DEC , - * /
    星期 1-7 或者 SUN-SAT , - * ? / L C #
    年(可选) 留空, 1970-2099 , - * /

    例子:

    CRON表达式 含义
    “0 0 12 * * ?” 每天中午十二点触发
    “0 15 10 ? * *” 每天早上10:15触发
    “0 15 10 * * ?” 每天早上10:15触发
    “0 15 10 * * ? *” 每天早上10:15触发
    “0 15 10 * * ? 2005” 2005年的每天早上10:15触发
    “0 * 14 * * ?” 每天从下午2点开始到2点59分每分钟一次触发
    “0 0/5 14 * * ?” 每天从下午2点开始到2:55分结束每5分钟一次触发
    “0 0/5 14,18 * * ?” 每天的下午2点至2:55和6点至6点55分两个时间段内每5分钟一次触发
    “0 0-5 14 * * ?” 每天14:00至14:05每分钟一次触发
    “0 10,44 14 ? 3 WED” 三月的每周三的14:10和14:44触发
    “0 15 10 ? * MON-FRI” 每个周一、周二、周三、周四、周五的10:15触发

    三:Spring @Scheduled定时任务动态修改cron参数

    Spring框架自3.0版本起,自带了任务调度功能,好比是一个轻量级的Quartz,而且使用起来也方便、简单,且不需要依赖其他的JAR包。秉承着Spring的一贯风格,Spring任务调度的实现同时支持注解配置和XML配置两种方式。

    再来谈谈变态的项目需求:我们正在做一个智能数字电表的数据采集项目,项目最终会在多个工业园上线,每个工业园对电表数据的采集周期可以进行自定义,例如A工业园想每10分钟采集一次数据,B工业园想每15分钟采集一次数据。因为数据采集是个重复的周期性工作,那么就可以考虑使用Spring框架的定时任务功能了。

    按正常来讲,修改定时任务的执行周期还不简单,把服务停下来,改下任务的cron参数,再重启服务就搞定了。但有没有一种可能,在不停服务的情况下,就可以动态的修改任务的cron参数呢?完全是有可能的!

    直接看代码:

    package com.tradeplatform.platform.user.job;
    
    import java.math.BigDecimal;
    import java.util.Date;
    import java.util.List;
    import java.util.Map;
    
    import org.apache.log4j.Logger;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.scheduling.Trigger;
    import org.springframework.scheduling.TriggerContext;
    import org.springframework.scheduling.annotation.EnableScheduling;
    import org.springframework.scheduling.annotation.SchedulingConfigurer;
    import org.springframework.scheduling.config.ScheduledTaskRegistrar;
    import org.springframework.scheduling.support.CronTrigger;
    import org.springframework.stereotype.Component;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.tradeplatform.platform.trade.mapper.TradeConfigMapper;
    import com.tradeplatform.platform.trade.service.TradeConfigService;
    import com.tradeplatform.platform.user.service.ReleasedTaskService;
    import com.tradeplatform.platform.user.service.UserAccountDetailService;
    import com.tradeplatform.platform.user.service.UserAccountService;
    import com.tradeplatform.trade.api.finan.utils.FinanceDetailRemark;
    import com.tradeplatform.trade.api.finan.utils.FinanceDetailType;
    import com.tradeplatform.trade.api.trade.utils.TradeConstants;
    import com.tradeplatform.trade.api.user.entity.UserAccount;
    
    /**
     * 自动释放币
     * <p>
     * 创建人:pengyq <br>
     * 创建时间:2018年5月31日 下午12:08:11 <br>
     * <p>
     * 修改人: <br>
     * 修改时间: <br>
     * 修改备注: <br>
     * </p>
     */
    @Component
    @EnableScheduling
    @Transactional
    public class ReleasedTask implements SchedulingConfigurer{
    
    	private Logger log = Logger.getLogger(getClass());
    
    	@Autowired
    	private ReleasedTaskService taskService;
    
    	private TradeConfigMapper tradeConfigMapper;
    	
    	@Autowired
    	private TradeConfigService configService;
    	
    
    	@Autowired
    	private UserAccountService accountService;
    
    	@Autowired
    	private UserAccountDetailService accountDetailService;
    	
    	
    	/**
    	 * 查询出符合条件的记录(新)
    	 * <p>
    	 * 创建人:lgq <br>
    	 * 创建时间:2018年5月31日 下午1:55:01 <br>
    	 * <p>
    	 * 修改人: <br>
    	 * 修改时间: <br>
    	 * 修改备注: <br>
    	 * </p>
    	 * 
    	 * @return
    	 */
    	public List<Map<String, Object>> getNeedReleasedAccounts() {
    		return taskService.getNeedReleasedAccounts();
    	}
    
    	
    	private String cron;
    	
    	@Autowired
    	public ReleasedTask(TradeConfigMapper tradeConfigMapper) {
    		// 获取每几天释放一次参数(单位:天)
    		this.tradeConfigMapper = tradeConfigMapper;
    		String interval = tradeConfigMapper.getTimeInterval().getConfigValue();
    		this.cron = "0 0 3 */" + interval + " * ?";
    		//cron = "*/10 * * * * ?";
    	}  
    	
    	@Override
    	public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
    		taskRegistrar.addTriggerTask(new Runnable() {  
                @Override  
                public void run() {  
                	//是否执行自动释放
                	String autoRelease = configService.getConfig(TradeConstants.TRADE_CONFIG_AUTO_RELEASE);
                	if (autoRelease.equals("1")) {
                		// 任务逻辑  
                    	log.info("释放任务执行---------");
                    	
                    	//查询出所有需要释放的钱包
                    	List<Map<String, Object>> accounts =  getNeedReleasedAccounts();
                    	
                    	// 每次释放比例变量(全局)
                		BigDecimal percentageOfRelease = new BigDecimal(
                				configService.getConfig(TradeConstants.TRADE_CONFIG_PERCENTAGE_OF_RELEASE));
                		
                		//待释放数量小于此参数时全部释放
                		BigDecimal releaseAll = new BigDecimal(
                				configService.getConfig(TradeConstants.TRADE_CONFIG_RELEASE_ALL));
                		
                		//循环进行释放
                		for (Map<String, Object> map : accounts) {
        					//待释放总额
        					BigDecimal freezeAmount = new BigDecimal(map.get("freeze_amount").toString());
        					
        					//本次循环释放金额
        					BigDecimal readyReleased = new BigDecimal("0");
        					
                			//先判断待释放金额是否小于全部释放参数
                			if (releaseAll.compareTo(freezeAmount) == 1 || releaseAll.compareTo(freezeAmount) == 0) {
                				//直接一次性释放完
                				readyReleased = freezeAmount;
                				
        					}else {
        						//本次释放金额
        						readyReleased = freezeAmount.multiply(percentageOfRelease);
        					}
                			
                			// 更新对应的用户、币账户已释放余额、未释放余额
                			Long uId = Long.valueOf(map.get("user_id").toString());
                			Long cId = Long.valueOf(map.get("coin_id").toString());
                			
                			// 账户已释放金额
                			int a = accountService.updateUnAmount(readyReleased.abs().negate(), uId, cId);
                			// 修改账户待释放金额
                			int b = accountService.updateAmount(readyReleased.abs().negate(), uId, cId);
    
                			if (a > 0 && b > 0) {
                				UserAccount account = accountService.getAccountByUserIdAndCoinId(uId, cId);
                				// 插入1条账户明细
                				accountDetailService.createPaymentDetail(uId, cId, account.getId(), account.getId(),
                						FinanceDetailType.TRADE_DETAIL_TYPE_INCOME, FinanceDetailRemark.RELEASED_COIN, readyReleased,
                						"释放", 0);
                			}
        				}
                		log.info("释放任务执行完---------");
    				}
                }  
            }, new Trigger() {
                @Override  
                public Date nextExecutionTime(TriggerContext triggerContext) {  
                    // 任务触发,可修改任务的执行周期  
                    CronTrigger trigger = new CronTrigger(cron);  
                    Date nextExec = trigger.nextExecutionTime(triggerContext);  
                    return nextExec;  
                }  
            });  
    	}
    
    }
    
    

    上面的TradeConfigMapper 使用的构造注入,不然会拿不到值。

    四:参考资料


    作者:不敲代码的攻城狮
    出处:https://www.cnblogs.com/leigq/
    任何傻瓜都能写出计算机可以理解的代码。好的程序员能写出人能读懂的代码。

     
  • 相关阅读:
    Encrypted Handshake Message
    RSAParameters Struct
    What if JWT is stolen?
    What's the difference between JWTs and Bearer Token?
    RSA Algorithm Example
    第18届Jolt大奖结果公布
    Ruby on rails开发从头来(windows)(三十六) 调试技巧
    Ruby on rails开发从头来(四十二) ActiveRecord基础(主键和ID)
    YouTube开放基础技术架构 让用户建自家YouTube
    Ruby on rails开发从头来(四十) ActiveRecord基础(Boolean属性)
  • 原文地址:https://www.cnblogs.com/leigq/p/13406585.html
Copyright © 2011-2022 走看看