zoukankan      html  css  js  c++  java
  • SpringMVC自带Cron定时器Demo及常见问题

    该技术的不适用的场景

    如果在集群环境下,多台服务器中只希望有一台执行,那 Spring 自带的这种定时器方式可能不太符合你的需要。
    但是,如果每台服务器都需要独立执行该定时器任务,且相互之间不存在同步,那么还是可以考虑的

    SpringMVC 定时器

    本文着重介绍的是 SpringMVC 配置定时器的方式,而不是 SpringBoot 配置定时器的方式。如果你想了解 fixedDelayfixedRate 的使用,我想下面这篇 SpringTask定时任务的使用 讲得不错。如果是 SpringBoot 配置定时器,那么关键区别就在于使用 @EnableScheduling 注解。

    本文 Demo 的项目结构:

    注解方式

    首先,在 Clock 类上添加 @Component,然后,在需要定时执行的方法上面加上 @Scheduled,最后指定 cron 表达式。

    Clock.java

    package coderead.spring.scheduled;
    
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    @Component
    public class Clock {
    
        // 每5秒钟执行一次
        @Scheduled(cron = "*/5 * * * * ?")
        public void testTime() {
            System.out.println(new Date());
        }
    }
    

    spring-mvc.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:task="http://www.springframework.org/schema/task"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/mvc
                http://www.springframework.org/schema/mvc/spring-mvc.xsd
                http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd
                http://www.springframework.org/schema/task
                http://www.springframework.org/schema/task/spring-task.xsd">
    
        <mvc:annotation-driven />
    
        <context:component-scan base-package="coderead.spring.*" />
        
        <!-- 定时任务支持注解 -->
        <task:annotation-driven />
    
    </beans>
    

    web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
             version="3.1">
        <!--配置多个上下文会导致多次执行-->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/spring-mvc.xml</param-value>
        </context-param>
    
        <!-- ================================== listener ================================== -->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
    
        <!-- ================================== servlet ================================== -->
        <!-- 前端控制器 -->
        <servlet>
            <servlet-name>dispatcherServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value></param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>dispatcherServlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
    </web-app>
    

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>coderead.spring</groupId>
        <artifactId>spring-scheduled-test</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>war</packaging>
    
    
        <properties>
            <spring.version>5.1.6.RELEASE</spring.version>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>${spring.version}</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.eclipse.jetty</groupId>
                    <artifactId>jetty-maven-plugin</artifactId>
                    <version>9.4.33.v20201020</version>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    如果你不知道怎么用 jetty 启动项目,你可以考虑参考 使用maven-Jetty9-plugin插件运行第一个Servlet

    xml 配置方式

    如果你需要使用 xml 配置,你会发现 @Scheduled 注解和 <task:scheduled> 有着相同的属性。因此我们将上一节的代码稍稍改动一下:

    Clock.java 去掉注解

    package coderead.spring.scheduled;
    
    import java.util.Date;
    
    public class Clock {
    
        public void testTime() {
            System.out.println(new Date());
        }
    }
    

    spring-mvc.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:task="http://www.springframework.org/schema/task"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/mvc
                http://www.springframework.org/schema/mvc/spring-mvc.xsd
                http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd
                http://www.springframework.org/schema/task
                http://www.springframework.org/schema/task/spring-task.xsd">
    
        <mvc:annotation-driven />
    
        <context:component-scan base-package="coderead.spring.*" />
    
        <!--用 xml 方式注入 Clock Bean-->
        <bean id="clock" class="coderead.spring.scheduled.Clock" />
        <!--用 xml 方式设置定时器-->
        <task:scheduled-tasks>
            <task:scheduled ref="clock" method="testTime" cron="*/5 * * * * ?"/>
        </task:scheduled-tasks>
    
    </beans>
    

    常见问题

    @Scheduled 定时任务不生效

    @Scheduled定时任务不生效???

    1. 此方法不能有参数
    2. 此方法不能有返回值
    3. 此类中不能包含其他带任何注解的方法(发现新大陆)

    还有一种可能就是没有在 spring-mvc.xml 文件中加入 <task:annotation-driven /> 而不仅仅是加入 <mvc:annotation-driven />

    @Scheduled 定时任务执行两次

    @Scheduled Spring定时任务每次执行两次解决方案
    主要原因是 web.xml 同时设置了 <context-param><init-param> 都设置了 contextConfigLocation,两次加载配置文件

    <web-app ....>
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/spring-mvc.xml</param-value>
        </context-param>
        ...
        <servlet>
            <servlet-name>dispatcherServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:spring/spring-mvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        ...
    </web-app>
    

    cron 表达式

    cron 表达式是用来规定代码执行周期的一种表达式,cron表达式详解 这篇文章详细的讲解了 cron 表达式的使用细节。
    以我的浅陋的经验,我对 cron 表达式的记忆是:

    1. 常用的 cron 表达式由 6 个域组成,域和域之间以空格分开

    2. 域从左到右,时间单位从秒开始逐步增大。他们分别是 "秒 分 时 日期 月份 星期"

    3. 因为日期和星期会相互影响,通常如果其中一个用 ? 表示任意,则另一个必须用 ? 表示“任意”。

    原因:通常,在指定日期条件之后,我们虽然希望“任意星期几”,但是实际上,此时星期需要根据日期的变化而相应变化,做不到完全任意。

    你还可以通过 在线 Cron 表达式 来帮助你理解前人代码中的 cron 表达式的含义,或者根据你的需求生成一个新的 cron 表达式。

    参考文献

    使用SpringMVC自带的@Scheduled完成定时任务

    SpringTask定时任务的使用

    spring 框架自带的定时器功能

  • 相关阅读:
    (转) 建立自己的MemberShip数据库
    '??' 语法
    c# 静态构造函数(转)
    ReSharp+VAssistX+VS2003 的个人设置
    支持多种数据类型的ListView排序
    学习笔记
    Java实验报告(实验二)
    Java实验报告(实验一)
    java数组中null和空的区别。
    网页选项卡功能
  • 原文地址:https://www.cnblogs.com/kendoziyu/p/spring-mvc-task-scheduled-with-cron-expression.html
Copyright © 2011-2022 走看看