├── .gitignore ├── src └── main │ ├── resources │ └── META-INF │ │ └── spring.factories │ └── java │ └── com │ └── gyx │ └── superscheduled │ ├── common │ ├── constant │ │ └── ZooKeeperConstant.java │ ├── annotation │ │ ├── SuperScheduledOrder.java │ │ └── SuperScheduledInteriorOrder.java │ └── utils │ │ ├── proxy │ │ ├── ProxyUtils.java │ │ ├── Chain.java │ │ └── Point.java │ │ ├── AnnotationUtils.java │ │ └── SerializableUtils.java │ ├── core │ ├── strategy │ │ ├── ScheduleStrategy.java │ │ ├── CronStrategy.java │ │ ├── FixedRateStrategy.java │ │ └── FixedDelayStrategy.java │ ├── RunnableInterceptor │ │ ├── strengthen │ │ │ ├── BaseStrengthen.java │ │ │ ├── ExecutionFlagStrengthen.java │ │ │ ├── LogStrengthen.java │ │ │ └── ColonyStrengthen.java │ │ ├── RunnableBaseInterceptor.java │ │ └── SuperScheduledRunnable.java │ ├── ScheduledFutureFactory.java │ ├── SuperScheduledPostProcessor.java │ ├── SuperScheduledConfig.java │ ├── SuperScheduledApplicationRunner.java │ └── SuperScheduledManager.java │ ├── exception │ └── SuperScheduledException.java │ ├── model │ ├── ScheduledRunningContext.java │ ├── IncObjectOutputStream.java │ ├── ScheduledLogFile.java │ ├── ScheduledLog.java │ └── ScheduledSource.java │ ├── api │ └── manager │ │ ├── ScheduledCronApi.java │ │ ├── ScheduledNameApi.java │ │ ├── ScheduledBaseApi.java │ │ ├── ScheduledFixedRateApi.java │ │ └── ScheduledFixedDelayApi.java │ ├── enums │ └── ScheduledType.java │ ├── properties │ ├── PlugInProperties.java │ ├── ThreadPoolTaskSchedulerProperties.java │ └── ZooKeeperProperties.java │ └── SuperScheduledAutoConfiguration.java ├── pom.xml ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | super-scheduled.iml 3 | target/ 4 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | # Auto Configure 2 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 3 | com.gyx.superscheduled.SuperScheduledAutoConfiguration -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/common/constant/ZooKeeperConstant.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.common.constant; 2 | 3 | public class ZooKeeperConstant { 4 | public static String ROOT_PATH = "/superScheduled"; 5 | public static String COLONY_STRENGTHEN = "/ColonyStrengthen"; 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/common/annotation/SuperScheduledOrder.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.common.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Retention(RetentionPolicy.RUNTIME) 6 | @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) 7 | @Documented 8 | public @interface SuperScheduledOrder { 9 | int value() default 0; 10 | } -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/common/annotation/SuperScheduledInteriorOrder.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.common.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Retention(RetentionPolicy.RUNTIME) 6 | @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) 7 | @Documented 8 | public @interface SuperScheduledInteriorOrder { 9 | int value() default 0; 10 | } -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/core/strategy/ScheduleStrategy.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.core.strategy; 2 | 3 | import com.gyx.superscheduled.model.ScheduledSource; 4 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 5 | 6 | import java.util.concurrent.ScheduledFuture; 7 | 8 | public interface ScheduleStrategy { 9 | ScheduledFuture schedule(ThreadPoolTaskScheduler threadPoolTaskScheduler, ScheduledSource scheduledSource, Runnable runnable); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/exception/SuperScheduledException.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.exception; 2 | 3 | public class SuperScheduledException extends RuntimeException { 4 | public SuperScheduledException() { 5 | super("未知错误"); 6 | } 7 | 8 | public SuperScheduledException(String message) { 9 | super(message); 10 | } 11 | 12 | public SuperScheduledException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/core/strategy/CronStrategy.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.core.strategy; 2 | 3 | import com.gyx.superscheduled.model.ScheduledSource; 4 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 5 | import org.springframework.scheduling.support.CronTrigger; 6 | 7 | import java.util.concurrent.ScheduledFuture; 8 | 9 | public class CronStrategy implements ScheduleStrategy { 10 | @Override 11 | public ScheduledFuture schedule(ThreadPoolTaskScheduler threadPoolTaskScheduler, ScheduledSource scheduledSource, Runnable runnable) { 12 | return threadPoolTaskScheduler.schedule(runnable, new CronTrigger(scheduledSource.getCron())); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/common/utils/proxy/ProxyUtils.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.common.utils.proxy; 2 | 3 | import org.springframework.cglib.core.DebuggingClassWriter; 4 | import org.springframework.cglib.proxy.Enhancer; 5 | import org.springframework.cglib.proxy.MethodInterceptor; 6 | 7 | public class ProxyUtils { 8 | public static T getInstance(Class clazz, MethodInterceptor interceptor) { 9 | System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\tmp\\file"); 10 | //字节码加强器:用来创建动态代理类 11 | Enhancer enhancer = new Enhancer(); 12 | //代理的目标对象 13 | enhancer.setSuperclass(clazz); 14 | //回调类,在代理类方法调用时会回调Callback类的intercept方法 15 | enhancer.setCallback(interceptor); 16 | //创建代理类 17 | Object result = enhancer.create(); 18 | return (T) result; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/model/ScheduledRunningContext.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.model; 2 | 3 | import java.io.Serializable; 4 | import java.time.LocalDate; 5 | import java.time.format.DateTimeFormatter; 6 | import java.util.Date; 7 | 8 | public class ScheduledRunningContext implements Serializable { 9 | private static final long serialVersionUID = 2525367910036678105L; 10 | 11 | private Boolean callOff=false; 12 | 13 | private String callOffRemark; 14 | 15 | public Boolean getCallOff() { 16 | return callOff; 17 | } 18 | 19 | public void setCallOff(Boolean callOff) { 20 | this.callOff = callOff; 21 | } 22 | 23 | public String getCallOffRemark() { 24 | return callOffRemark; 25 | } 26 | 27 | public void setCallOffRemark(String callOffRemark) { 28 | this.callOffRemark = callOffRemark; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/api/manager/ScheduledCronApi.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.api.manager; 2 | 3 | 4 | import com.gyx.superscheduled.core.SuperScheduledManager; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.*; 7 | 8 | @RestController 9 | @RequestMapping("/scheduled/cron") 10 | public class ScheduledCronApi { 11 | 12 | @Autowired 13 | private SuperScheduledManager superScheduledManager; 14 | 15 | @PostMapping("/{name}/add") 16 | public void addCronScheduled(@PathVariable("name") String name,@RequestBody String cron) { 17 | superScheduledManager.addCronScheduled(name, cron); 18 | } 19 | 20 | @PostMapping("/{name}/set") 21 | public void setScheduledCron(@PathVariable("name") String name,@RequestBody String cron) { 22 | superScheduledManager.setScheduledCron(name, cron); 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/common/utils/proxy/Chain.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.common.utils.proxy; 2 | 3 | import java.util.List; 4 | 5 | public class Chain { 6 | private List list; 7 | private int index = -1; 8 | 9 | public List getList() { 10 | return list; 11 | } 12 | 13 | public void setList(List list) { 14 | this.list = list; 15 | } 16 | 17 | public int getIndex() { 18 | return index; 19 | } 20 | 21 | /** 22 | * 索引自增1 23 | */ 24 | public int incIndex() { 25 | return ++index; 26 | } 27 | 28 | /** 29 | * 索引还原 30 | */ 31 | public void resetIndex() { 32 | this.index = -1; 33 | } 34 | 35 | public void setIndex(int index) { 36 | this.index = index; 37 | } 38 | 39 | public Chain() { 40 | } 41 | 42 | public Chain(List list) { 43 | this.list = list; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/api/manager/ScheduledNameApi.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.api.manager; 2 | 3 | 4 | import com.gyx.superscheduled.core.SuperScheduledManager; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | import java.util.List; 11 | 12 | @RestController 13 | @RequestMapping("/scheduled/name") 14 | public class ScheduledNameApi { 15 | 16 | @Autowired 17 | private SuperScheduledManager superScheduledManager; 18 | 19 | @GetMapping("/all") 20 | public List getAllSuperScheduledName() { 21 | return superScheduledManager.getAllSuperScheduledName(); 22 | } 23 | 24 | @GetMapping("/run") 25 | public List getRunScheduledName() { 26 | return superScheduledManager.getRunScheduledName(); 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/model/IncObjectOutputStream.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.model; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.io.ObjectOutputStream; 7 | 8 | public class IncObjectOutputStream extends ObjectOutputStream { 9 | public static File file; 10 | 11 | public IncObjectOutputStream(File file) throws IOException, SecurityException { 12 | super(new FileOutputStream(file, true)); 13 | } 14 | 15 | @Override 16 | public void writeStreamHeader() throws IOException { 17 | if (file != null) { 18 | if (file.length() == 0) { 19 | //存在文件,但是文件里面内容为空 20 | super.writeStreamHeader(); 21 | } 22 | } else { 23 | //文件不存在 24 | super.writeStreamHeader(); 25 | } 26 | } 27 | 28 | public File getFile() { 29 | return file; 30 | } 31 | 32 | public void setFile(File file) { 33 | this.file = file; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/enums/ScheduledType.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.enums; 2 | 3 | public enum ScheduledType { 4 | CRON(1, "cron"), 5 | FIXED_DELAY(2, "fixedDelay"), 6 | FIXED_RATE(3, "fixedRate"); 7 | 8 | private int key; 9 | 10 | private String value; 11 | 12 | public String getValue() { 13 | return value; 14 | } 15 | 16 | public void setValue(String value) { 17 | this.value = value; 18 | } 19 | 20 | public int getKey() { 21 | return key; 22 | } 23 | 24 | public void setKey(int key) { 25 | this.key = key; 26 | } 27 | 28 | ScheduledType(int key, String value) { 29 | this.key = key; 30 | this.value = value; 31 | } 32 | 33 | ScheduledType() { 34 | } 35 | 36 | public static String getValue(Integer key) { 37 | if (key == null) { 38 | return null; 39 | } 40 | ScheduledType[] enums = values(); 41 | for (ScheduledType enu : enums) { 42 | if (enu.getKey() == key) { 43 | return enu.getValue(); 44 | } 45 | } 46 | return null; 47 | } 48 | } -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/model/ScheduledLogFile.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.model; 2 | 3 | import java.io.File; 4 | import java.io.Serializable; 5 | import java.time.LocalDate; 6 | import java.time.format.DateTimeFormatter; 7 | import java.util.Date; 8 | 9 | public class ScheduledLogFile implements Serializable { 10 | private String path; 11 | 12 | private String fileName; 13 | 14 | private String size; 15 | 16 | public ScheduledLogFile(File file) { 17 | this.path = file.getPath(); 18 | this.fileName = file.getName(); 19 | this.size = String.valueOf(file.length()/1024); 20 | } 21 | 22 | public ScheduledLogFile() { 23 | } 24 | 25 | public String getPath() { 26 | return path; 27 | } 28 | 29 | public void setPath(String path) { 30 | this.path = path; 31 | } 32 | 33 | public String getFileName() { 34 | return fileName; 35 | } 36 | 37 | public void setFileName(String fileName) { 38 | this.fileName = fileName; 39 | } 40 | 41 | public String getSize() { 42 | return size; 43 | } 44 | 45 | public void setSize(String size) { 46 | this.size = size; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/core/strategy/FixedRateStrategy.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.core.strategy; 2 | 3 | import com.gyx.superscheduled.model.ScheduledSource; 4 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 5 | import org.springframework.scheduling.config.FixedRateTask; 6 | 7 | import java.util.Date; 8 | import java.util.concurrent.ScheduledFuture; 9 | 10 | public class FixedRateStrategy implements ScheduleStrategy { 11 | @Override 12 | public ScheduledFuture schedule(ThreadPoolTaskScheduler threadPoolTaskScheduler, ScheduledSource scheduledSource, Runnable runnable) { 13 | Long fixedRate = scheduledSource.getFixedRateString() == null ? scheduledSource.getFixedRate() : Long.valueOf(scheduledSource.getFixedRateString()); 14 | Long initialDelay = scheduledSource.getInitialDelayString() == null ? scheduledSource.getInitialDelay() : Long.valueOf(scheduledSource.getInitialDelayString()); 15 | if (initialDelay == null) { 16 | initialDelay = 0L; 17 | } 18 | FixedRateTask fixedRateTask = new FixedRateTask(runnable, fixedRate, initialDelay); 19 | Date startTime = new Date(System.currentTimeMillis() + fixedRateTask.getInitialDelay()); 20 | return threadPoolTaskScheduler.scheduleAtFixedRate(runnable, startTime, fixedRateTask.getInterval()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/core/strategy/FixedDelayStrategy.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.core.strategy; 2 | 3 | import com.gyx.superscheduled.model.ScheduledSource; 4 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 5 | import org.springframework.scheduling.config.FixedDelayTask; 6 | 7 | import java.util.Date; 8 | import java.util.concurrent.ScheduledFuture; 9 | 10 | public class FixedDelayStrategy implements ScheduleStrategy { 11 | @Override 12 | public ScheduledFuture schedule(ThreadPoolTaskScheduler threadPoolTaskScheduler, ScheduledSource scheduledSource, Runnable runnable) { 13 | Long fixedDelay = scheduledSource.getFixedDelayString() == null ? scheduledSource.getFixedDelay() : Long.valueOf(scheduledSource.getFixedDelayString()); 14 | Long initialDelay = scheduledSource.getInitialDelayString() == null ? scheduledSource.getInitialDelay() : Long.valueOf(scheduledSource.getInitialDelayString()); 15 | if (initialDelay == null) { 16 | initialDelay = 0L; 17 | } 18 | FixedDelayTask fixedDelayTask = new FixedDelayTask(runnable, fixedDelay, initialDelay); 19 | Date startTime = new Date(System.currentTimeMillis() + fixedDelayTask.getInitialDelay()); 20 | return threadPoolTaskScheduler.scheduleWithFixedDelay(runnable, startTime, fixedDelayTask.getInterval()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/properties/PlugInProperties.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.properties; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | @ConfigurationProperties(prefix = "spring.super.scheduled.plug-interfaces") 6 | public class PlugInProperties { 7 | private Boolean executionFlag = false; 8 | private Boolean executionLog = false; 9 | private Boolean colony = false; 10 | private String logPath = ""; 11 | private String colonyName = "colony"; 12 | 13 | public Boolean getExecutionFlag() { 14 | return executionFlag; 15 | } 16 | 17 | public void setExecutionFlag(Boolean executionFlag) { 18 | this.executionFlag = executionFlag; 19 | } 20 | 21 | public Boolean getExecutionLog() { 22 | return executionLog; 23 | } 24 | 25 | public void setExecutionLog(Boolean executionLog) { 26 | this.executionLog = executionLog; 27 | } 28 | 29 | public String getLogPath() { 30 | return logPath; 31 | } 32 | 33 | public void setLogPath(String logPath) { 34 | this.logPath = logPath; 35 | } 36 | 37 | public Boolean getColony() { 38 | return colony; 39 | } 40 | 41 | public void setColony(Boolean colony) { 42 | this.colony = colony; 43 | } 44 | 45 | public String getColonyName() { 46 | return colonyName; 47 | } 48 | 49 | public void setColonyName(String colonyName) { 50 | this.colonyName = colonyName; 51 | } 52 | } -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/properties/ThreadPoolTaskSchedulerProperties.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.properties; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | @ConfigurationProperties(prefix = "spring.super.scheduled.thread-pool") 6 | public class ThreadPoolTaskSchedulerProperties { 7 | private Integer poolSize = 10; 8 | private String threadNamePrefix; 9 | private Boolean waitForTasksToCompleteOnShutdown = false; 10 | private Integer awaitTerminationSeconds = 0; 11 | 12 | public Integer getPoolSize() { 13 | return poolSize; 14 | } 15 | 16 | public void setPoolSize(Integer poolSize) { 17 | this.poolSize = poolSize; 18 | } 19 | 20 | public String getThreadNamePrefix() { 21 | return threadNamePrefix; 22 | } 23 | 24 | public void setThreadNamePrefix(String threadNamePrefix) { 25 | this.threadNamePrefix = threadNamePrefix; 26 | } 27 | 28 | public Boolean getWaitForTasksToCompleteOnShutdown() { 29 | return waitForTasksToCompleteOnShutdown; 30 | } 31 | 32 | public void setWaitForTasksToCompleteOnShutdown(Boolean waitForTasksToCompleteOnShutdown) { 33 | this.waitForTasksToCompleteOnShutdown = waitForTasksToCompleteOnShutdown; 34 | } 35 | 36 | public Integer getAwaitTerminationSeconds() { 37 | return awaitTerminationSeconds; 38 | } 39 | 40 | public void setAwaitTerminationSeconds(Integer awaitTerminationSeconds) { 41 | this.awaitTerminationSeconds = awaitTerminationSeconds; 42 | } 43 | } -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/properties/ZooKeeperProperties.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.properties; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | @ConfigurationProperties(prefix = "spring.super.scheduled.zookeeper") 6 | public class ZooKeeperProperties { 7 | private Integer sessionTimeout = 60000; 8 | 9 | private Integer connectionTimeout = 60000; 10 | 11 | private String url; 12 | 13 | private String zkPath; 14 | 15 | private String zkParentNodePath; 16 | 17 | public Integer getSessionTimeout() { 18 | return sessionTimeout; 19 | } 20 | 21 | public void setSessionTimeout(Integer sessionTimeout) { 22 | this.sessionTimeout = sessionTimeout; 23 | } 24 | 25 | public Integer getConnectionTimeout() { 26 | return connectionTimeout; 27 | } 28 | 29 | public void setConnectionTimeout(Integer connectionTimeout) { 30 | this.connectionTimeout = connectionTimeout; 31 | } 32 | 33 | public String getUrl() { 34 | return url; 35 | } 36 | 37 | public void setUrl(String url) { 38 | this.url = url; 39 | } 40 | 41 | public String getZkPath() { 42 | return zkPath; 43 | } 44 | 45 | public void setZkPath(String zkPath) { 46 | this.zkPath = zkPath; 47 | } 48 | 49 | public String getZkParentNodePath() { 50 | return zkParentNodePath; 51 | } 52 | 53 | public void setZkParentNodePath(String zkParentNodePath) { 54 | this.zkParentNodePath = zkParentNodePath; 55 | } 56 | } -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/common/utils/proxy/Point.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.common.utils.proxy; 2 | 3 | import com.gyx.superscheduled.core.RunnableInterceptor.SuperScheduledRunnable; 4 | import com.gyx.superscheduled.model.ScheduledSource; 5 | 6 | public abstract class Point { 7 | /** 8 | * 定时任务名 9 | */ 10 | private String superScheduledName; 11 | 12 | private ScheduledSource scheduledSource; 13 | 14 | /** 15 | * 执行顺序 16 | */ 17 | private Integer order; 18 | 19 | /** 20 | * 内部执行顺序 21 | */ 22 | private Integer interiorOrder; 23 | 24 | /** 25 | * 抽象的执行方法,使用代理实现 26 | * 27 | * @param runnable 定时任务执行器 28 | */ 29 | public abstract Object invoke(SuperScheduledRunnable runnable); 30 | 31 | public String getSuperScheduledName() { 32 | return superScheduledName; 33 | } 34 | 35 | public void setSuperScheduledName(String superScheduledName) { 36 | this.superScheduledName = superScheduledName; 37 | } 38 | 39 | public ScheduledSource getScheduledSource() { 40 | return scheduledSource; 41 | } 42 | 43 | public void setScheduledSource(ScheduledSource scheduledSource) { 44 | this.scheduledSource = scheduledSource; 45 | } 46 | 47 | public Integer getOrder() { 48 | return order; 49 | } 50 | 51 | public void setOrder(Integer order) { 52 | this.order = order; 53 | } 54 | 55 | public Integer getInteriorOrder() { 56 | return interiorOrder; 57 | } 58 | 59 | public void setInteriorOrder(Integer interiorOrder) { 60 | this.interiorOrder = interiorOrder; 61 | } 62 | } -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/core/RunnableInterceptor/strengthen/BaseStrengthen.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.core.RunnableInterceptor.strengthen; 2 | 3 | import com.gyx.superscheduled.model.ScheduledRunningContext; 4 | import org.springframework.core.Ordered; 5 | import org.springframework.core.annotation.Order; 6 | 7 | import java.lang.reflect.Method; 8 | 9 | @Order(Ordered.HIGHEST_PRECEDENCE) 10 | public interface BaseStrengthen { 11 | /** 12 | * 前置强化方法 13 | * 14 | * @param bean bean实例(或者是被代理的bean) 15 | * @param method 执行的方法对象 16 | * @param args 方法参数 17 | * @param context 任务线程运行时的上下文 18 | */ 19 | void before(Object bean, Method method, Object[] args, ScheduledRunningContext context); 20 | 21 | /** 22 | * 后置强化方法 23 | * 出现异常不会执行 24 | * 如果未出现异常,在afterFinally方法之后执行 25 | * 26 | * @param bean bean实例(或者是被代理的bean) 27 | * @param method 执行的方法对象 28 | * @param args 方法参数 29 | * @param context 任务线程运行时的上下文 30 | */ 31 | void after(Object bean, Method method, Object[] args, ScheduledRunningContext context); 32 | 33 | /** 34 | * 异常强化方法 35 | * 36 | * @param bean bean实例(或者是被代理的bean) 37 | * @param method 执行的方法对象 38 | * @param args 方法参数 39 | * @param context 任务线程运行时的上下文 40 | */ 41 | void exception(Object bean, Method method, Object[] args, ScheduledRunningContext context); 42 | 43 | /** 44 | * Finally强化方法,出现异常也会执行 45 | * 46 | * @param bean bean实例(或者是被代理的bean) 47 | * @param method 执行的方法对象 48 | * @param args 方法参数 49 | * @param context 任务线程运行时的上下文 50 | */ 51 | void afterFinally(Object bean, Method method, Object[] args, ScheduledRunningContext context); 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/api/manager/ScheduledBaseApi.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.api.manager; 2 | 3 | 4 | import com.gyx.superscheduled.core.SuperScheduledManager; 5 | import com.gyx.superscheduled.model.ScheduledLog; 6 | import com.gyx.superscheduled.model.ScheduledLogFile; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | import java.util.List; 11 | 12 | @RestController 13 | @RequestMapping("/scheduled") 14 | public class ScheduledBaseApi { 15 | 16 | @Autowired 17 | private SuperScheduledManager superScheduledManager; 18 | 19 | /** 20 | * 手动执行一次任务 21 | * 22 | * @param name 定时任务名称 23 | */ 24 | @PostMapping("/{name}/run") 25 | public void runScheduled(@PathVariable("name") String name) { 26 | superScheduledManager.runScheduled(name); 27 | } 28 | 29 | /** 30 | * 结束正在执行中的任务,跳过这次运行 31 | * 只有在每个前置增强器结束之后才会判断是否需要跳过此次运行 32 | * 33 | * @param name 定时任务名称 34 | */ 35 | @PostMapping("/{name}/callOff") 36 | public void callOffScheduled(@PathVariable("name") String name) { 37 | superScheduledManager.callOffScheduled(name); 38 | } 39 | 40 | /** 41 | * 终止定时任务 42 | * 43 | * @param name 定时任务名称 44 | */ 45 | @DeleteMapping("/{name}") 46 | public void cancelScheduled(@PathVariable("name") String name) { 47 | superScheduledManager.cancelScheduled(name); 48 | } 49 | 50 | /** 51 | * 获取日志文件信息 52 | */ 53 | @GetMapping("/log/files") 54 | public List logFiles() { 55 | return superScheduledManager.getScheduledLogFiles(); 56 | } 57 | 58 | /** 59 | * 获取日志信息 60 | */ 61 | @GetMapping("/log/{fileName}") 62 | public List getLogs(@PathVariable("fileName") String fileName) { 63 | return superScheduledManager.getScheduledLogs(fileName); 64 | } 65 | } -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/common/utils/AnnotationUtils.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.common.utils; 2 | 3 | import com.gyx.superscheduled.common.utils.proxy.Point; 4 | 5 | import java.lang.annotation.Annotation; 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.InvocationHandler; 8 | import java.lang.reflect.Proxy; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | public class AnnotationUtils { 13 | public static Object changeAnnotationValue(Annotation annotation, String key, Object newValue) throws Exception { 14 | InvocationHandler handler = Proxy.getInvocationHandler(annotation); 15 | Field f; 16 | try { 17 | f = handler.getClass().getDeclaredField("memberValues"); 18 | } catch (Exception e) { 19 | e.printStackTrace(); 20 | throw new IllegalStateException(e); 21 | } 22 | f.setAccessible(true); 23 | Map memberValues; 24 | memberValues = (Map) f.get(handler); 25 | Object oldValue = memberValues.get(key); 26 | if (oldValue == null || oldValue.getClass() != newValue.getClass()) { 27 | throw new IllegalArgumentException(); 28 | } 29 | memberValues.put(key, newValue); 30 | return oldValue; 31 | } 32 | 33 | /** 34 | * 根据SuperScheduledOrder排序 35 | */ 36 | public static void superScheduledOrderSort(List points) { 37 | if (points == null || points.isEmpty()) { 38 | return; 39 | } 40 | points.sort((o1, o2) -> { 41 | if (o1.getInteriorOrder() == null && o2.getInteriorOrder() == null) { 42 | return o2.getOrder().compareTo(o1.getOrder()); 43 | }else if (o1.getInteriorOrder() != null && o2.getInteriorOrder() != null){ 44 | return o2.getInteriorOrder().compareTo(o1.getInteriorOrder()); 45 | }else if (o1.getInteriorOrder() != null){ 46 | return -1; 47 | }else{ 48 | return 1; 49 | } 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/api/manager/ScheduledFixedRateApi.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.api.manager; 2 | 3 | 4 | import com.gyx.superscheduled.core.SuperScheduledManager; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @RestController 12 | @RequestMapping("/scheduled/fixedRate") 13 | public class ScheduledFixedRateApi { 14 | 15 | @Autowired 16 | private SuperScheduledManager superScheduledManager; 17 | 18 | /** 19 | * 以FixedRate模式启动定时任务 20 | * @param name 定时任务名称 21 | * @param fixedRate 运行间隔时间 22 | * @param initialDelay 首次运行延迟时间 23 | */ 24 | @PostMapping("/{name}/add/{fixedRate}/{initialDelay}") 25 | public void addFixedRateScheduled(@PathVariable("name") String name, 26 | @PathVariable("fixedRate") Long fixedRate, 27 | @PathVariable("initialDelay") Long initialDelay) { 28 | superScheduledManager.addFixedRateScheduled(name, fixedRate, initialDelay); 29 | } 30 | 31 | /** 32 | * 以FixedRate模式启动定时任务 33 | * @param name 定时任务名称 34 | * @param fixedRate 运行间隔时间 35 | */ 36 | @PostMapping("/{name}/add/{fixedRate}") 37 | public void addFixedRateScheduled(@PathVariable("name") String name, 38 | @PathVariable("fixedRate") Long fixedRate) { 39 | superScheduledManager.addFixedRateScheduled(name, fixedRate); 40 | } 41 | 42 | /** 43 | * 将定时任务转为FixedRate模式运行,并修改执行间隔的参数值 44 | * @param name 定时任务名称 45 | * @param fixedRate 运行间隔时间 46 | */ 47 | @PostMapping("/{name}/set/{fixedRate}") 48 | public void setScheduledFixedRate(@PathVariable("name") String name, 49 | @PathVariable("fixedRate") Long fixedRate) { 50 | superScheduledManager.setScheduledFixedRate(name, fixedRate); 51 | } 52 | } -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/api/manager/ScheduledFixedDelayApi.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.api.manager; 2 | 3 | 4 | import com.gyx.superscheduled.core.SuperScheduledManager; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @RestController 12 | @RequestMapping("/scheduled/fixedDelay") 13 | public class ScheduledFixedDelayApi { 14 | 15 | @Autowired 16 | private SuperScheduledManager superScheduledManager; 17 | 18 | /** 19 | * 以FixedDelay模式启动定时任务 20 | * @param name 定时任务名称 21 | * @param fixedDelay 运行间隔时间 22 | * @param initialDelay 首次运行延迟时间 23 | */ 24 | @PostMapping("/{name}/add/{fixedDelay}/{initialDelay}") 25 | public void addFixedRateScheduled(@PathVariable("name") String name, 26 | @PathVariable("fixedDelay") Long fixedDelay, 27 | @PathVariable("initialDelay") Long initialDelay) { 28 | superScheduledManager.addFixedDelayScheduled(name, fixedDelay, initialDelay); 29 | } 30 | 31 | /** 32 | * 以FixedDelay模式启动定时任务 33 | * @param name 定时任务名称 34 | * @param fixedDelay 运行间隔时间 35 | */ 36 | @PostMapping("/{name}/add/{fixedDelay}") 37 | public void addFixedDelayScheduled(@PathVariable("name") String name, 38 | @PathVariable("fixedDelay") Long fixedDelay) { 39 | superScheduledManager.addFixedDelayScheduled(name, fixedDelay); 40 | } 41 | 42 | /** 43 | * 将定时任务转为FixedDelay模式运行,并修改执行间隔的参数值 44 | * @param name 定时任务名称 45 | * @param fixedDelay 运行间隔时间 46 | */ 47 | @PostMapping("/{name}/set/{fixedDelay}") 48 | public void setScheduledFixedRate(@PathVariable("name") String name, 49 | @PathVariable("fixedDelay") Long fixedDelay) { 50 | superScheduledManager.setScheduledFixedDelay(name, fixedDelay); 51 | } 52 | } -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/core/RunnableInterceptor/strengthen/ExecutionFlagStrengthen.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.core.RunnableInterceptor.strengthen; 2 | 3 | import com.gyx.superscheduled.common.annotation.SuperScheduledInteriorOrder; 4 | import com.gyx.superscheduled.common.utils.proxy.Point; 5 | import com.gyx.superscheduled.model.ScheduledRunningContext; 6 | import org.apache.commons.logging.Log; 7 | import org.apache.commons.logging.LogFactory; 8 | 9 | import java.lang.reflect.Method; 10 | 11 | @SuperScheduledInteriorOrder 12 | public class ExecutionFlagStrengthen implements BaseStrengthen { 13 | protected final Log logger = LogFactory.getLog(getClass()); 14 | 15 | /** 16 | * 前置强化方法 17 | * 18 | * @param bean bean实例(或者是被代理的bean) 19 | * @param method 执行的方法对象 20 | * @param args 方法参数 21 | * @param context 任务线程运行时的上下文 22 | */ 23 | @Override 24 | public void before(Object bean, Method method, Object[] args, ScheduledRunningContext context) { 25 | Point point = (Point) bean; 26 | logger.info("定时任务" + point.getSuperScheduledName() + "开始执行"); 27 | } 28 | 29 | /** 30 | * 后置强化方法 31 | * 32 | * @param bean bean实例(或者是被代理的bean) 33 | * @param method 执行的方法对象 34 | * @param args 方法参数 35 | * @param context 任务线程运行时的上下文 36 | */ 37 | @Override 38 | public void after(Object bean, Method method, Object[] args, ScheduledRunningContext context) { 39 | Point point = (Point) bean; 40 | logger.info("定时任务" + point.getSuperScheduledName() + "执行结束"); 41 | } 42 | 43 | /** 44 | * 异常强化方法 45 | * 46 | * @param bean bean实例(或者是被代理的bean) 47 | * @param method 执行的方法对象 48 | * @param args 方法参数 49 | * @param context 任务线程运行时的上下文 50 | */ 51 | @Override 52 | public void exception(Object bean, Method method, Object[] args, ScheduledRunningContext context) { 53 | 54 | } 55 | 56 | /** 57 | * Finally强化方法,出现异常也会执行 58 | * 59 | * @param bean bean实例(或者是被代理的bean) 60 | * @param method 执行的方法对象 61 | * @param args 方法参数 62 | * @param context 任务线程运行时的上下文 63 | */ 64 | @Override 65 | public void afterFinally(Object bean, Method method, Object[] args, ScheduledRunningContext context) { 66 | 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/core/ScheduledFutureFactory.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.core; 2 | 3 | import com.gyx.superscheduled.core.strategy.CronStrategy; 4 | import com.gyx.superscheduled.core.strategy.FixedDelayStrategy; 5 | import com.gyx.superscheduled.core.strategy.FixedRateStrategy; 6 | import com.gyx.superscheduled.core.strategy.ScheduleStrategy; 7 | import com.gyx.superscheduled.enums.ScheduledType; 8 | import com.gyx.superscheduled.exception.SuperScheduledException; 9 | import com.gyx.superscheduled.model.ScheduledSource; 10 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | import java.util.concurrent.ScheduledFuture; 15 | 16 | public class ScheduledFutureFactory { 17 | private static Map strategyCache = new HashMap<>(3); 18 | 19 | /** 20 | * 策略启动定时任务 21 | * 22 | * @param threadPoolTaskScheduler 定时任务线程池 23 | * @param scheduledSource 定时任务源信息 24 | * @param runnable 执行逻辑 25 | */ 26 | public static ScheduledFuture create(ThreadPoolTaskScheduler threadPoolTaskScheduler, ScheduledSource scheduledSource, Runnable runnable) { 27 | ScheduledType type = scheduledSource.getType(); 28 | ScheduleStrategy scheduleStrategy = scheduleStrategy(type); 29 | return scheduleStrategy.schedule(threadPoolTaskScheduler, scheduledSource, runnable); 30 | } 31 | 32 | /** 33 | * 静态工厂 34 | * 35 | * @param type 定时任务的类型 36 | */ 37 | private static ScheduleStrategy scheduleStrategy(ScheduledType type) { 38 | ScheduleStrategy scheduleStrategy = strategyCache.get(type); 39 | if (scheduleStrategy != null) { 40 | return scheduleStrategy; 41 | } 42 | switch (type) { 43 | case CRON: 44 | scheduleStrategy = new CronStrategy(); 45 | break; 46 | case FIXED_DELAY: 47 | scheduleStrategy = new FixedDelayStrategy(); 48 | break; 49 | case FIXED_RATE: 50 | scheduleStrategy = new FixedRateStrategy(); 51 | break; 52 | default: 53 | throw new SuperScheduledException("创建定时任务,执行失败,无法确定创建定时任务的类型"); 54 | } 55 | strategyCache.put(type, scheduleStrategy); 56 | return scheduleStrategy; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/core/RunnableInterceptor/RunnableBaseInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.core.RunnableInterceptor; 2 | 3 | import com.gyx.superscheduled.core.RunnableInterceptor.strengthen.BaseStrengthen; 4 | import com.gyx.superscheduled.exception.SuperScheduledException; 5 | import org.apache.commons.logging.Log; 6 | import org.apache.commons.logging.LogFactory; 7 | import org.springframework.cglib.proxy.MethodInterceptor; 8 | import org.springframework.cglib.proxy.MethodProxy; 9 | 10 | import java.lang.reflect.Method; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | public class RunnableBaseInterceptor implements MethodInterceptor { 15 | protected final Log logger = LogFactory.getLog(getClass()); 16 | /** 17 | * 定时任务执行器 18 | */ 19 | private SuperScheduledRunnable runnable; 20 | /** 21 | * 定时任务增强类 22 | */ 23 | private BaseStrengthen strengthen; 24 | 25 | @Override 26 | public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 27 | Object result; 28 | List methodName = Arrays.asList("invoke", "before", "after", "exception", "afterFinally"); 29 | if (methodName.contains(method.getName())) { 30 | strengthen.before(obj, method, args, runnable.getContext()); 31 | try { 32 | result = runnable.invoke(); 33 | } catch (Exception e) { 34 | strengthen.exception(obj, method, args, runnable.getContext()); 35 | throw new SuperScheduledException(strengthen.getClass() + "中强化执行时发生错误", e); 36 | } finally { 37 | strengthen.afterFinally(obj, method, args, runnable.getContext()); 38 | } 39 | strengthen.after(obj, method, args, runnable.getContext()); 40 | } else { 41 | result = methodProxy.invokeSuper(obj, args); 42 | } 43 | return result; 44 | } 45 | 46 | public RunnableBaseInterceptor(Object object, SuperScheduledRunnable runnable) { 47 | this.runnable = runnable; 48 | if (BaseStrengthen.class.isAssignableFrom(object.getClass())) { 49 | this.strengthen = (BaseStrengthen) object; 50 | } else { 51 | throw new SuperScheduledException(object.getClass() + "对象不是BaseStrengthen类型"); 52 | } 53 | } 54 | 55 | public RunnableBaseInterceptor() { 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/core/RunnableInterceptor/SuperScheduledRunnable.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.core.RunnableInterceptor; 2 | 3 | import com.gyx.superscheduled.common.utils.proxy.Chain; 4 | import com.gyx.superscheduled.common.utils.proxy.Point; 5 | import com.gyx.superscheduled.exception.SuperScheduledException; 6 | import com.gyx.superscheduled.model.ScheduledRunningContext; 7 | import org.apache.commons.logging.Log; 8 | import org.apache.commons.logging.LogFactory; 9 | 10 | import java.lang.reflect.InvocationTargetException; 11 | import java.lang.reflect.Method; 12 | 13 | public class SuperScheduledRunnable { 14 | protected final Log logger = LogFactory.getLog(getClass()); 15 | 16 | /** 17 | * 原始的方法 18 | */ 19 | private Method method; 20 | /** 21 | * 方法所在的bean 22 | */ 23 | private Object bean; 24 | /** 25 | * 增强器的调用链 26 | */ 27 | private Chain chain; 28 | /** 29 | * 线程运行时的上下文 30 | */ 31 | private ScheduledRunningContext context; 32 | 33 | 34 | public Object invoke() { 35 | Object result = null; 36 | if (!context.getCallOff()) { 37 | //索引自增1 38 | if (chain.incIndex() == chain.getList().size()) { 39 | //调用链中的增强方法已经全部执行结束 40 | try { 41 | //调用链索引初始化 42 | chain.resetIndex(); 43 | //执行原本的方法 44 | result = method.invoke(bean); 45 | } catch (IllegalAccessException | InvocationTargetException e) { 46 | throw new SuperScheduledException(e.getLocalizedMessage()); 47 | } 48 | } else { 49 | //获取被代理后的方法增强 50 | Point point = chain.getList().get(chain.getIndex()); 51 | //执行增强方法 52 | result = point.invoke(this); 53 | } 54 | } else { 55 | logger.info(getContext().getCallOffRemark()); 56 | chain.resetIndex(); 57 | context.setCallOff(false); 58 | } 59 | return result; 60 | } 61 | 62 | public SuperScheduledRunnable() { 63 | this.context = new ScheduledRunningContext(); 64 | } 65 | 66 | public Method getMethod() { 67 | return method; 68 | } 69 | 70 | public void setMethod(Method method) { 71 | this.method = method; 72 | } 73 | 74 | public Object getBean() { 75 | return bean; 76 | } 77 | 78 | public void setBean(Object bean) { 79 | this.bean = bean; 80 | } 81 | 82 | public Chain getChain() { 83 | return chain; 84 | } 85 | 86 | public void setChain(Chain chain) { 87 | this.chain = chain; 88 | } 89 | 90 | public ScheduledRunningContext getContext() { 91 | return context; 92 | } 93 | 94 | public void setContext(ScheduledRunningContext context) { 95 | this.context = context; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/model/ScheduledLog.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.model; 2 | 3 | import com.gyx.superscheduled.enums.ScheduledType; 4 | import com.gyx.superscheduled.exception.SuperScheduledException; 5 | import org.springframework.scheduling.annotation.Scheduled; 6 | import org.springframework.util.StringUtils; 7 | 8 | import java.io.Serializable; 9 | import java.lang.reflect.Method; 10 | import java.time.LocalDate; 11 | import java.time.format.DateTimeFormatter; 12 | import java.util.Arrays; 13 | import java.util.Date; 14 | import java.util.List; 15 | 16 | public class ScheduledLog implements Serializable { 17 | private static final long serialVersionUID = 2525367910036678105L; 18 | 19 | private String superScheduledName; 20 | 21 | private Date statrDate; 22 | 23 | private Date endDate; 24 | 25 | private Exception exception; 26 | 27 | private Long executionTime; 28 | 29 | private Boolean isSuccess; 30 | 31 | private ScheduledSource scheduledSource; 32 | 33 | private String fileName; 34 | 35 | public String getSuperScheduledName() { 36 | return superScheduledName; 37 | } 38 | 39 | public void setSuperScheduledName(String superScheduledName) { 40 | this.superScheduledName = superScheduledName; 41 | } 42 | 43 | public Date getStatrDate() { 44 | return statrDate; 45 | } 46 | 47 | public void setStatrDate(Date statrDate) { 48 | this.statrDate = statrDate; 49 | } 50 | 51 | public Date getEndDate() { 52 | return endDate; 53 | } 54 | 55 | public void setEndDate(Date endDate) { 56 | this.endDate = endDate; 57 | } 58 | 59 | public Exception getException() { 60 | return exception; 61 | } 62 | 63 | public void setException(Exception exception) { 64 | this.exception = exception; 65 | } 66 | 67 | public Long getExecutionTime() { 68 | return executionTime; 69 | } 70 | 71 | public void setExecutionTime(Long executionTime) { 72 | this.executionTime = executionTime; 73 | } 74 | 75 | public Boolean getSuccess() { 76 | return isSuccess; 77 | } 78 | 79 | public void setSuccess(Boolean success) { 80 | isSuccess = success; 81 | } 82 | 83 | public ScheduledSource getScheduledSource() { 84 | return scheduledSource; 85 | } 86 | 87 | public void setScheduledSource(ScheduledSource scheduledSource) { 88 | this.scheduledSource = scheduledSource; 89 | } 90 | 91 | public String getFileName() { 92 | return fileName; 93 | } 94 | 95 | public void generateFileName() { 96 | this.fileName = superScheduledName+ DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDate.now()); 97 | } 98 | 99 | public void computingTime(){ 100 | this.executionTime = this.getEndDate().getTime() - this.statrDate.getTime(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/core/RunnableInterceptor/strengthen/LogStrengthen.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.core.RunnableInterceptor.strengthen; 2 | 3 | import com.gyx.superscheduled.common.annotation.SuperScheduledInteriorOrder; 4 | import com.gyx.superscheduled.common.utils.SerializableUtils; 5 | import com.gyx.superscheduled.common.utils.proxy.Point; 6 | import com.gyx.superscheduled.model.ScheduledLog; 7 | import com.gyx.superscheduled.model.ScheduledRunningContext; 8 | 9 | import java.lang.reflect.Method; 10 | import java.util.Date; 11 | 12 | @SuperScheduledInteriorOrder(-1) 13 | public class LogStrengthen implements BaseStrengthen { 14 | private ScheduledLog scheduledLog; 15 | 16 | private String logPath; 17 | 18 | /** 19 | * 前置强化方法 20 | * 21 | * @param bean bean实例(或者是被代理的bean) 22 | * @param method 执行的方法对象 23 | * @param args 方法参数 24 | * @param context 任务线程运行时的上下文 25 | */ 26 | @Override 27 | public void before(Object bean, Method method, Object[] args, ScheduledRunningContext context) { 28 | Point point = (Point) bean; 29 | scheduledLog = new ScheduledLog(); 30 | scheduledLog.setScheduledSource(point.getScheduledSource()); 31 | scheduledLog.setStatrDate(new Date()); 32 | scheduledLog.setSuperScheduledName(point.getSuperScheduledName()); 33 | } 34 | 35 | /** 36 | * 后置强化方法 37 | * 38 | * @param bean bean实例(或者是被代理的bean) 39 | * @param method 执行的方法对象 40 | * @param args 方法参数 41 | * @param context 任务线程运行时的上下文 42 | */ 43 | @Override 44 | public void after(Object bean, Method method, Object[] args, ScheduledRunningContext context) { 45 | scheduledLog.setEndDate(new Date()); 46 | scheduledLog.setSuccess(Boolean.TRUE); 47 | scheduledLog.computingTime(); 48 | SerializableUtils.toIncFile(scheduledLog, logPath, scheduledLog.getFileName()); 49 | } 50 | 51 | /** 52 | * 异常强化方法 53 | * 54 | * @param bean bean实例(或者是被代理的bean) 55 | * @param method 执行的方法对象 56 | * @param args 方法参数 57 | * @param context 任务线程运行时的上下文 58 | */ 59 | @Override 60 | public void exception(Object bean, Method method, Object[] args, ScheduledRunningContext context) { 61 | scheduledLog.setSuccess(Boolean.FALSE); 62 | } 63 | 64 | /** 65 | * Finally强化方法,出现异常也会执行 66 | * 67 | * @param bean bean实例(或者是被代理的bean) 68 | * @param method 执行的方法对象 69 | * @param args 方法参数 70 | * @param context 任务线程运行时的上下文 71 | */ 72 | @Override 73 | public void afterFinally(Object bean, Method method, Object[] args, ScheduledRunningContext context) { 74 | scheduledLog.setEndDate(new Date()); 75 | scheduledLog.computingTime(); 76 | scheduledLog.generateFileName(); 77 | if (scheduledLog.getSuccess() != null && !scheduledLog.getSuccess()) { 78 | SerializableUtils.toIncFile(scheduledLog, logPath, scheduledLog.getFileName()); 79 | } 80 | } 81 | 82 | public LogStrengthen() { 83 | } 84 | 85 | public LogStrengthen(String logPath) { 86 | this.logPath = logPath; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/core/RunnableInterceptor/strengthen/ColonyStrengthen.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.core.RunnableInterceptor.strengthen; 2 | 3 | import com.gyx.superscheduled.common.annotation.SuperScheduledInteriorOrder; 4 | import com.gyx.superscheduled.exception.SuperScheduledException; 5 | import com.gyx.superscheduled.model.ScheduledRunningContext; 6 | import com.gyx.superscheduled.properties.ZooKeeperProperties; 7 | import org.I0Itec.zkclient.ZkClient; 8 | import org.apache.commons.logging.Log; 9 | import org.apache.commons.logging.LogFactory; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | 12 | import java.lang.reflect.Method; 13 | import java.util.Collections; 14 | import java.util.List; 15 | 16 | @SuperScheduledInteriorOrder(Integer.MAX_VALUE) 17 | public class ColonyStrengthen implements BaseStrengthen { 18 | protected final Log logger = LogFactory.getLog(getClass()); 19 | @Autowired 20 | private ZkClient zk; 21 | @Autowired 22 | private ZooKeeperProperties zooKeeperProperties; 23 | 24 | /** 25 | * 前置强化方法 26 | * 27 | * @param bean bean实例(或者是被代理的bean) 28 | * @param method 执行的方法对象 29 | * @param args 方法参数 30 | * @param context 任务线程运行时的上下文 31 | */ 32 | @Override 33 | public void before(Object bean, Method method, Object[] args, ScheduledRunningContext context) { 34 | boolean exists = zk.exists(zooKeeperProperties.getZkParentNodePath()); 35 | 36 | if (exists) { 37 | List children = zk .getChildren(zooKeeperProperties.getZkParentNodePath()); 38 | if (children != null && children.size() > 0) { 39 | Collections.sort(children); 40 | if (zooKeeperProperties.getZkPath().equals(zooKeeperProperties.getZkParentNodePath() + '/' + children.get(0))) { 41 | return; 42 | } 43 | } else { 44 | throw new SuperScheduledException("zookeeper连接出现异常"); 45 | } 46 | context.setCallOff(true); 47 | context.setCallOffRemark("任务已交由其他服务运行"); 48 | } 49 | } 50 | 51 | /** 52 | * 后置强化方法 53 | * 54 | * @param bean bean实例(或者是被代理的bean) 55 | * @param method 执行的方法对象 56 | * @param args 方法参数 57 | * @param context 任务线程运行时的上下文 58 | */ 59 | @Override 60 | public void after(Object bean, Method method, Object[] args, ScheduledRunningContext context) { 61 | } 62 | 63 | /** 64 | * 异常强化方法 65 | * 66 | * @param bean bean实例(或者是被代理的bean) 67 | * @param method 执行的方法对象 68 | * @param args 方法参数 69 | * @param context 任务线程运行时的上下文 70 | */ 71 | @Override 72 | public void exception(Object bean, Method method, Object[] args, ScheduledRunningContext context) { 73 | 74 | } 75 | 76 | /** 77 | * Finally强化方法,出现异常也会执行 78 | * 79 | * @param bean bean实例(或者是被代理的bean) 80 | * @param method 执行的方法对象 81 | * @param args 方法参数 82 | * @param context 任务线程运行时的上下文 83 | */ 84 | @Override 85 | public void afterFinally(Object bean, Method method, Object[] args, ScheduledRunningContext context) { 86 | } 87 | 88 | } 89 | 90 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/common/utils/SerializableUtils.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.common.utils; 2 | 3 | import com.gyx.superscheduled.exception.SuperScheduledException; 4 | import com.gyx.superscheduled.model.IncObjectOutputStream; 5 | import com.gyx.superscheduled.model.ScheduledLogFile; 6 | import org.springframework.util.StringUtils; 7 | 8 | import java.io.*; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class SerializableUtils { 13 | 14 | public static void toFile(Object obj, String fileSrc, String fileName) { 15 | fileSrc = existFile(fileSrc); 16 | fileSrc = fileSrc + File.separator + fileName; 17 | try (ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(fileSrc))) { 18 | os.writeObject(obj); 19 | os.flush(); 20 | } catch (IOException ex) { 21 | ex.printStackTrace(); 22 | } 23 | } 24 | 25 | public static void toIncFile(Object obj, String fileSrc, String fileName) { 26 | fileSrc = existFile(fileSrc); 27 | File file = new File(fileSrc + File.separator + fileName); 28 | IncObjectOutputStream.file = file; 29 | try (IncObjectOutputStream os = new IncObjectOutputStream(file)) { 30 | os.writeObject(obj); 31 | os.flush(); 32 | } catch (IOException ex) { 33 | ex.printStackTrace(); 34 | } 35 | } 36 | 37 | /** 38 | * 反序列化对象 39 | * @param fileSrc 文件夹路径 40 | * @param fileName 文件名 41 | */ 42 | public static List fromIncFile(String fileSrc, String fileName) { 43 | List list = new ArrayList<>(); 44 | try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileSrc + File.separator + fileName))) { 45 | T obj = null; 46 | while ((obj = (T) ois.readObject()) != null){ 47 | list.add(obj); 48 | } 49 | }catch (EOFException e){ 50 | //文件读取结束,不需要处理 51 | }catch (IOException | ClassNotFoundException ex) { 52 | ex.printStackTrace(); 53 | } 54 | return list; 55 | } 56 | 57 | /** 58 | * 获取指定文件夹下的所有文件信息 59 | * @param fileSrc 文件夹路径 60 | */ 61 | public static List getScheduledLogFiles(String fileSrc) { 62 | File file = new File(fileSrc); 63 | if (file.isDirectory()) { 64 | // 获取路径下的所有文件 65 | File[] files = file.listFiles(); 66 | List logFiles = new ArrayList<>(); 67 | for (int i = 0; i < files.length; i++) { 68 | // 如果还是文件夹 递归获取里面的文件 文件夹 69 | if (!files[i].isDirectory()) { 70 | ScheduledLogFile scheduledLogFile = new ScheduledLogFile(files[i]); 71 | logFiles.add(scheduledLogFile); 72 | } 73 | } 74 | return logFiles; 75 | } else { 76 | throw new SuperScheduledException("路径:"+fileSrc+"不是文件夹"); 77 | } 78 | } 79 | 80 | /** 81 | * 判断文件夹是否存在,如果不存在就创建 82 | * @param fileSrc 文件夹路径 83 | */ 84 | private static String existFile(String fileSrc) { 85 | if (StringUtils.isEmpty(fileSrc)) { 86 | fileSrc = ""; 87 | } else { 88 | File file = new File(fileSrc); 89 | //如果文件夹不存在 90 | if (!file.exists()) { 91 | //创建文件夹 92 | file.mkdir(); 93 | } 94 | } 95 | return fileSrc; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/core/SuperScheduledPostProcessor.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.core; 2 | 3 | import com.gyx.superscheduled.exception.SuperScheduledException; 4 | import com.gyx.superscheduled.model.ScheduledSource; 5 | import org.apache.commons.logging.Log; 6 | import org.apache.commons.logging.LogFactory; 7 | import org.springframework.beans.BeansException; 8 | import org.springframework.beans.factory.config.BeanPostProcessor; 9 | import org.springframework.context.ApplicationContext; 10 | import org.springframework.context.ApplicationContextAware; 11 | import org.springframework.context.annotation.DependsOn; 12 | import org.springframework.core.annotation.Order; 13 | import org.springframework.scheduling.annotation.Scheduled; 14 | import org.springframework.stereotype.Component; 15 | 16 | import java.lang.reflect.Method; 17 | 18 | import static com.gyx.superscheduled.common.utils.AnnotationUtils.changeAnnotationValue; 19 | 20 | @DependsOn({"superScheduledConfig"}) 21 | @Component 22 | @Order 23 | public class SuperScheduledPostProcessor implements BeanPostProcessor, ApplicationContextAware { 24 | protected final Log logger = LogFactory.getLog(getClass()); 25 | 26 | private ApplicationContext applicationContext; 27 | 28 | /** 29 | * 实例化bean之前的操作 30 | * @param bean bean实例 31 | * @param beanName bean的Name 32 | */ 33 | @Override 34 | public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 35 | return bean; 36 | } 37 | 38 | /** 39 | * 实例化bean之后的操作 40 | * @param bean bean实例 41 | * @param beanName bean的Name 42 | */ 43 | @Override 44 | public Object postProcessAfterInitialization(Object bean, 45 | String beanName) throws BeansException { 46 | SuperScheduledConfig superScheduledConfig = applicationContext.getBean(SuperScheduledConfig.class); 47 | 48 | //获取bean的方法 49 | Method[] methods = bean.getClass().getDeclaredMethods(); 50 | if (methods.length > 0) { 51 | for (Method method : methods) { 52 | Scheduled annotation = method.getAnnotation(Scheduled.class); 53 | if (annotation == null) { 54 | continue; 55 | } 56 | ScheduledSource scheduledSource = new ScheduledSource(annotation, method, bean); 57 | if (!scheduledSource.check()) { 58 | throw new SuperScheduledException("在" + beanName + "Bean中" + method.getName() + "方法的注解参数错误"); 59 | } 60 | String name = beanName + "." + method.getName(); 61 | superScheduledConfig.addScheduledSource(name, scheduledSource); 62 | try { 63 | clearOriginalScheduled(annotation); 64 | } catch (Exception e) { 65 | throw new SuperScheduledException("在关闭原始方法" + beanName + method.getName() + "时出现错误"); 66 | } 67 | } 68 | } 69 | return bean; 70 | } 71 | 72 | /** 73 | * 修改注解原先的属性 74 | * @param annotation 注解实例对象 75 | * @throws Exception 76 | */ 77 | private void clearOriginalScheduled(Scheduled annotation) throws Exception { 78 | changeAnnotationValue(annotation, "cron", Scheduled.CRON_DISABLED); 79 | changeAnnotationValue(annotation, "fixedDelay", -1L); 80 | changeAnnotationValue(annotation, "fixedDelayString", ""); 81 | changeAnnotationValue(annotation, "fixedRate", -1L); 82 | changeAnnotationValue(annotation, "fixedRateString", ""); 83 | changeAnnotationValue(annotation, "initialDelay", -1L); 84 | changeAnnotationValue(annotation, "initialDelayString", ""); 85 | } 86 | 87 | 88 | /** 89 | * 获取SpringBoot的上下文 90 | * @param applicationContext SpringBoot的上下文 91 | */ 92 | @Override 93 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 94 | this.applicationContext = applicationContext; 95 | } 96 | } -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/core/SuperScheduledConfig.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.core; 2 | 3 | import com.gyx.superscheduled.core.RunnableInterceptor.SuperScheduledRunnable; 4 | import com.gyx.superscheduled.model.ScheduledSource; 5 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.Map; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | import java.util.concurrent.ScheduledFuture; 11 | 12 | @Component("superScheduledConfig") 13 | public class SuperScheduledConfig { 14 | /** 15 | * 执行定时任务的线程池 16 | */ 17 | private ThreadPoolTaskScheduler taskScheduler; 18 | 19 | /** 20 | * 定时任务名称与定时任务回调钩子 的关联关系容器 21 | */ 22 | private Map nameToScheduledFuture = new ConcurrentHashMap<>(); 23 | 24 | /** 25 | * 定时任务名称与定时任务需要执行的逻辑 的关联关系容器 26 | */ 27 | private Map nameToRunnable = new ConcurrentHashMap<>(); 28 | 29 | /** 30 | * 定时任务名称与任务执行器 的关联关系容器 31 | */ 32 | private Map nameToSuperScheduledRunnable = new ConcurrentHashMap<>(); 33 | 34 | /** 35 | * 定时任务名称与定时任务的源信息 的关联关系容器 36 | */ 37 | private Map nameToScheduledSource = new ConcurrentHashMap<>(); 38 | 39 | 40 | public void addScheduledSource(String name, ScheduledSource scheduledSource) { 41 | this.nameToScheduledSource.put(name, scheduledSource); 42 | } 43 | 44 | public ScheduledSource getScheduledSource(String name) { 45 | return nameToScheduledSource.get(name); 46 | } 47 | 48 | public void addSuperScheduledRunnable(String name, SuperScheduledRunnable superScheduledRunnable) { 49 | this.nameToSuperScheduledRunnable.put(name, superScheduledRunnable); 50 | } 51 | 52 | public SuperScheduledRunnable getSuperScheduledRunnable(String name) { 53 | return nameToSuperScheduledRunnable.get(name); 54 | } 55 | 56 | public Runnable getRunnable(String name) { 57 | return nameToRunnable.get(name); 58 | } 59 | 60 | public ScheduledFuture getScheduledFuture(String name) { 61 | return nameToScheduledFuture.get(name); 62 | } 63 | 64 | public void addScheduledFuture(String name, ScheduledFuture scheduledFuture) { 65 | this.nameToScheduledFuture.put(name, scheduledFuture); 66 | } 67 | 68 | public void addRunnable(String name, Runnable runnable) { 69 | this.nameToRunnable.put(name, runnable); 70 | } 71 | 72 | public ThreadPoolTaskScheduler getTaskScheduler() { 73 | return taskScheduler; 74 | } 75 | 76 | public void setTaskScheduler(ThreadPoolTaskScheduler taskScheduler) { 77 | this.taskScheduler = taskScheduler; 78 | } 79 | 80 | public Map getNameToScheduledFuture() { 81 | return nameToScheduledFuture; 82 | } 83 | 84 | public void setNameToScheduledFuture(Map nameToScheduledFuture) { 85 | this.nameToScheduledFuture = nameToScheduledFuture; 86 | } 87 | 88 | public Map getNameToRunnable() { 89 | return nameToRunnable; 90 | } 91 | 92 | public void setNameToRunnable(Map nameToRunnable) { 93 | this.nameToRunnable = nameToRunnable; 94 | } 95 | 96 | public void removeScheduledFuture(String name) { 97 | nameToScheduledFuture.remove(name); 98 | } 99 | 100 | public Map getNameToScheduledSource() { 101 | return nameToScheduledSource; 102 | } 103 | 104 | public void setNameToScheduledSource(Map nameToScheduledSource) { 105 | this.nameToScheduledSource = nameToScheduledSource; 106 | } 107 | 108 | public Map getNameToSuperScheduledRunnable() { 109 | return nameToSuperScheduledRunnable; 110 | } 111 | 112 | public void setNameToSuperScheduledRunnable(Map nameToSuperScheduledRunnable) { 113 | this.nameToSuperScheduledRunnable = nameToSuperScheduledRunnable; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/SuperScheduledAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled; 2 | 3 | import com.gyx.superscheduled.common.constant.ZooKeeperConstant; 4 | import com.gyx.superscheduled.core.RunnableInterceptor.strengthen.ColonyStrengthen; 5 | import com.gyx.superscheduled.core.RunnableInterceptor.strengthen.ExecutionFlagStrengthen; 6 | import com.gyx.superscheduled.core.RunnableInterceptor.strengthen.LogStrengthen; 7 | import com.gyx.superscheduled.properties.PlugInProperties; 8 | import com.gyx.superscheduled.properties.ThreadPoolTaskSchedulerProperties; 9 | import com.gyx.superscheduled.properties.ZooKeeperProperties; 10 | import org.I0Itec.zkclient.ZkClient; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 13 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 14 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 15 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 16 | import org.springframework.context.annotation.Bean; 17 | import org.springframework.context.annotation.ComponentScan; 18 | import org.springframework.context.annotation.Configuration; 19 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 20 | 21 | import java.io.IOException; 22 | import java.net.InetAddress; 23 | 24 | @Configuration 25 | @ComponentScan("com.gyx.superscheduled") 26 | @EnableConfigurationProperties(value = {ThreadPoolTaskSchedulerProperties.class, PlugInProperties.class, ZooKeeperProperties.class}) 27 | public class SuperScheduledAutoConfiguration { 28 | @Autowired 29 | private ThreadPoolTaskSchedulerProperties threadPoolTaskSchedulerProperties; 30 | @Autowired 31 | private PlugInProperties plugInProperties; 32 | @Autowired 33 | private ZooKeeperProperties zooKeeperProperties; 34 | 35 | @Bean("threadPoolTaskScheduler") 36 | @ConditionalOnMissingBean 37 | public ThreadPoolTaskScheduler threadPoolTaskScheduler() { 38 | ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); 39 | taskScheduler.setPoolSize(threadPoolTaskSchedulerProperties.getPoolSize()); 40 | taskScheduler.setThreadNamePrefix(threadPoolTaskSchedulerProperties.getThreadNamePrefix()); 41 | taskScheduler.setWaitForTasksToCompleteOnShutdown(threadPoolTaskSchedulerProperties.getWaitForTasksToCompleteOnShutdown()); 42 | taskScheduler.setAwaitTerminationSeconds(threadPoolTaskSchedulerProperties.getAwaitTerminationSeconds()); 43 | taskScheduler.initialize(); 44 | return taskScheduler; 45 | } 46 | 47 | @Bean 48 | @ConditionalOnProperty(prefix = "spring.super.scheduled.plug-in", name = "executionFlag", havingValue = "true") 49 | @ConditionalOnMissingBean 50 | public ExecutionFlagStrengthen executionFlagStrengthen() { 51 | return new ExecutionFlagStrengthen(); 52 | } 53 | 54 | @Bean 55 | @ConditionalOnProperty(prefix = "spring.super.scheduled.plug-in", name = "executionLog", havingValue = "true") 56 | @ConditionalOnMissingBean 57 | public LogStrengthen logStrengthen() { 58 | return new LogStrengthen(plugInProperties.getLogPath()); 59 | } 60 | 61 | @Bean 62 | @ConditionalOnProperty(prefix = "spring.super.scheduled.plug-in", name = "colony", havingValue = "true") 63 | @ConditionalOnMissingBean 64 | public ColonyStrengthen colonyStrengthen() { 65 | return new ColonyStrengthen(); 66 | } 67 | 68 | /** 69 | * 获取zk连接 70 | * 71 | * @return 72 | */ 73 | @Bean 74 | @ConditionalOnMissingBean 75 | @ConditionalOnBean(ColonyStrengthen.class) 76 | @ConditionalOnProperty(prefix = "spring.super.scheduled.zookeeper", name = "url") 77 | public ZkClient zooKeeper() { 78 | ZkClient zk = null; 79 | try { 80 | zk = new ZkClient(zooKeeperProperties.getUrl() 81 | , zooKeeperProperties.getSessionTimeout() 82 | , zooKeeperProperties.getConnectionTimeout()); 83 | if (!zk.exists(ZooKeeperConstant.ROOT_PATH)) { 84 | zk.createPersistent(ZooKeeperConstant.ROOT_PATH); 85 | } 86 | if (!zk.exists(ZooKeeperConstant.ROOT_PATH+"/"+plugInProperties.getColonyName())) { 87 | zk.createPersistent(ZooKeeperConstant.ROOT_PATH+"/"+plugInProperties.getColonyName()); 88 | } 89 | 90 | String zkPath = zk.createEphemeralSequential(ZooKeeperConstant.ROOT_PATH +"/"+plugInProperties.getColonyName()+ ZooKeeperConstant.COLONY_STRENGTHEN 91 | , InetAddress.getLocalHost().getHostAddress()); 92 | zooKeeperProperties.setZkPath(zkPath); 93 | zooKeeperProperties.setZkParentNodePath(ZooKeeperConstant.ROOT_PATH+"/"+plugInProperties.getColonyName()); 94 | } catch (IOException e) { 95 | e.printStackTrace(); 96 | } 97 | return zk; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/core/SuperScheduledApplicationRunner.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.core; 2 | 3 | import com.gyx.superscheduled.common.annotation.SuperScheduledInteriorOrder; 4 | import com.gyx.superscheduled.common.annotation.SuperScheduledOrder; 5 | import com.gyx.superscheduled.common.utils.AnnotationUtils; 6 | import com.gyx.superscheduled.common.utils.proxy.Chain; 7 | import com.gyx.superscheduled.common.utils.proxy.Point; 8 | import com.gyx.superscheduled.common.utils.proxy.ProxyUtils; 9 | import com.gyx.superscheduled.core.RunnableInterceptor.RunnableBaseInterceptor; 10 | import com.gyx.superscheduled.core.RunnableInterceptor.SuperScheduledRunnable; 11 | import com.gyx.superscheduled.core.RunnableInterceptor.strengthen.BaseStrengthen; 12 | import com.gyx.superscheduled.exception.SuperScheduledException; 13 | import com.gyx.superscheduled.model.ScheduledSource; 14 | import org.apache.commons.logging.Log; 15 | import org.apache.commons.logging.LogFactory; 16 | import org.springframework.beans.BeansException; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.boot.ApplicationArguments; 19 | import org.springframework.boot.ApplicationRunner; 20 | import org.springframework.context.ApplicationContext; 21 | import org.springframework.context.ApplicationContextAware; 22 | import org.springframework.context.annotation.DependsOn; 23 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 24 | import org.springframework.stereotype.Component; 25 | 26 | import java.time.LocalDateTime; 27 | import java.time.format.DateTimeFormatter; 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | import java.util.Map; 31 | import java.util.concurrent.ScheduledFuture; 32 | 33 | @DependsOn("threadPoolTaskScheduler") 34 | @Component 35 | public class SuperScheduledApplicationRunner implements ApplicationRunner, ApplicationContextAware { 36 | protected final Log logger = LogFactory.getLog(getClass()); 37 | private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 38 | private ApplicationContext applicationContext; 39 | 40 | /** 41 | * 定时任务配置管理器 42 | */ 43 | @Autowired 44 | private SuperScheduledConfig superScheduledConfig; 45 | /** 46 | * 定时任务执行线程 47 | */ 48 | @Autowired 49 | private ThreadPoolTaskScheduler threadPoolTaskScheduler; 50 | 51 | @Override 52 | public void run(ApplicationArguments args) { 53 | superScheduledConfig.setTaskScheduler(threadPoolTaskScheduler); 54 | Map nameToScheduledSource = superScheduledConfig.getNameToScheduledSource(); 55 | for (String name : nameToScheduledSource.keySet()) { 56 | //获取定时任务源数据 57 | ScheduledSource scheduledSource = nameToScheduledSource.get(name); 58 | //获取所有增强类 59 | String[] baseStrengthenBeanNames = applicationContext.getBeanNamesForType(BaseStrengthen.class); 60 | //创建执行控制器 61 | SuperScheduledRunnable runnable = new SuperScheduledRunnable(); 62 | runnable.setMethod(scheduledSource.getMethod()); 63 | runnable.setBean(scheduledSource.getBean()); 64 | //将增强器代理成point 65 | List points = new ArrayList<>(baseStrengthenBeanNames.length); 66 | for (String baseStrengthenBeanName : baseStrengthenBeanNames) { 67 | Object baseStrengthenBean = applicationContext.getBean(baseStrengthenBeanName); 68 | //获取秩序顺序 69 | SuperScheduledOrder orderAnnotation = baseStrengthenBean.getClass().getAnnotation(SuperScheduledOrder.class); 70 | SuperScheduledInteriorOrder interiorOrderAnnotation = baseStrengthenBean.getClass().getAnnotation(SuperScheduledInteriorOrder.class); 71 | //创建代理 72 | Point proxy = ProxyUtils.getInstance(Point.class, new RunnableBaseInterceptor(baseStrengthenBean, runnable)); 73 | proxy.setOrder(orderAnnotation == null ? 0 : orderAnnotation.value()); 74 | proxy.setInteriorOrder(interiorOrderAnnotation == null ? null : interiorOrderAnnotation.value()); 75 | proxy.setSuperScheduledName(name); 76 | proxy.setScheduledSource(scheduledSource); 77 | //所有的points连接起来 78 | points.add(proxy); 79 | } 80 | //按照执行顺序排序 81 | AnnotationUtils.superScheduledOrderSort(points); 82 | runnable.setChain(new Chain(points)); 83 | //添加缓存中 84 | superScheduledConfig.addRunnable(name, runnable::invoke); 85 | //执行方法 86 | try { 87 | ScheduledFuture schedule = ScheduledFutureFactory.create(threadPoolTaskScheduler 88 | , scheduledSource, runnable::invoke); 89 | superScheduledConfig.addScheduledFuture(name, schedule); 90 | logger.info(df.format(LocalDateTime.now()) + "任务" + name + "已经启动..."); 91 | 92 | } catch (Exception e) { 93 | throw new SuperScheduledException("任务" + name + "启动失败,错误信息:" + e.getLocalizedMessage()); 94 | } 95 | } 96 | } 97 | 98 | @Override 99 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 100 | this.applicationContext = applicationContext; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starters 9 | 2.1.4.RELEASE 10 | 11 | com.github.guoyixing 12 | spring-boot-starter-super-scheduled 13 | 0.3.4 14 | 15 | jar 16 | spring-boot-starter-super-scheduled 17 | https://github.com/guoyixing/super-scheduled 18 | 19 | 20 | 1.8 21 | 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter 27 | 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-web 33 | 34 | 35 | 36 | com.101tec 37 | zkclient 38 | 0.7 39 | 40 | 41 | 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-maven-plugin 47 | 48 | 49 | org.apache.maven.plugins 50 | maven-compiler-plugin 51 | 3.1 52 | 53 | 1.8 54 | 1.8 55 | UTF-8 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | The Apache Software License, Version 2.0 65 | http://www.apache.org/licenses/LICENSE-2.0.txt 66 | 67 | 68 | 69 | 70 | 71 | scm:git:git://github.com/guoyixing/super-scheduled.git 72 | https://github.com/guoyixing/super-scheduled/tree/master 73 | 74 | 75 | 76 | 77 | GuoYixing 78 | 904985041@qq.com 79 | https://github.com/guoyixing 80 | 81 | 82 | 83 | 84 | 85 | snapshots 86 | 87 | true 88 | 89 | 90 | 91 | 92 | org.apache.maven.plugins 93 | maven-source-plugin 94 | 2.2.1 95 | 96 | 97 | package 98 | 99 | jar-no-fork 100 | 101 | 102 | 103 | 104 | 105 | org.apache.maven.plugins 106 | maven-javadoc-plugin 107 | 2.9.1 108 | 109 | 110 | package 111 | 112 | jar 113 | 114 | 115 | 116 | 117 | 118 | org.apache.maven.plugins 119 | maven-gpg-plugin 120 | 1.6 121 | 122 | 123 | verify 124 | 125 | sign 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | snapshots 135 | https://oss.sonatype.org/content/repositories/snapshots/ 136 | 137 | 138 | snapshots 139 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 140 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/model/ScheduledSource.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.model; 2 | 3 | import com.gyx.superscheduled.enums.ScheduledType; 4 | import com.gyx.superscheduled.exception.SuperScheduledException; 5 | import org.springframework.scheduling.annotation.Scheduled; 6 | import org.springframework.util.StringUtils; 7 | 8 | import java.io.Serializable; 9 | import java.lang.reflect.Method; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | public class ScheduledSource implements Serializable { 14 | /** 15 | * cron表达式 16 | */ 17 | private String cron; 18 | /** 19 | * 时区,cron表达式会基于该时区解析 20 | */ 21 | private String zone; 22 | /** 23 | * 上一次执行完毕时间点之后多长时间再执行 24 | */ 25 | private Long fixedDelay; 26 | /** 27 | * 与 fixedDelay 意思相同,只是使用字符串的形式 28 | */ 29 | private String fixedDelayString; 30 | /** 31 | * 上一次开始执行时间点之后多长时间再执行 32 | */ 33 | private Long fixedRate; 34 | /** 35 | * 与 fixedRate 意思相同,只是使用字符串的形式 36 | */ 37 | private String fixedRateString; 38 | /** 39 | * 第一次延迟多长时间后再执行 40 | */ 41 | private Long initialDelay; 42 | /** 43 | * 与 initialDelay 意思相同,只是使用字符串的形式 44 | */ 45 | private String initialDelayString; 46 | 47 | private transient Method method; 48 | 49 | private transient Object bean; 50 | 51 | private ScheduledType type; 52 | 53 | 54 | public Boolean check() { 55 | String sb = "1" 56 | + (cron == null ? 0 : 1) 57 | + (fixedDelay == null ? 0 : 1) 58 | + (fixedDelayString == null ? 0 : 1) 59 | + (fixedRate == null ? 0 : 1) 60 | + (fixedRateString == null ? 0 : 1) 61 | + (initialDelay == null ? 0 : 1) 62 | + (initialDelayString == null ? 0 : 1); 63 | Integer flag = Integer.valueOf(sb, 2); 64 | List probability = Arrays.asList(132, 133, 134, 136, 137, 138, 144, 145, 146, 160, 161, 162, 192); 65 | return probability.contains(flag); 66 | } 67 | 68 | public ScheduledSource(Scheduled annotation, Method method, Object bean) { 69 | this.cron = StringUtils.isEmpty(annotation.cron()) ? null : annotation.cron(); 70 | this.fixedDelay = annotation.fixedDelay() < 0 ? null : annotation.fixedDelay(); 71 | this.fixedDelayString = StringUtils.isEmpty(annotation.fixedDelayString()) ? null : annotation.fixedDelayString(); 72 | this.fixedRate = annotation.fixedRate() < 0 ? null : annotation.fixedRate(); 73 | this.fixedRateString = StringUtils.isEmpty(annotation.fixedRateString()) ? null : annotation.fixedRateString(); 74 | this.initialDelay = annotation.initialDelay() < 0 ? null : annotation.initialDelay(); 75 | this.initialDelayString = StringUtils.isEmpty(annotation.initialDelayString()) ? null : annotation.initialDelayString(); 76 | this.type = confirmType(); 77 | this.bean = bean; 78 | this.method = method; 79 | } 80 | 81 | public ScheduledType confirmType() { 82 | if (cron != null) { 83 | return ScheduledType.CRON; 84 | } else if (fixedDelay != null || fixedDelayString != null) { 85 | return ScheduledType.FIXED_DELAY; 86 | } else if (fixedRate != null || fixedRateString != null) { 87 | return ScheduledType.FIXED_RATE; 88 | } 89 | return null; 90 | } 91 | 92 | public void refreshType() { 93 | this.type = confirmType(); 94 | if (this.type == null) { 95 | throw new SuperScheduledException("刷新type,执行失败,无法确定定时任务的类型"); 96 | } 97 | } 98 | 99 | public void clear() { 100 | this.cron = null; 101 | this.fixedDelay = null; 102 | this.fixedDelayString = null; 103 | this.fixedRate = null; 104 | this.fixedRateString = null; 105 | } 106 | 107 | public ScheduledSource() { 108 | } 109 | 110 | public String getCron() { 111 | return cron; 112 | } 113 | 114 | public void setCron(String cron) { 115 | this.cron = cron; 116 | refreshType(); 117 | } 118 | 119 | public String getZone() { 120 | return zone; 121 | } 122 | 123 | public void setZone(String zone) { 124 | this.zone = zone; 125 | } 126 | 127 | public Long getFixedDelay() { 128 | return fixedDelay; 129 | } 130 | 131 | public void setFixedDelay(Long fixedDelay) { 132 | this.fixedDelay = fixedDelay; 133 | } 134 | 135 | public String getFixedDelayString() { 136 | return fixedDelayString; 137 | } 138 | 139 | public void setFixedDelayString(String fixedDelayString) { 140 | this.fixedDelayString = fixedDelayString; 141 | } 142 | 143 | public Long getFixedRate() { 144 | return fixedRate; 145 | } 146 | 147 | public void setFixedRate(Long fixedRate) { 148 | this.fixedRate = fixedRate; 149 | } 150 | 151 | public String getFixedRateString() { 152 | return fixedRateString; 153 | } 154 | 155 | public void setFixedRateString(String fixedRateString) { 156 | this.fixedRateString = fixedRateString; 157 | } 158 | 159 | public Long getInitialDelay() { 160 | return initialDelay; 161 | } 162 | 163 | public void setInitialDelay(Long initialDelay) { 164 | this.initialDelay = initialDelay; 165 | } 166 | 167 | public String getInitialDelayString() { 168 | return initialDelayString; 169 | } 170 | 171 | public void setInitialDelayString(String initialDelayString) { 172 | this.initialDelayString = initialDelayString; 173 | } 174 | 175 | public ScheduledType getType() { 176 | return type; 177 | } 178 | 179 | public void setType(ScheduledType type) { 180 | this.type = type; 181 | } 182 | 183 | public Method getMethod() { 184 | return method; 185 | } 186 | 187 | public void setMethod(Method method) { 188 | this.method = method; 189 | } 190 | 191 | public Object getBean() { 192 | return bean; 193 | } 194 | 195 | public void setBean(Object bean) { 196 | this.bean = bean; 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 木兰宽松许可证, 第2版 2 | 3 | 木兰宽松许可证, 第2版 4 | 2020年1月 http://license.coscl.org.cn/MulanPSL2 5 | 6 | 7 | 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: 8 | 9 | 0. 定义 10 | 11 | “软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 12 | 13 | “贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 14 | 15 | “贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 16 | 17 | “法人实体”是指提交贡献的机构及其“关联实体”。 18 | 19 | “关联实体”是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 20 | 21 | 1. 授予版权许可 22 | 23 | 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 24 | 25 | 2. 授予专利许可 26 | 27 | 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 28 | 29 | 3. 无商标许可 30 | 31 | “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 32 | 33 | 4. 分发限制 34 | 35 | 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 36 | 37 | 5. 免责声明与责任限制 38 | 39 | “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 40 | 41 | 6. 语言 42 | “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 43 | 44 | 条款结束 45 | 46 | 如何将木兰宽松许可证,第2版,应用到您的软件 47 | 48 | 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: 49 | 50 | 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; 51 | 52 | 2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; 53 | 54 | 3, 请将如下声明文本放入每个源文件的头部注释中。 55 | 56 | Copyright (c) [Year] [name of copyright holder] 57 | [Software Name] is licensed under Mulan PSL v2. 58 | You can use this software according to the terms and conditions of the Mulan PSL v2. 59 | You may obtain a copy of Mulan PSL v2 at: 60 | http://license.coscl.org.cn/MulanPSL2 61 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 62 | See the Mulan PSL v2 for more details. 63 | 64 | 65 | Mulan Permissive Software License,Version 2 66 | 67 | Mulan Permissive Software License,Version 2 (Mulan PSL v2) 68 | January 2020 http://license.coscl.org.cn/MulanPSL2 69 | 70 | Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: 71 | 72 | 0. Definition 73 | 74 | Software means the program and related documents which are licensed under this License and comprise all Contribution(s). 75 | 76 | Contribution means the copyrightable work licensed by a particular Contributor under this License. 77 | 78 | Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. 79 | 80 | Legal Entity means the entity making a Contribution and all its Affiliates. 81 | 82 | Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. 83 | 84 | 1. Grant of Copyright License 85 | 86 | Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. 87 | 88 | 2. Grant of Patent License 89 | 90 | Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. 91 | 92 | 3. No Trademark License 93 | 94 | No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4. 95 | 96 | 4. Distribution Restriction 97 | 98 | You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. 99 | 100 | 5. Disclaimer of Warranty and Limitation of Liability 101 | 102 | THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 103 | 104 | 6. Language 105 | 106 | THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. 107 | 108 | END OF THE TERMS AND CONDITIONS 109 | 110 | How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software 111 | 112 | To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: 113 | 114 | i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; 115 | 116 | ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; 117 | 118 | iii Attach the statement to the appropriate annotated syntax at the beginning of each source file. 119 | 120 | 121 | Copyright (c) [Year] [name of copyright holder] 122 | [Software Name] is licensed under Mulan PSL v2. 123 | You can use this software according to the terms and conditions of the Mulan PSL v2. 124 | You may obtain a copy of Mulan PSL v2 at: 125 | http://license.coscl.org.cn/MulanPSL2 126 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 127 | See the Mulan PSL v2 for more details. 128 | -------------------------------------------------------------------------------- /src/main/java/com/gyx/superscheduled/core/SuperScheduledManager.java: -------------------------------------------------------------------------------- 1 | package com.gyx.superscheduled.core; 2 | 3 | import com.gyx.superscheduled.common.utils.SerializableUtils; 4 | import com.gyx.superscheduled.core.RunnableInterceptor.SuperScheduledRunnable; 5 | import com.gyx.superscheduled.exception.SuperScheduledException; 6 | import com.gyx.superscheduled.model.ScheduledLog; 7 | import com.gyx.superscheduled.model.ScheduledLogFile; 8 | import com.gyx.superscheduled.model.ScheduledSource; 9 | import com.gyx.superscheduled.properties.PlugInProperties; 10 | import org.apache.commons.logging.Log; 11 | import org.apache.commons.logging.LogFactory; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 14 | import org.springframework.stereotype.Component; 15 | 16 | import java.time.LocalDateTime; 17 | import java.time.format.DateTimeFormatter; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.Set; 21 | import java.util.concurrent.ScheduledFuture; 22 | 23 | @Component 24 | public class SuperScheduledManager { 25 | protected final Log logger = LogFactory.getLog(getClass()); 26 | private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 27 | 28 | @Autowired 29 | private SuperScheduledConfig superScheduledConfig; 30 | @Autowired 31 | private PlugInProperties plugInProperties; 32 | 33 | /** 34 | * 修改Scheduled的执行周期 35 | * 36 | * @param name scheduled的名称 37 | * @param cron cron表达式 38 | */ 39 | public void setScheduledCron(String name, String cron) { 40 | //终止原先的任务 41 | cancelScheduled(name); 42 | //创建新的任务 43 | ScheduledSource scheduledSource = superScheduledConfig.getScheduledSource(name); 44 | scheduledSource.clear(); 45 | scheduledSource.setCron(cron); 46 | addScheduled(name, scheduledSource); 47 | } 48 | 49 | /** 50 | * 修改Scheduled的fixedDelay 51 | * 52 | * @param name scheduled的名称 53 | * @param fixedDelay 上一次执行完毕时间点之后多长时间再执行 54 | */ 55 | public void setScheduledFixedDelay(String name, Long fixedDelay) { 56 | //终止原先的任务 57 | cancelScheduled(name); 58 | //创建新的任务 59 | ScheduledSource scheduledSource = superScheduledConfig.getScheduledSource(name); 60 | scheduledSource.clear(); 61 | scheduledSource.setFixedDelay(fixedDelay); 62 | addScheduled(name, scheduledSource); 63 | } 64 | 65 | /** 66 | * 修改Scheduled的fixedRate 67 | * 68 | * @param name scheduled的名称 69 | * @param fixedRate 上一次开始执行之后多长时间再执行 70 | */ 71 | public void setScheduledFixedRate(String name, Long fixedRate) { 72 | //终止原先的任务 73 | cancelScheduled(name); 74 | //创建新的任务 75 | ScheduledSource scheduledSource = superScheduledConfig.getScheduledSource(name); 76 | scheduledSource.clear(); 77 | scheduledSource.setFixedRate(fixedRate); 78 | addScheduled(name, scheduledSource); 79 | } 80 | 81 | /** 82 | * 查询所有启动的Scheduled 83 | */ 84 | public List getRunScheduledName() { 85 | Set names = superScheduledConfig.getNameToScheduledFuture().keySet(); 86 | return new ArrayList<>(names); 87 | } 88 | 89 | /** 90 | * 查询所有的Scheduled 91 | */ 92 | public List getAllSuperScheduledName() { 93 | Set names = superScheduledConfig.getNameToRunnable().keySet(); 94 | return new ArrayList<>(names); 95 | } 96 | 97 | /** 98 | * 终止Scheduled 99 | * 100 | * @param name scheduled的名称 101 | */ 102 | public void cancelScheduled(String name) { 103 | ScheduledFuture scheduledFuture = superScheduledConfig.getScheduledFuture(name); 104 | scheduledFuture.cancel(true); 105 | superScheduledConfig.removeScheduledFuture(name); 106 | logger.info(df.format(LocalDateTime.now()) + "任务" + name + "已经终止..."); 107 | } 108 | 109 | /** 110 | * 启动Scheduled 111 | * 112 | * @param name scheduled的名称 113 | * @param scheduledSource 定时任务的源信息 114 | */ 115 | public void addScheduled(String name, ScheduledSource scheduledSource) { 116 | if (getRunScheduledName().contains(name)) { 117 | throw new SuperScheduledException("定时任务" + name + "已经被启动过了"); 118 | } 119 | if (!scheduledSource.check()) { 120 | throw new SuperScheduledException("定时任务" + name + "源数据内容错误"); 121 | } 122 | 123 | scheduledSource.refreshType(); 124 | 125 | Runnable runnable = superScheduledConfig.getRunnable(name); 126 | ThreadPoolTaskScheduler taskScheduler = superScheduledConfig.getTaskScheduler(); 127 | 128 | 129 | ScheduledFuture schedule = ScheduledFutureFactory.create(taskScheduler, scheduledSource, runnable); 130 | logger.info(df.format(LocalDateTime.now()) + "任务" + name + "已经启动..."); 131 | 132 | superScheduledConfig.addScheduledSource(name, scheduledSource); 133 | superScheduledConfig.addScheduledFuture(name, schedule); 134 | } 135 | 136 | /** 137 | * 以cron类型启动Scheduled 138 | * 139 | * @param name scheduled的名称 140 | * @param cron cron表达式 141 | */ 142 | public void addCronScheduled(String name, String cron) { 143 | ScheduledSource scheduledSource = new ScheduledSource(); 144 | scheduledSource.setCron(cron); 145 | 146 | addScheduled(name, scheduledSource); 147 | } 148 | 149 | /** 150 | * 以fixedDelay类型启动Scheduled 151 | * 152 | * @param name scheduled的名称 153 | * @param fixedDelay 上一次执行完毕时间点之后多长时间再执行 154 | * @param initialDelay 第一次执行的延迟时间 155 | */ 156 | public void addFixedDelayScheduled(String name, Long fixedDelay, Long... initialDelay) { 157 | ScheduledSource scheduledSource = new ScheduledSource(); 158 | scheduledSource.setFixedDelay(fixedDelay); 159 | if (initialDelay != null && initialDelay.length == 1) { 160 | scheduledSource.setInitialDelay(initialDelay[0]); 161 | } else if (initialDelay != null && initialDelay.length > 1) { 162 | throw new SuperScheduledException("第一次执行的延迟时间只能传入一个参数"); 163 | } 164 | 165 | addScheduled(name, scheduledSource); 166 | } 167 | 168 | /** 169 | * 以fixedRate类型启动Scheduled 170 | * 171 | * @param name scheduled的名称 172 | * @param fixedRate 上一次开始执行之后多长时间再执行 173 | * @param initialDelay 第一次执行的延迟时间 174 | */ 175 | public void addFixedRateScheduled(String name, Long fixedRate, Long... initialDelay) { 176 | ScheduledSource scheduledSource = new ScheduledSource(); 177 | scheduledSource.setFixedRate(fixedRate); 178 | if (initialDelay != null && initialDelay.length == 1) { 179 | scheduledSource.setInitialDelay(initialDelay[0]); 180 | } else if (initialDelay != null && initialDelay.length > 1) { 181 | throw new SuperScheduledException("第一次执行的延迟时间只能传入一个参数"); 182 | } 183 | 184 | addScheduled(name, scheduledSource); 185 | } 186 | 187 | /** 188 | * 手动执行一次任务 189 | * 190 | * @param name scheduled的名称 191 | */ 192 | public void runScheduled(String name) { 193 | Runnable runnable = superScheduledConfig.getRunnable(name); 194 | runnable.run(); 195 | } 196 | 197 | /** 198 | * 结束正在执行中的任务,跳过这次运行 199 | * 只有在每个前置增强器结束之后才会判断是否需要跳过此次运行 200 | * 201 | * @param name scheduled的名称 202 | */ 203 | public void callOffScheduled(String name) { 204 | SuperScheduledRunnable superScheduledRunnable = superScheduledConfig.getSuperScheduledRunnable(name); 205 | superScheduledRunnable.getContext().setCallOff(true); 206 | } 207 | 208 | /** 209 | * 获取日志信息 210 | * 211 | * @param fileName 日志文件名 212 | */ 213 | public List getScheduledLogs(String fileName) { 214 | String logPath = plugInProperties.getLogPath(); 215 | List scheduledLogs = SerializableUtils.fromIncFile(logPath, fileName); 216 | return scheduledLogs; 217 | } 218 | 219 | /** 220 | * 获取日志文件信息 221 | */ 222 | public List getScheduledLogFiles() { 223 | String logPath = plugInProperties.getLogPath(); 224 | return SerializableUtils.getScheduledLogFiles(logPath); 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpringBoot的定时任务动态管理 2 | SpringBoot的定时任务的加强工具,实现定时任务动态管理,完全兼容原生@Scheduled注解,无需对原本的定时任务进行修改 3 | 4 | ## 引入jar包 5 | ```xml 6 | 7 | com.github.guoyixing 8 | spring-boot-starter-super-scheduled 9 | 0.3.4 10 | 11 | ``` 12 | ## 原理说明 13 | [https://keyboard-dog.blog.csdn.net/article/details/106494637](https://keyboard-dog.blog.csdn.net/article/details/106494637) 14 | 15 | ## 配置参数 16 | ### 1.配置定时任务线程池(不配置时采用默认参数) 17 | ```properties 18 | #线程池大小 19 | spring.super.scheduled.thread-pool.poolSize=30 20 | #线程名前缀 21 | spring.super.scheduled.thread-pool.threadNamePrefix=super 22 | #设置是否关闭时等待执行中的任务执行完成 23 | spring.super.scheduled.thread-pool.waitForTasksToCompleteOnShutdown=false 24 | #设置此执行器被关闭时等待的最长时间,用于在其余容器继续关闭之前等待剩余任务执行完成 25 | #需要将waitForTasksToCompleteOnShutdown设置为true,此配置才起作用 26 | spring.super.scheduled.thread-pool.awaitTerminationSeconds=0 27 | ``` 28 | ### 2.扩展插件配置 29 | ```properties 30 | #开启执行标志 31 | spring.super.scheduled.plug-in.executionFlag=true 32 | #开启定时任务调度日志,日志文件是存在本地磁盘上的 33 | spring.super.scheduled.plug-in.executionLog=true 34 | #日志存放位置,不设置默认位置为程序同级目录下 35 | spring.super.scheduled.plug-in.logPath=H:/tmp/log-scheduled 36 | #开启基于zookeeper的集群模式 37 | spring.super.scheduled.plug-in.colony=true 38 | #zookeeper集群模式的定时任务服务名,相同名称的定时任务名称服务会被统一管理 39 | spring.super.scheduled.plug-in.colonyName=test 40 | ``` 41 | ### 3.zookeeper配置 42 | ```properties 43 | #设置zookeeper地址,zookeeper集群多个地址用英文逗号隔开 44 | spring.super.scheduled.zookeeper.url=127.0.0.1:2181 45 | #设置zookeeper session超时时间 46 | spring.super.scheduled.zookeeper.sessionTimeout=60000 47 | #设置zookeeper连接超时时间 48 | spring.super.scheduled.zookeeper.connectionTimeout=60000 49 | ``` 50 | 51 | ## 使用样例 52 | ### 1.正常使用springScheduled 53 | ```java 54 | @SpringBootApplication 55 | @EnableScheduling 56 | public class TestApplication { 57 | public static void main(String[] args) { 58 | SpringApplication.run(TestApplication.class, args); 59 | } 60 | } 61 | ``` 62 | ```java 63 | @Component 64 | public class TestTask { 65 | private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 66 | 67 | @Scheduled(cron = "0/2 * * * * ?") 68 | public void robReceiveExpireTask() { 69 | System.out.println(df.format(LocalDateTime.now()) + "测试测试"); 70 | } 71 | } 72 | ``` 73 | ### 2.定时任务动态管理 74 | ```java 75 | @RunWith(SpringRunner.class) 76 | @SpringBootTest 77 | public class TestApplicationTests { 78 | //直接注入管理器 79 | @Autowired 80 | private SuperScheduledManager superScheduledManager; 81 | 82 | @Test 83 | public void test() { 84 | //获取所有定时任务 85 | List allSuperScheduledName = superScheduledManager.getAllSuperScheduledName(); 86 | String name = allSuperScheduledName.get(0); 87 | //终止定时任务 88 | superScheduledManager.cancelScheduled(name); 89 | 90 | try { 91 | Thread.sleep(5000); 92 | } catch (InterruptedException e) { 93 | e.printStackTrace(); 94 | } 95 | 96 | System.out.println("任务名:" + name); 97 | //启动定时任务 98 | superScheduledManager.addCronScheduled(name, "0/2 * * * * ?"); 99 | //获取启动汇总的定时任务 100 | List runScheduledName = superScheduledManager.getRunScheduledName(); 101 | runScheduledName.forEach(System.out::println); 102 | 103 | try { 104 | Thread.sleep(10000); 105 | } catch (InterruptedException e) { 106 | e.printStackTrace(); 107 | } 108 | //修改定时任务执行周期 109 | superScheduledManager.setScheduledCron(name, "0/5 * * * * ?"); 110 | } 111 | } 112 | ``` 113 | ### 3.管理器功能 114 | #### 3.1 获取所有定时任务 115 | ```java 116 | List allSuperScheduledName = superScheduledManager.getAllSuperScheduledName(); 117 | ``` 118 | #### 3.1 获取所有启动的定时任务 119 | ```java 120 | List runScheduledName = superScheduledManager.getRunScheduledName(); 121 | ``` 122 | #### 3.2 终止定时任务 123 | ```java 124 | superScheduledManager.cancelScheduled(name); 125 | ``` 126 | #### 3.3 cron类型操作 127 | ```java 128 | //以cron模式启动定时任务 129 | superScheduledManager.addCronScheduled(name, "0/2 * * * * ?"); 130 | //将定时任务转为cron模式运行,并修改cron的参数值 131 | superScheduledManager.setScheduledCron(name, "0/2 * * * * ?"); 132 | ``` 133 | #### 3.4 FixedRate类型操作 134 | ```java 135 | //以FixedRate模式启动定时任务 136 | //上一次开始执行之后2秒后再执行,首次运行延迟1秒 137 | superScheduledManager.addFixedRateScheduled(name, 2000L,1000L); 138 | //首次运行不进行延迟 139 | superScheduledManager.addFixedRateScheduled(name, 2000L); 140 | //将定时任务转为FixedRate模式运行,并修改执行间隔的参数值 141 | superScheduledManager.setScheduledFixedRate(name, 2000L); 142 | ``` 143 | #### 3.5 FixedDelay类型操作 144 | ```java 145 | //以FixedDelay模式启动定时任务 146 | //上一次执行完毕之后2秒后再执行,首次运行延迟1秒 147 | superScheduledManager.addFixedDelayScheduled(name, 2000L,1000L); 148 | //首次运行不进行延迟 149 | superScheduledManager.addFixedDelayScheduled(name, 2000L); 150 | //将定时任务转为FixedDelay模式运行,并修改执行间隔的参数值 151 | superScheduledManager.setScheduledFixedDelay(name, 2000L); 152 | ``` 153 | #### 3.6 手动执行一次 154 | ```java 155 | superScheduledManager.runScheduled(name); 156 | ``` 157 | #### 3.7 获取日志文件信息 158 | ```java 159 | superScheduledManager.getScheduledLogFiles(); 160 | ``` 161 | #### 3.8 获取日志信息 162 | ```java 163 | superScheduledManager.getScheduledLogs(fileName); 164 | ``` 165 | #### 3.9 结束正在执行中的任务,跳过这次运行 166 | **只有在每个前置增强器结束之后才会判断是否需要跳过此次运行** 167 | ```java 168 | superScheduledManager.callOffScheduled(name); 169 | ``` 170 | 171 | ### 4.Api接口 172 | #### 4.1 获取所有定时任务 173 | `GET` /scheduled/name/all 174 | #### 4.2 获取启动的定时任务 175 | `GET` /scheduled/name/run 176 | #### 4.3 手动执行一次任务 177 | `POST` /scheduled/{name}/run 178 | #### 4.4 终止定时任务 179 | `DELETE` /scheduled/{name} 180 | #### 4.5 cronApi 181 | ##### 4.5.1 以cron类型启动Scheduled 182 | `POST` /scheduled/cron/{name}/add
183 | 参数:`text` \[cron\] 184 | ##### 4.5.2 将定时任务转为cron模式运行,并修改cron的参数值 185 | `POST` /scheduled/cron/{name}/set
186 | 参数:`text` \[cron\] 187 | #### 4.6 fixedDelayApi 188 | ##### 4.6.1 以FixedDelay模式启动定时任务 189 | `POST` /scheduled/fixedDelay/{name}/add/{fixedDelay}/{initialDelay} 190 | ##### 4.6.2 以FixedDelay模式启动定时任务(不延迟) 191 | `POST` /scheduled/fixedDelay/{name}/add/{fixedDelay} 192 | ##### 4.6.3 将定时任务转为FixedDelay模式运行,并修改执行间隔的参数值 193 | `POST` /scheduled/fixedDelay/{name}/set/{fixedDelay} 194 | #### 4.7 fixedRateApi 195 | ##### 4.7.1 以FixedRate模式启动定时任务 196 | `POST` /scheduled/fixedRate/{name}/add/{fixedRate}/{initialDelay} 197 | ##### 4.7.2 以FixedRate模式启动定时任务(不延迟) 198 | `POST` /scheduled/fixedRate/{name}/add/{fixedRate} 199 | ##### 4.7.3 将定时任务转为FixedRate模式运行,并修改执行间隔的参数值 200 | `POST` /scheduled/fixedRate/{name}/set/{fixedRate} 201 | #### 4.8 获取日志文件信息 202 | `GET` /scheduled/log/files 203 | #### 4.9 获取日志信息 204 | `GET` /scheduled/log/{fileName} 205 | #### 4.10 结束正在执行中的任务,跳过这次运行 206 | **只有在每个前置增强器结束之后才会判断是否需要跳过此次运行** 207 | `POST` /scheduled/{name}/callOff 208 | 209 | ### 5.扩展接口 210 | #### 5.1 扩展样例 211 | 1. 将类注入到spring容器中 212 | 2. 实现BaseStrengthen接口 213 | 3. 使用@SuperScheduledOrder(int)注解实现执行优先级控制,int值越大优先级越高,默认值为0 214 | ```java 215 | @Component 216 | @SuperScheduledOrder(1) 217 | public class Strong implements BaseStrengthen { 218 | /** 219 | * 前置强化方法 220 | * 221 | * @param bean bean实例(或者是被代理的bean) 222 | * @param method 执行的方法对象 223 | * @param args 方法参数 224 | * @param context 任务线程运行时的上下文 225 | */ 226 | @Override 227 | public void before(Object bean, Method method, Object[] args, ScheduledRunningContext context) { 228 | System.out.println("定时任务执行前运行"); 229 | } 230 | 231 | /** 232 | * 后置强化方法 233 | * 234 | * @param bean bean实例(或者是被代理的bean) 235 | * @param method 执行的方法对象 236 | * @param args 方法参数 237 | * @param context 任务线程运行时的上下文 238 | */ 239 | @Override 240 | public void after(Object bean, Method method, Object[] args, ScheduledRunningContext context) { 241 | System.out.println("定时任务执行成功后运行"); 242 | } 243 | 244 | /** 245 | * 异常强化方法 246 | * 247 | * @param bean bean实例(或者是被代理的bean) 248 | * @param method 执行的方法对象 249 | * @param args 方法参数 250 | * @param context 任务线程运行时的上下文 251 | */ 252 | @Override 253 | public void exception(Object bean, Method method, Object[] args, ScheduledRunningContext context) { 254 | System.out.println("定时任务执行异常时运行"); 255 | } 256 | 257 | /** 258 | * Finally强化方法,出现异常也会执行 259 | * 260 | * @param bean bean实例(或者是被代理的bean) 261 | * @param method 执行的方法对象 262 | * @param args 方法参数 263 | * @param context 任务线程运行时的上下文 264 | */ 265 | @Override 266 | public void afterFinally(Object bean, Method method, Object[] args, ScheduledRunningContext context) { 267 | System.out.println("定时任务执行完成后运行(异常时也运行)"); 268 | } 269 | } 270 | ``` 271 | #### 5.2 更多样例 272 | 更多样例参考:
273 | 执行标记增强器:com.gyx.superscheduled.core.RunnableInterceptor.strengthen.ExecutionFlagStrengthen
274 | 执行日志增强器:com.gyx.superscheduled.core.RunnableInterceptor.strengthen.LogStrengthen
275 | 276 | ### 6.集群模式 277 | #### 6.1基于zookeeper的集群模式 278 | 部署多服务的时候,会限制定时任务的执行,防止同一个任务在多个服务上反复运行
279 | `目前不支持设置的指定的定时任务`
280 | `目前不支持统一的动态管理,只能单个逐一设置` 281 | ##### 6.1.1开启zk集群模式 282 | ``` 283 | spring.super.scheduled.plug-in.colony=true 284 | #设置zookeeper地址,zookeeper集群多个地址用英文逗号隔开 285 | spring.super.scheduled.zookeeper.url=127.0.0.1:2181 286 | #设置zookeeper session超时时间,默认值为60秒 287 | spring.super.scheduled.zookeeper.sessionTimeout=60000 288 | #设置zookeeper连接超时时间,默认值为60秒 289 | spring.super.scheduled.zookeeper.connectionTimeout=60000 290 | ``` 291 | 292 | 293 | ## 版本更新 294 | ### 0.1.0版 295 | * 只兼容原生@Scheduled注解cron属性 296 | ### 0.2.0版 297 | * 完全兼容原生@Scheduled注解 298 | ### 0.2.1版 299 | * 完善Manager功能 300 | * 修复大量bug 301 | ### 0.3.0版 302 | * 添加api接口 303 | * 添加定时任务线程池配置 304 | ### 0.3.1版 305 | * 添加扩展接口 306 | ### 0.3.2版 307 | * 添加定时任务调度日志 308 | ### 0.3.3版 309 | * 添加任务线程运行时上下文 310 | * 添加跳过当前执行任务的能力 311 | * 添加扩展类的执行优先级 312 | ### 0.3.4版 313 | * 添加基于zookeeper的集群模式 314 | * 修复跳过单次运行产生的bug 315 | 316 | ## 后续计划 317 | * 后续加入可视化管理 318 | * 集群任务统一管理 319 | --------------------------------------------------------------------------------