Spring定时任务中@PostConstruct被多次执行异常

本文记录了一次因@RequestMapping注解配置重复而导致Spring MVC启动失败的问题排查过程。通过详细的错误日志分析,找到了问题根源并给出了解决方案。

Spring定时任务中@PostConstruct被多次执行异常

项目中刚刚修改一个功能,代码正准备验证,启动Idea的debug模式,运行项目,发现启动失败,查看日志发现定时任务被重复执行,导致异常。debug定时任务的初始化类,发现启动定时任务是在@PostConstruct方法中执行的,网上查询,有说Spring在某种情况下初始化有bug,注解@Component可能出现多次执行。把@Component修改为@Service就行了!
结果我改了也没解决问题!
无赖更一步跟进日志,发现以下内容:

[ERROR][2017-06-20 19:50:16] - [org.springframework.web.servlet.DispatcherServlet] - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'galleryController' bean method 
public com.test.model.domain.response.Result com.test.model.back.controller.GalleryController.removeRecord(java.lang.Integer)
to {[/gallery/removeRecord],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}: There is already 'galleryController' bean method
public java.lang.String com.test.model.back.controller.GalleryController.removeRecord(java.lang.Integer,org.springframework.ui.ModelMap) mapped.
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:755) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757) ~[spring-context-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480) ~[spring-context-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:663) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:535) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:489) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136) [spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at javax.servlet.GenericServlet.init(GenericServlet.java:158) [servlet-api.jar:3.0.FR]
    at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1282) [catalina.jar:7.0.70]
    at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1088) [catalina.jar:7.0.70]
    at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5349) [catalina.jar:7.0.70]
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5641) [catalina.jar:7.0.70]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147) [catalina.jar:7.0.70]
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:899) [catalina.jar:7.0.70]
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:875) [catalina.jar:7.0.70]
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:652) [catalina.jar:7.0.70]
    at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1863) [catalina.jar:7.0.70]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.7.0_79]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.7.0_79]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_79]
    at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_79]
    at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301) [tomcat-coyote.jar:7.0.70]
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819) [na:1.7.0_79]
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801) [na:1.7.0_79]
    at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:618) [catalina.jar:7.0.70]
    at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:565) [catalina.jar:7.0.70]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.7.0_79]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.7.0_79]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_79]
    at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_79]
    at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301) [tomcat-coyote.jar:7.0.70]
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819) [na:1.7.0_79]
    at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801) [na:1.7.0_79]
    at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1487) [na:1.7.0_79]
    at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:97) [na:1.7.0_79]
    at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1328) [na:1.7.0_79]
    at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1420) [na:1.7.0_79]
    at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:848) [na:1.7.0_79]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.7.0_79]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.7.0_79]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_79]
    at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_79]
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322) [na:1.7.0_79]
    at sun.rmi.transport.Transport$2.run(Transport.java:202) [na:1.7.0_79]
    at sun.rmi.transport.Transport$2.run(Transport.java:199) [na:1.7.0_79]
    at java.security.AccessController.doPrivileged(Native Method) [na:1.7.0_79]
    at sun.rmi.transport.Transport.serviceCall(Transport.java:198) [na:1.7.0_79]
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:567) [na:1.7.0_79]
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828) [na:1.7.0_79]
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.access$400(TCPTransport.java:619) [na:1.7.0_79]
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:684) [na:1.7.0_79]
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:681) [na:1.7.0_79]
    at java.security.AccessController.doPrivileged(Native Method) [na:1.7.0_79]
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:681) [na:1.7.0_79]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [na:1.7.0_79]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [na:1.7.0_79]
    at java.lang.Thread.run(Thread.java:745) [na:1.7.0_79]
