Планировщик задач и использование таймера в Spring MVC на примере ScheduleTask и Quartz.
Планировщик задач и использование таймера в Spring MVC на примере ScheduleTask и Quartz.
Обзор приложения Spring MVC + AngularJS + Bootstrap + HTML5
Используемые технологии и библиотеки
- Spring MVC 4.2.4.Release
- Quartz Scheduler 2.2.1
- Maven 3.2.5
- IntelliJ IDEA 15.0.2
1. Описание задачи
Настроить приложение Spring MVC на выполнение запланированных задач по графику. Для этой цели мы будем использовать встроенную поддержку планировщика задач в Spring — Quartz Scheduler. Рассмотрим как запускать задачу по триггеру (по интервалу времени или выражению cron), а так же применение аннотаций для планирования задач.
2. Структура проекта
В проекте используются три класса для демонстрации различных видов планирования и выполнения задач: ScheduleTask — использует аннотацию @Scheduled для планирования задачи, QuartzTask и CronQuartzTask — два разных класса, которые показывают возможность выполнения задач по простому триггеру и с помощью выражения cron.
3. Зависимости в pom.xml
Относительно предыдущих глав была добавлена следующая зависимость.
1 2 3 4 5 6 7 8 9 |
<!-- Quartz scheduling framework --> <quartz.scheduling.version>2.2.1</quartz.scheduling.version> <!-- Quartz scheduling framework --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>${quartz.scheduling.version}</version> </dependency> |
Полный pom.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
<?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>ru.javastudy</groupId> <artifactId>mvc_html5_angular</artifactId> <version>1.0</version> <properties> <!-- Generic properties --> <java.version>1.8</java.version> <!-- Web --> <jsp.version>2.2</jsp.version> <!--Java standard tag library--> <jstl.version>1.2</jstl.version> <servlet.version>3.1.0</servlet.version> <!-- Spring --> <spring-framework.version>4.2.4.RELEASE</spring-framework.version> <!-- JUnit test --> <junit.version>4.12</junit.version> <!-- Logging --> <!--logback - improved version of log4j--> <logback.version>1.0.13</logback.version> <slf4j.version>1.7.13</slf4j.version> <!-- jackson json JSON Processing API --> <jackson.databind-version>2.2.3</jackson.databind-version> <!-- Hibernate / JPA --> <hibernate.version>5.0.1.Final</hibernate.version> <!-- I don't know why, but with 5.0.5 final app not working! --> <!-- Spring Data --> <spring-framework.data.version>1.9.1.RELEASE</spring-framework.data.version> <!-- Quartz scheduling framework --> <quartz.scheduling.version>2.2.1</quartz.scheduling.version> </properties> <dependencyManagement> <!--all spring dependencies --> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-framework-bom</artifactId> <version>${spring-framework.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <!--bootstrap webjars.org--> <dependencies> <!-- Spring MVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency> <!-- Other Servlet Web dependencies --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>${jstl.version}</version> </dependency> <!--Servlet API--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${servlet.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>${jsp.version}</version> <scope>provided</scope> </dependency> <!-- Apache Commons File Upload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> <!-- Excel view --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.12</version> </dependency> <!-- PDF view --> <dependency> <groupId>com.lowagie</groupId> <artifactId>itext</artifactId> <version>2.1.5</version> </dependency> <!-- HSQLDB embedded database. Встроенная база данных--> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.3.3</version> </dependency> <!-- Spring JDBC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> </dependency> <!--JUnit Test--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!-- Test Artifacts with Spring--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <scope>test</scope> </dependency> <!-- Logging with SLF4J & LogBack --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> <scope>compile</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> <scope>runtime</scope> </dependency> <!--Contains org.springframework.mail.javamail--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring-framework.version}</version> </dependency> <!-- Spring MVC Mail Related Dependency --> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4.7</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>1.7</version> </dependency> <!-- Spring REST jackson JSON Processing API --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.databind-version}</version> </dependency> <!--Hibernate ORM--> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>${hibernate.version}</version> </dependency> <!--Hibernate validator (contains @NotEmpty)--> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.1.0.Final</version> </dependency> <!--Spring Data--> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>${spring-framework.data.version}</version> </dependency> <!-- Quartz scheduling framework --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>${quartz.scheduling.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.0</version> <configuration> <source>1.8</source> <target>1.8</target> <compilerArgument>-Xlint:all</compilerArgument> <showWarnings>true</showWarnings> <showDeprecation>true</showDeprecation> </configuration> </plugin> </plugins> <!--need to find configs in tests in package web-inf like @ContextConfiguration(locations = {"classpath:/config/application-context.xml" --> <testResources> <testResource> <directory>src/main/webapp/WEB-INF/config</directory> </testResource> </testResources> </build> </project> |
4. web.xml, spring config
Приведу под катом полные файлы конфигурации для того, чтобы не нужно было скачивать проект и т.к. эти настройки не поменялись относительно предыдущих глав.
web.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
<?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"> <display-name>mvc-html5-angularjs</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value> WEB-INF/config/application-context.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/config/mvc-config.xml</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> <!--Позволяет работать с русскими символами--> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>/WEB-INF/view/index.jsp</welcome-file> </welcome-file-list> <error-page> <error-code>404</error-code> <location>/WEB-INF/view/error/errorstatus.jsp</location> </error-page> </web-app> |
mvc-config.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- context:component-scan This tag will scan @Component, @Repository, @Service, @Controller and also resolves @Autowired and @Qualifier --> <context:component-scan base-package="ru.javastudy.mvcHtml5Angular.mvc" /> <!-- mvc:annotation-driven configures Spring MVC annotations Support for validating @Controller inputs with @Valid, if a JSR-303 Provider is present on the classpath. HttpMessageConverter support for @RequestBody method parameters and @ResponseBody method return values from @RequestMapping or @ExceptionHandler methods. --> <mvc:annotation-driven/> <!-- activate @Transactional JPA annotation --> <tx:annotation-driven/> <!-- ViewResolver bean config for mapping strings to jsp views --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- Example: a logical view name of 'showMessage' is mapped to '/WEB-INF/jsp/showMessage.jsp' --> <property name="order" value="1" /> <property name="prefix" value="/WEB-INF/view/" /> <property name="suffix" value=".jsp" /> </bean> <!-- File Upload bean config--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- set the maximum file size in bytes --> <property name="maxUploadSize" value="1000000"/> </bean> <!--Excel and PDF xml view configuration --> <!--disabling for jUnit test. --> <bean class="org.springframework.web.servlet.view.XmlViewResolver"> <property name="order" value="0" /> <property name="location" value="/WEB-INF/config/excel-pdf-config.xml"/> </bean> <mvc:view-controller path="/index.html" view-name="/index"/> <mvc:view-controller path="/about.html" view-name="/about/about"/> <mvc:view-controller path="/file.html" view-name="/file/file"/> <mvc:view-controller path="/jdbc.html" view-name="/jdbc/jdbc"/> <mvc:view-controller path="/email.html" view-name="/email/email"/> <mvc:view-controller path="/rest.html" view-name="/rest/rest"/> <mvc:view-controller path="/orm.html" view-name="/orm/orm"/> <mvc:view-controller path="/jstl.html" view-name="/jstl/jstl"/> <!-- Static Resources Configuration (get access to static sources such as CSS and JavaScript files) --> <mvc:resources mapping="/resources/**" location="/resources/" /> <!-- themes can be put in different folder such as <mvc:resources mapping="/resources/**" location="/resources/themeBlue" /> <mvc:resources mapping="/resources/**" location="/resources/themeGreen" /> --> </beans> |
application-context.xml (добавленное будет описано ниже):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" 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/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> <!--find property file. See bean id='dataSource' for example ${jdbc.hsqldb.driverClass}--> <context:property-placeholder location="classpath:util.properties" /> <!-- XML Bean Definitions --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.hsqldb.driverClass}" /> <property name="url" value="${jdbc.hsqldb.url}" /> <property name="username" value="${jdbc.hsqldb.username}" /> <property name="password" value="${jdbc.hsqldb.password}" /> </bean> <!-- initialize Embedded DataSource. Встроенная база данных--> <jdbc:initialize-database data-source="dataSource"> <jdbc:script location="classpath:dbschema.sql"/> <jdbc:script location="classpath:test-data.sql"/> </jdbc:initialize-database> <!-- Java Mail Configuration --> <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <property name="username" value="${java.mail.username}"/> <property name="password" value="${java.mail.password}"/> <property name="port" value="465"/> <property name="javaMailProperties"> <props> <prop key="mail.smtp.auth">true</prop> <prop key="mail.smtp.starttls.enable">true</prop> <prop key="mail.smtp.starttls.required">true</prop> <prop key="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop> <prop key="mail.smtp.host">${java.mail.host}</prop> </props> </property> </bean> <!-- Velocity Email Template Config Bean --> <bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean"> <property name="resourceLoaderPath" value="/WEB-INF/email-templates/"/> </bean> <!-- REST template configuration --> <bean id="restTemplate" class="org.springframework.web.client.RestTemplate"/> <!--Do not forget activate @Transactional JPA annotation with <annotation-driven/>--> <!-- JPA Persistence Context and EntityManager configuration --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" > <!--packagesToScan - search Entity and mapping them --> <property name="packagesToScan" value="ru.javastudy.mvcHtml5Angular.mvc.bean" /> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" > <property name="generateDdl" value="true" /> <property name="showSql" value="true" /> </bean> </property> <property name="jpaProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">false</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> </bean> <!-- Automatic Transaction Participation--> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <!--not uses but could autowired in JDBCExample bean--> <!-- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> --> <!-- Quartz scheduling configuration --> <task:annotation-driven/> <!-- Quartz simple trigger --> <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> <property name="jobDetail" ref="simpleQuartzJob" /> <property name="repeatInterval" value="3000" /> <property name="startDelay" value="1000" /> </bean> <!-- Quartz cron trigger --> <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail" ref="quartzCronJob"/> <property name="cronExpression" value="0/7 * * * * ?" /> <!-- every 30 seconds (seconds, minutes, hours, day of month, month, day of week, year(optional)) --> </bean> <!-- Quartz job --> <bean id="simpleQuartzJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="simpleQuartzTask" /> <property name="targetMethod" value="simpleTaskMethod" /> </bean> <!-- Quartz cron job --> <bean id="quartzCronJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="cronQuartzTask" /> <property name="targetMethod" value="cronTaskMethod" /> </bean> <!-- Quartz simple task --> <bean id="simpleQuartzTask" class="ru.javastudy.mvcHtml5Angular.mvc.quarts.QuartzTask" /> <!-- Quartz cron task --> <bean id="cronQuartzTask" class="ru.javastudy.mvcHtml5Angular.mvc.quarts.CronQuartzTask" /> <!-- Quartz Scheduler --> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="jobDetails"> <list> <ref bean="simpleQuartzJob" /> <ref bean="quartzCronJob" /> </list> </property> <property name="triggers"> <list> <ref bean="simpleTrigger" /> <ref bean="cronTrigger" /> </list> </property> </bean> <!--ScheduleTask example. Use and @Component on ru.javastudy.mvcHtml5Angular.mvc.scheduling.ScheduleTask--> <!--<context:component-scan base-package="ru.javastudy.mvcHtml5Angular.mvc.scheduling"/>--> <!--or <bean id="scheduleTask" class="ru.javastudy.mvcHtml5Angular.mvc.scheduling.ScheduleTask"/> --> <!--End scheduling configuration --> </beans> |
5. Описание Quartz, Cron
Сначала о Quartz и в чем смысл его использования. Quartz — это библиотека с открытым исходным кодом, которая может быть встроена в любое Java приложение. С помощью нее можно планировать и выполнять сотни задач. Библиотека поддерживает много фич из enterprise технологий.
Cron — демон-планировщик заданий в Unix-подобных операционных системах, использующийся для периодического выполнения заданий в определённое время. Выражения cron имеют вид ‘ * * * * * ? ‘ (minutes, hours, day of month, month, day of week, year(optional) ).
Более подробную информацию о выражения cron можно найти в интернете, для нас же будет важно понимание следующего:
* — выбирает все величины. То есть на месте позиции часа символ * означает, что задание будет выполняться каждый час;
? — незначащая величина;
, — отделяет дополнительные величины. Например, триггер “0 0 11,12 * * ?” будет срабатывать в 11 и 12 часов;
/ — определяет инкремент величины. Например, “0 0 0/2 * * ?” означает, что триггер будет срабатывать каждые 2 часа.
Сравнение Cron и Quartz
- Использование cron подразумевает подключение еще одной точки входа в приложение, в то время как Quartz уже встроен в него. Для Quartz появляется много возможностей внутренней коммуникации в приложении, а для cron всё сложнее. Quartz можно запустить в многопоточной среде без особых сложностей.
- cron зависит от платформы, Quartz — нет.
- Quartz позволяет гарантировать запуск задач после прошедшего времени вызова (например если сервер в нужное время лежал, то задача всё равно запуститься при восстановлении работы). В то время как чистый cron это не гарантирует (если не настроено вручную).
- Quartz поддерживает более гибкие выражения и настройки.
- Quartz позволяет работать в многопоточном режиме, что добавляет множество возможностей.
- Если вы используете cron из ОС, то при изменении состоянии jvm любое состояние cron может быть потеряно.
- С Quartz вы получаете возможность портирования (например на Win OS где cron недоступен).
- Возможность администрирования состояния Quartz (например на Tomcat можно смотреть что там происходит с планировщиком).
6. Описание классов и настроек
@Scheduled
Самый простой способ настройки планировщика задач — использование в классе аннотации @Scheduled.
ScheduleTask:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
package ru.javastudy.mvcHtml5Angular.mvc.scheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.Date; /** * Created for JavaStudy.ru on 02.03.2016. */ @Component public class ScheduleTask { @Scheduled(fixedDelay = 10000) public void fixedDelaySchedule() { System.out.println("fixedDelaySchedule every 10 seconds" + new Date()); } //every 30 seconds (seconds, minutes, hours, day of month, month, day of week, year(optional)) @Scheduled(cron = "0/30 * * * * ?") public void cronSchedule() { System.out.println("cronSchedule every 30 seconds" + new Date()); } } |
Класс задаем в качестве spring bean с помощью аннотации @Component. Теперь над методами используем аннотацию @Scheduled с параметрами. В данном случае в одном методе задается задержка в 10000 мс, а во втором передается cron выражение. В результате методы будут выполнятся каждые 10 и 30 секунд соответственно.
Конфигурация spring для работы аннотации @Scheduled
Для того, чтобы аннотация была замечена приложением и методы в классе были выполнены как задумано, необходимо в файле конфигурации spring записать следующие строчки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" 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/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> <!-- Quartz scheduling configuration --> <task:annotation-driven/> <!--ScheduleTask example. Use and @Component on ru.javastudy.mvcHtml5Angular.mvc.scheduling.ScheduleTask--> <context:component-scan base-package="ru.javastudy.mvcHtml5Angular.mvc.scheduling"/> <!--or <bean id="scheduleTask" class="ru.javastudy.mvcHtml5Angular.mvc.scheduling.ScheduleTask"/> --> <!--End scheduling configuration --> ... |
- Определить в пространстве имен xmlns:task=»http://www.springframework.org/schema/task» и http://www.springframework.org/schema/task, http://www.springframework.org/schema/task/spring-task.xsd
- Подключить поиск аннотаций для планирования задач <task:annotation-driven/>
- Задать бин или указать пакет поиска бинов с аннотациями спринг (@Component)
Теперь при запуске приложения, каждые 10 и 30 секунд будет выводиться в консоль следующие записи (первая запись будет при запуске, т.к. в cron выражении 0\30 задана нулевая задержка после запуска приложения и вызов каждые 30 секунд):
1 2 3 4 5 6 |
cronSchedule every 30 secondsTue Apr 05 22:37:30 MSK 2016 fixedDelaySchedule every 10 secondsTue Apr 05 22:37:38 MSK 2016 fixedDelaySchedule every 10 secondsTue Apr 05 22:37:48 MSK 2016 fixedDelaySchedule every 10 secondsTue Apr 05 22:37:58 MSK 2016 cronSchedule every 30 secondsTue Apr 05 22:38:00 MSK 2016 fixedDelaySchedule every 10 secondsTue Apr 05 22:38:08 MSK 2016 |
Quartz Task
Теперь рассмотрим как планировать и выполнять задачи с помощью библиотеки Quartz Scheduler. Мы будем рассматривать два способа планирования задач: 1й по простому триггеру с временной задержкой, 2й — с помощью выражения cron аналогично примеру с аннотациями.
QuartzTask:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package ru.javastudy.mvcHtml5Angular.mvc.quarts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Calendar; /** * look application-context.xml * 1. simpleTrigger * 2. simpleQuartzJob * 3. bean id="simpleQuartzTask" * 4. Quartz Scheduler */ public class QuartzTask { private static final Logger logger = LoggerFactory.getLogger(QuartzTask.class); public void simpleTaskMethod() { // you can log here to database with simpletrigger logger.info("Test Simple Quartz Time: " + Calendar.getInstance().getTime()); System.out.println("Test Simple Quartz Time: " + Calendar.getInstance().getTime()); } } |
CronQuartzTask:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package ru.javastudy.mvcHtml5Angular.mvc.quarts; import java.util.Calendar; /** * look application-context.xml * 1. cronTrigger * 2. quartzCronJob * 3. bean id="cronQuartzTask" * 4. Quartz Scheduler */ public class CronQuartzTask { public void cronTaskMethod() { //you can send emails to users here System.out.println("Cron Time: " + Calendar.getInstance().getTime()); } } |
Как видите, оба класса ничего не реализуют, не расширяют и не помечены какой-либо аннотацией. В них по одному методу с выводом в консоль простенькой информации.
Чтобы выполнить эти методы нам понадобится добавить немного настроек в конфигурацию Spring. В данном случае они были добавлены в application-context.xml.
application-context.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
<!-- Quartz scheduling configuration --> <task:annotation-driven/> <!-- Quartz simple trigger --> <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> <property name="jobDetail" ref="simpleQuartzJob" /> <property name="repeatInterval" value="3000" /> <property name="startDelay" value="1000" /> </bean> <!-- Quartz cron trigger --> <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail" ref="quartzCronJob"/> <property name="cronExpression" value="0/7 * * * * ?" /> <!-- every 30 seconds (seconds, minutes, hours, day of month, month, day of week, year(optional)) --> </bean> <!-- Quartz job --> <bean id="simpleQuartzJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="simpleQuartzTask" /> <property name="targetMethod" value="simpleTaskMethod" /> </bean> <!-- Quartz cron job --> <bean id="quartzCronJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="cronQuartzTask" /> <property name="targetMethod" value="cronTaskMethod" /> </bean> <!-- Quartz simple task --> <bean id="simpleQuartzTask" class="ru.javastudy.mvcHtml5Angular.mvc.quarts.QuartzTask" /> <!-- Quartz cron task --> <bean id="cronQuartzTask" class="ru.javastudy.mvcHtml5Angular.mvc.quarts.CronQuartzTask" /> <!-- Quartz Scheduler --> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="jobDetails"> <list> <ref bean="simpleQuartzJob" /> <ref bean="quartzCronJob" /> </list> </property> <property name="triggers"> <list> <ref bean="simpleTrigger" /> <ref bean="cronTrigger" /> </list> </property> </bean> |
Рассмотрим что здесь написано по пунктам.
- Вначале задается два триггера, по которым будут запускаться задачи. В них в качестве параметров передается информация о «работе», которую нужно выполнить и свойство, которое устанавливает событие, в результате которого будет запущено выполнение задачи. В данном случае это или временная задержка или выражение cron.
- Описание «работы», т.е. задачи, которую необходимо выполнить. Параметрами выступают бин и метод в этом классе. В нашем случае это класс QuartzTask и метод simpleTaskMethod() в нем. Аналогично для примера с cron.
- Задание двух классов в качестве spring бинов (QuartzTask и CronQuartzTask).
- Настройка самого планировщика. В него передается список «работ» (задач) и триггеры, по которым они должны будут запускаться.
Запуск выдаст примерно следующее:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Test Simple Quartz Time: Tue Apr 05 22:08:19 MSK 2016 Cron Time: Tue Apr 05 22:08:21 MSK 2016 Test Simple Quartz Time: Tue Apr 05 22:08:22 MSK 2016 Test Simple Quartz Time: Tue Apr 05 22:08:25 MSK 2016 fixedDelaySchedule every 10 secondsTue Apr 05 22:08:25 MSK 2016 Cron Time: Tue Apr 05 22:08:28 MSK 2016 Test Simple Quartz Time: Tue Apr 05 22:08:28 MSK 2016 cronSchedule every 30 secondsTue Apr 05 22:08:30 MSK 2016 Test Simple Quartz Time: Tue Apr 05 22:08:31 MSK 2016 Test Simple Quartz Time: Tue Apr 05 22:08:34 MSK 2016 Cron Time: Tue Apr 05 22:08:35 MSK 2016 fixedDelaySchedule every 10 secondsTue Apr 05 22:08:35 MSK 2016 Test Simple Quartz Time: Tue Apr 05 22:08:37 MSK 2016 |
Исходные коды
MVC_AngularJS_Html5 full project — полный проект Spring MVC + AngularJS + Bootstrap + HTML5.
9. ScheduleTask and Quartz — код для этой части
Обзор приложения Spring MVC + AngularJS + Bootstrap + HTML5
4One thought on “Планировщик задач и использование таймера в Spring MVC на примере ScheduleTask и Quartz.”
Добавить комментарий
Для отправки комментария вам необходимо авторизоваться.
недавно на конференции услышал такую мысль, что «специалисты не рекомендуют использовать многопоточность из веб-приложений»… к сожалению, не имел возможности задать вопрос выступающему, но, возможно у Вас есть идеи откуда появилось такое предубеждение?