Caused by: java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'galleryController' bean method 
public com.test.model.domain.response.Result com.test.model.back.controller.GalleryController.removeRecord(java.lang.Integer)
to {[/gallery/removeRecord],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}: There is already 'galleryController' bean method
public java.lang.String com.test.model.back.controller.GalleryController.removeRecord(java.lang.Integer,org.springframework.ui.ModelMap) mapped.
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerHandlerMethod(AbstractHandlerMethodMapping.java:212) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.detectHandlerMethods(AbstractHandlerMethodMapping.java:184) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.initHandlerMethods(AbstractHandlerMethodMapping.java:144) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.afterPropertiesSet(AbstractHandlerMethodMapping.java:123) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.afterPropertiesSet(RequestMappingHandlerMapping.java:126) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1633) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1570) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    ... 63 common frames omitted
[2017-06-20 07:50:16,467] Artifact back:war exploded: Artifact is deployed successfully
[2017-06-20 07:50:16,467] Artifact back:war exploded: Deploy took 6,867 milliseconds

原来,由于定时任务异常比较多,忽略了最开始的异常内容,发现程序异常后自己重新又启动了,而这个异常是GalleryController这个控制器中@RequestMapping配置重复导致的。springMVC中不允许有重复@RequestMapping,刚刚修改的方法中忘记修改@RequestMapping中的地址,所以报错,而Idea发现异常后会自动重启Tomcat容器,结果就是无限循环,异常日志太多,看不过来,异常排查的方向不对导致!

<think>我们参考了用户提供的引用内容,特别是引用[1][2][4]中关于Spring Boot定时任务的介绍。用户的需求是:在Spring Boot应用中控制定时任务执行一次。 解决方案分析: 在Spring Boot中,通常使用`@Scheduled`注解来创建定时任务。但是,标准的`@Scheduled`注解支持`cron`、`fixedRate`、`fixedDelay`等属性,这些属性都会导致任务重复执行。如果希望任务只执行一次,我们可以采用以下两种方法: 方法一:使用`@Scheduled`的`initialDelay`属性,并设置一个足够长的延迟时间(比如几年),这样在应用运行期间任务只会执行一次。但这种方法够优雅,且如果应用重启,任务可能会再次执行(因为初始延迟会重新计算)。 方法二:在任务执行体中控制只执行一次。我们可以在任务执行时记录状态(比如在数据库中记录或使用一个原子变量),确保即使任务被多次触发,实际业务逻辑也只执行一次。 然而,用户要求的是“单次执行”,即任务在应用启动后只执行一次,且重复执行。我们可以结合Spring Boot的任务调度机制,通过编程方式动态注册任务,并在任务执行后取消调度。 具体步骤: 1. 创建一个实现`Runnable`接口的任务类。 2. 使用`ScheduledTaskRegistrar`动态注册一个只执行一次的任务(通过`ScheduledFuture`)。 3. 在任务执行完毕后,取消该任务的调度。 这种方法的优点: - 任务只会在注册后执行一次 - 会因为应用长时间运行而导致重复执行 - 需要依赖外部存储状态 实现代码: 步骤1:创建任务类(可以是Runnable的匿名内部类) 步骤2:使用`ScheduledTaskRegistrar`的`scheduler`调度一次任务 步骤3:在任务中执行逻辑,然后取消任务 注意:我们需要获取到`ScheduledFuture`对象,以便在任务执行后取消。 但是,在Spring Boot中,我们通常通过配置`SchedulingConfigurer`来使用`ScheduledTaskRegistrar`。 下面是一个完整的实现示例: ```java import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.config.ScheduledTaskRegistrar; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @Configuration @EnableScheduling public class OneTimeScheduledTask implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { // 创建一个任务 Runnable task = () -> { try { // 这里写你的业务逻辑,只执行一次 System.out.println("执行一次定时任务"); } finally { // 任务执行完成后,取消调度(避免重复执行) // 注意:这里需要访问到ScheduledFuture,所以我们在定义任务时将其捕获 } }; // 使用ScheduledFuture来跟踪任务,以便取消 ScheduledFuture<?> future = taskRegistrar.getScheduler().schedule( task, triggerContext -> { // 设置任务在应用启动后立即执行(延迟0秒) return new Date(System.currentTimeMillis()); } ); // 修改任务,使其在执行完成后取消 Runnable wrappedTask = () -> { task.run(); // 取消任务 future.cancel(false); }; // 重新调度任务(实际执行的是包装后的任务) taskRegistrar.addTriggerTask(wrappedTask, triggerContext -> { // 立即执行一次 return new Date(System.currentTimeMillis()); }); } } ``` 然而,上述代码有一个问题:`ScheduledFuture`是在任务被调度执行之前就获取的,并且我们包装了一个新的任务,在原始任务执行后取消。但是,注意我们使用`addTriggerTask`重新添加了一个任务,而之前用`schedule`方法调度的任务实际上没有被取消,所以我们需要避免两个任务同时存在。 优化方案:我们只使用一次触发任务,并在任务执行后取消自己。但是,在触发任务中我们无法直接获取到自己的`ScheduledFuture`。 因此,我们可以这样做: ```java @Configuration @EnableScheduling public class OneTimeScheduledTask implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { // 使用一个原子引用保存ScheduledFuture AtomicReference<ScheduledFuture<?>> futureRef = new AtomicReference<>(); Runnable task = () -> { try { // 业务逻辑 System.out.println("执行一次定时任务"); } finally { // 取消任务 ScheduledFuture<?> future = futureRef.get(); if (future != null) { future.cancel(false); } } }; // 定义一个Trigger,用于确定任务执行时间(立即执行) Trigger trigger = triggerContext -> new Date(); // 添加任务,并捕获返回的ScheduledFuture ScheduledFuture<?> future = taskRegistrar.getScheduler().schedule(task, trigger); futureRef.set(future); } } ``` 但是,这样写有一个问题:`ScheduledTaskRegistrar`的`getScheduler()`返回的是`ScheduledExecutorService`,它的`schedule`方法只执行一次。所以,我们需要在任务中取消,因为`schedule`方法本来就是只执行一次的。 因此,最简单的方案是: ```java @Configuration @EnableScheduling public class OneTimeScheduledTask implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.getScheduler().schedule( () -> { // 这里写只执行一次的任务逻辑 System.out.println("执行一次定时任务"); }, 0, // 延迟0秒,立即执行 TimeUnit.SECONDS ); } } ``` 这样,任务只会在应用启动后执行一次,因为`schedule`方法就是用于安排一个在给定延迟后只执行一次的任务。 所以,我们需要额外的取消操作。 验证:当应用启动时,任务会执行一次,然后会再次执行。 但是,请注意:`ScheduledTaskRegistrar`的`getScheduler()`方法返回的`ScheduledExecutorService`是由Spring管理的,它会在应用关闭时自动关闭。 因此,推荐使用这种简单的方式。 另外,我们也可以使用`@Scheduled`注解的方式,但需要结合条件控制,比如使用一个标志位: ```java @Component @EnableScheduling public class OneTimeTask { private AtomicBoolean executed = new AtomicBoolean(false); @Scheduled(fixedDelay = Long.MAX_VALUE) // 设置一个非常大的延迟,确保会重复执行 public void executeOnce() { if (executed.compareAndSet(false, true)) { // 执行任务 System.out.println("执行一次"); } } } ``` 但是,这种方法实际上任务被多次触发(虽然业务逻辑只执行一次),而且第一次触发后,由于设置了`Long.MAX_VALUE`的延迟,几乎会再次触发(除非应用运行超过290年)。所以,这种方法虽然可行,但够直观。 结论:使用`SchedulingConfigurer`和`ScheduledExecutorService.schedule()`方法是最直接且符合需求的。 因此,我们给出最终方案: ```java import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.config.ScheduledTaskRegistrar; import java.util.concurrent.TimeUnit; @Configuration @EnableScheduling public class OneTimeTaskConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { // 在应用启动后,延迟0秒执行一次 taskRegistrar.getScheduler().schedule(() -> { // 这里写你的业务代码 System.out.println("This task runs only once after application starts."); }, 0, TimeUnit.SECONDS); } } ``` 这样,任务将在Spring Boot应用启动后立即执行一次,且只执行一次。 注意事项: 1. 确保任务执行时间要太长,以免阻塞其他任务(如果任务耗时较长,建议使用异步任务)。 2. 如果任务执行过程中抛出异常,需要根据业务需求处理异常,避免影响应用运行。 引用说明: 在Spring Boot中,通过`SchedulingConfigurer`可以自定义任务调度器,并动态添加任务[^2]。使用`ScheduledExecutorService`的`schedule`方法可以安排一次性任务[^4]。 因此,我们推荐使用上述配置类的方式实现单次执行定时任务。 相关问题: 1. 如何在Spring Boot中实现只执行一次的定时任务? 2. Spring Boot定时任务单次执行有哪些方法? 3. 如何动态注册Spring Boot定时任务? 4. 在Spring Boot中如何取消已经注册的定时任务?</think>在 Spring Boot 中实现定时任务单次执行,可以通过以下两种推荐方法实现: ### 方法一:使用 `@Scheduled` 结合执行标志位(推荐) ```java import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.concurrent.atomic.AtomicBoolean; @Component public class OneTimeScheduledTask { private final AtomicBoolean executed = new AtomicBoolean(false); // 应用启动后立即执行一次 @Scheduled(initialDelay = 0) public void executeOnce() { if (executed.compareAndSet(false, true)) { // 这里放置需要执行一次的代码 System.out.println("任务执行一次完成"); // 实际业务逻辑示例: // dataMigrationService.migrateDataOnce(); } } } ``` **实现原理:** 1. 使用 `AtomicBoolean` 确保线程安全的执行状态控制 2. `@Scheduled(initialDelay = 0)` 使任务在应用启动后立即执行 3. `compareAndSet(false, true)` 保证只执行一次 4. 符合 Spring Boot 定时任务规范,无需额外配置[^1][^2] ### 方法二:使用 `ScheduledExecutorService` 实现精确控制 ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @Configuration public class OneTimeTaskConfig { @PostConstruct public void scheduleOneTimeTask() { ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); // 延迟0秒执行(立即执行) executor.schedule(() -> { // 这里放置需要执行一次的代码 System.out.println("一次性任务执行完成"); // 实际业务逻辑示例: // dataMigrationService.migrateDataOnce(); executor.shutdown(); // 关闭线程池 }, 0, TimeUnit.SECONDS); } } ``` **两种方法对比:** | 特性 | `@Scheduled` + 标志位 | `ScheduledExecutorService` | |------|----------------------|----------------------------| | 复杂度 | 简单 | 中等 | | 线程管理 | Spring 管理 | 手动管理 | | 启动时机 | 随 Spring 容器启动 | 精确控制 | | 适合场景 | 常规单次任务 | 需要精细控制的任务 | | 是否依赖 Spring | 是 | 否 | ### 注意事项 1. **分布式环境**:在集群部署时,需使用分布式锁(如 Redis 锁)替代 `AtomicBoolean`,避免多实例重复执行 2. **执行时间**:确保任务执行时间合理,避免阻塞应用启动 3. **异常处理**:添加 try-catch 块捕获异常,防止单次任务失败导致业务中断 4. **任务可见性**:建议添加日志记录,明确标记任务执行状态 ### 配置说明 在启动类添加 `@EnableScheduling` 注解(仅方法一需要): ```java @SpringBootApplication @EnableScheduling // 启用定时任务 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` > **提示**:对于需要精确控制执行时间的单次任务(如特定日期执行),可结合 `@Scheduled(cron = "一次性时间表达式")` 实现[^4]。对于需要任务执行后立即销毁的场景,推荐使用方法二。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值