Jade Dungeon

Spring

Spring框架

Spring.transaction

JPA和事务管理

很重要的一点是JPA本身并不提供任何类型的声明式事务管理。如果在依赖注入容器之外 使用JPA,事务处理必须由开发人员编程实现。

UserTransaction utx = entityManager.getTransaction();
 
try {
    utx.begin();
    businessLogic();
    utx.commit();
} catch(Exception ex) {
    utx.rollback();
    throw ex;
}

这种方式的事务管理使事务范围可以在代码中很清晰地表达出来,但它有以下缺点:

  • 容易出现重复代码和错误
  • 任何错误可能产生较大的影响
  • 错误难以调试和复现
  • 降低了代码库的可读性
  • 如果该方法调用了其他的事务方法如何处理呢?

使用Spring Transactional注解

使用Spring Transactional注解,上面的代码就简化为:

@Transactional
public void businessLogic() {
	// ... use entity manager inside a transaction ...
}

代码更加简洁,可读性更好,也是目前Spring中事务处理的推荐方式。

通过使用@Transactional,事务传播等很多重要方面可以自动处理。这种情况下如果 businessLogic()调用了其他事务方法,该方法将根据选项确定如何加入正在运行事务。

这个强大机制的一个潜在缺点是它隐藏了底层的运行,当它不能正常工作时很难调试。

@Transactional含义

关于@Transactional,关键点之一是要考虑两个独立的概念,它们都有各自的范围和 生命周期:

  • persistence context(持久化上下文)
  • database transaction(事务)

@Transactional本身定义了单个事务的范围。这个事务在persistence context的范围内 。

JPA中的持久化上下文是EntityManager,内部实现使用了Hibernate Session(使用 Hibernate作为持久化provider)。

持久化上下文仅仅是一个同步对象,它记录了有限集合的Java对象的状态,并且保证这些 对象的变化最终持久化到数据库。

这是与单个事务非常不同的概念。一个Entity Manager可以跨越多个事务使用,而且的确 是这样使用的。

EntityManager何时跨越多个事务?

最常见的情况是应用使用Open Session In View模式处理懒初始化异常时,之前的文章 介绍过这种做法的优势和劣势。

这种情况下视图层运行的多个查询处于独立的事务中,而不是单事务的业务逻辑,但这些 查询由相同的entity manager管理。

另一种情况是开发人员将持久化上下文标记为PersistenceContextType.EXTENDED, 这表示它能够响应多个请求。

如何定义EntityManager和Transaction之间的关系?

这由应用开发者来选择,但是JPA Entity Manager最常用的方式是「Entity Manager per application transaction」(每个事务都有自己的实体管理器)模式。entity manager 注入的常用方法是:

@PersistenceContext
private EntityManager em;

这里默认为「Entity Manager per transaction」模式。这种模式下如果在@Transactional 方法内部使用该Entity Manager,那么该方法将在单一事务中运行。

@PersistenceContext如何工作?

随之而来的问题就是@PersistenceContext如何仅在容器启动时注入entity manager,假定 entity manager生命周期很短暂,而且每次请求需要多个entity manager。

答案是它不能:EntityManager是一个接口,注入到spring bean中的不是entity manager 本身,而是在运行时代理具体entity manager的context aware proxy(上下文感知代理) 。

通常用于代理的具体类为SharedEntityManagerInvocationHandler,借助调试器可以确认 这一点。

那么@Transactional如何工作?

实现了EntityManager接口的持久化上下文代理并不是声明式事务管理的唯一部分,事实上 包含三个组成部分:

  • EntityManager Proxy本身
  • 事务的切面
  • 事务管理器

看一下这三部分以及它们之间的相互作用。

事务的切面

事务的切面是一个「around(环绕)」切面,在注解的业务方法前后都可以被调用。实现 切面的具体类是TransactionInterceptor。

事务的切面有两个主要职责:

  • before时,切面提供一个调用点,来决定被调用业务方法应该在正在进行事务的 范围内运行,还是开始一个新的独立事务。
  • after时,切面需要确定事务被提交,回滚或者继续运行。

before时,事务切面自身不包含任何决策逻辑,是否开始新事务的决策委派给事务 管理器完成。

事务管理器

事务管理器需要解决下面两个问题:

  • 新的Entity Manager是否应该被创建?
  • 是否应该开始新的事务?

这些需要事务切面before逻辑被调用时决定。事务管理器的决策基于以下两点:

  • 事务是否正在进行
  • 事务方法的propagation属性(比如REQUIRES_NEW总要开始新事务)

如果事务管理器确定要创建新事务,那么将:

  • 创建一个新的entity manager
  • entity manager绑定到当前线程
  • 从数据库连接池中获取连接
  • 将连接绑定到当前线程

使用ThreadLocal变量将entity manager和数据库连接都绑定到当前线程。

事务运行时他们存储在线程中,当它们不再被使用时,事务管理器决定是否将他们清除。

程序的任何部分如果需要当前的entity manager和数据库连接都可以从线程中获取。

EntityManager proxy

EntityManager proxy(前面已经介绍过)就是谜题的最后一部分。当业务方法调用 entityManager.persist()时,这不是由entity manager直接调用的。

而是业务方法调用代理,代理从线程获取当前的entity manager,前面介绍过事务管理器 将entity manager绑定到线程。

了解了@Transactional机制的各个部分,我们来看一下实现它的常用Spring配置。

整合三个部分

如何将三个部分组合起来使事务注解可以正确地发挥作用呢?首先定义entity manager工厂 。

这样就可以通过持久化上下文注解注入Entity Manager proxy。

@Configuration
public class EntityManagerFactoriesConfiguration {
    @Autowired
    private DataSource dataSource;

    @Bean(name = "entityManagerFactory")
    public LocalContainerEntityManagerFactoryBean emf() {
        LocalContainerEntityManagerFactoryBean emf = ...
        emf.setDataSource(dataSource);
        emf.setPackagesToScan(
            new String[] {"your.package"});
        emf.setJpaVendorAdapter(
            new HibernateJpaVendorAdapter());
        return emf;
    }
}

下一步实现配置事务管理器和在@Transactional注解的类中应用事务的切面。

@Configuration
@EnableTransactionManagement
public class TransactionManagersConfig {
    @Autowired
    EntityManagerFactory emf;
    @Autowired
    private DataSource dataSource;

    @Bean(name = "transactionManager")
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager tm =
            new JpaTransactionManager();
            tm.setEntityManagerFactory(emf);
            tm.setDataSource(dataSource);
        return tm;
    }
}

注解@EnableTransactionManagement通知Spring,@Transactional注解的类被事务的切面 包围。这样@Transactional就可以使用了。

常见错误

  • MySQL引擎必须是InnoDB才能启用事务。
  • 方法上需要使用@Transactional才能开启事务,事务必须是public方法
  • @Transactional除了可以用于方法,还可以用于类,表示这个类所有的public方法都会配置事务。
  • 必须是运行期的异常才会触发回滚。
  • 多个数据源配置或者多个事务管理器的时候,注意如果操作数据库A,不能使用B的事务,虽然这个问题很幼稚,但是有时候用错难查找问题。
  • 如果在Spring中,需要配置@EnableTransactionManagement来开启事务,等同于配置xml文件<tx:annotation-driven/>,但是在Springboot中已经不需要了,在springboot中SpringBootApplication注解包含了@EnableAutoConfiguration注解,会自动注入。
  • 事务方法不能在同个类里面调用。Spring用切面对方法进行包装,只对外部调用方法进行拦截,内部方法没有进行拦截。
  • 使用new Thread(...).start新建线程内的事务失效。因为不同的线程使用的是不同SqlSession,相当于另外一个连接,根本不会用到同一个事务。

总结

Spring声明式事务管理机制非常强大,但它可能被误用或者容易发生配置错误。

当这个机制不能正常工作或者未达到预期运行结果等问题出现时,理解它的内部工作情况 是很有帮助的。

需要记住的最重要的一点是,要考虑到两个概念:事务和持久化上下文,每个都有自己 不可读的明显的生命周期。

Spring.quartz

最近在研究Spring中的定时任务功能,最好的办法当然是使用Quartz来实现。对于一个新手来说,花了我不少时间,这里我写个笔记,给大家参考。 我使用的是Maven来管理项目,需要的Jar包我给大家贴出来。

quartz-1.8.5.jar
commons-logging.jar
spring-core-3.0.5.RELEASE.jar
spring-beans-3.0.5.RELEASE.jar
spring-context-3.0.5.RELEASE.jar
spring-context-support-3.0.5.RELEASE.jar
spring-asm-3.0.5.RELEASE.jar
spring-expression-3.0.5.RELEASE.jar
spring.transaction-3.0.5.RELEASE.jar
spring-web-3.0.5.RELEASE.jar
Maven的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>QtzTest</groupId>  
        <artifactId>QtzTest</artifactId>  
        <version>1.0</version>  
      
        <properties>  
            <springframework.version>3.0.5.RELEASE</springframework.version>  
        </properties>  
      
        <dependencies>  
            <dependency>  
                <groupId>org.springframework</groupId>  
                <artifactId>spring-context</artifactId>  
                <version>${springframework.version}</version>  
            </dependency>  
      
            <dependency>  
                <groupId>org.springframework</groupId>  
                <artifactId>spring-context-support</artifactId>  
                <version>${springframework.version}</version>  
            </dependency>  
      
            <dependency>  
                <groupId>org.springframework</groupId>  
                <artifactId>spring-tx</artifactId>  
                <version>${springframework.version}</version>  
            </dependency>  
      
            <dependency>  
                <groupId>org.springframework</groupId>  
                <artifactId>spring-web</artifactId>  
                <version>${springframework.version}</version>  
            </dependency>  
      
            <dependency>  
                <groupId>org.quartz-scheduler</groupId>  
                <artifactId>quartz</artifactId>  
                <version>1.8.5</version>  
            </dependency>  
        </dependencies>  
      
        <build>  
            <finalName>${project.artifactId}</finalName>  
            <plugins>  
                <plugin>  
                    <groupId>org.mortbay.jetty</groupId>  
                    <artifactId>jetty-maven-plugin</artifactId>  
                    <version>7.5.4.v20111024</version>  
                    <configuration>  
                        <scanIntervalSeconds>10</scanIntervalSeconds>  
                        <webApp>  
                            <contextPath>/${project.artifactId}</contextPath>  
                        </webApp>  
                    </configuration>  
                </plugin>  
            </plugins>  
        </build>  
    </project>  

特别注意一点,与Spring3.1以下版本整合必须使用Quartz1,最初我拿2.1.3的,怎么搞都报错:

Caused by: org.springframework.beans.factory.CannotLoadBeanClassException: Error loading class [org.springframework.scheduling.quartz.CronTriggerBean] for bean with name 'mytrigger' defined in class path resource [applicationContext.xml]: problem with class file or dependent class; nested exception is java.lang.IncompatibleClassChangeError: class org.springframework.scheduling.quartz.CronTriggerBean has interface org.quartz.CronTrigger as super class

查看发现spring3.0.5中org.springframework.scheduling.quartz.CronTriggerBean继承了org.quartz.CronTrigger(public class CronTriggerBeanextends CronTrigger),而在quartz2.1.3中org.quartz.CronTrigger是个接口(publicabstract interface CronTrigger extends Trigger),而在quartz1.8.5及1.8.4中org.quartz.CronTrigger是个类(publicclass CronTrigger extends Trigger),从而造成无法在applicationContext中配置触发器。这是spring3.1以下版本和quartz2版本不兼容的一个bug。(感谢tiren的回复,spring3.1以及以后版本支持quartz2)

在Spring中使用Quartz有两种方式实现:第一种是任务类继承QuartzJobBean,第二种则是在配置文件里定义任务类和要执行的方法,类和方法仍然是普通类。很显然,第二种方式远比第一种方式来的灵活。

第一种方式的JAVA代码:

Java代码 收藏代码

    package com.ncs.hj;  
      
    import org.quartz.JobExecutionContext;  
    import org.quartz.JobExecutionException;  
    import org.springframework.scheduling.quartz.QuartzJobBean;  
      
    public class SpringQtz extends QuartzJobBean{  
        private static int counter = 0;  
        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {  
            System.out.println();  
            long ms = System.currentTimeMillis();  
            System.out.println("\t\t" + new Date(ms));  
            System.out.println(ms);  
            System.out.println("(" + counter++ + ")");  
            String s = (String) context.getMergedJobDataMap().get("service");  
            System.out.println(s);  
            System.out.println();  
        }  
    }  

第二种方式的JAVA代码: Java代码 收藏代码

    package com.ncs.hj;  
      
    import org.quartz.JobExecutionContext;  
    import org.quartz.JobExecutionException;  
    import org.springframework.scheduling.quartz.QuartzJobBean;  
      
    import java.util.Date;  
      
    public class SpringQtz {  
        private static int counter = 0;  
        protected void execute()  {  
            long ms = System.currentTimeMillis();  
            System.out.println("\t\t" + new Date(ms));  
            System.out.println("(" + counter++ + ")");  
        }  
    }  

Spring的配置文件: Xml代码 收藏代码

    <!------------ 配置调度程序quartz ,其中配置JobDetail有两种方式-------------->    
        <!--方式一:使用JobDetailBean,任务类必须实现Job接口 -->     
        <bean id="myjob" class="org.springframework.scheduling.quartz.JobDetailBean">    
         <property name="name" value="exampleJob"></property>    
         <property name="jobClass" value="com.ncs.hj.SpringQtz"></property>   
         <property name="jobDataAsMap">  
    <map>  
        <entry key="service"><value>simple is the beat</value></entry>  
    </map>  
    ;/property>  
        </bean>   
        <!--运行时请将方式一注释掉! -->    
        <!-- 方式二:使用MethodInvokingJobDetailFactoryBean,任务类可以不实现Job接口,通过targetMethod指定调用方法-->    
        <!-- 定义目标bean和bean中的方法 -->  
        <bean id="SpringQtzJob" class="com.ncs.hj.SpringQtz"/>  
        <bean id="SpringQtzJobMethod" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
        <property name="targetObject">  
            <ref bean="SpringQtzJob"/>  
        </property>  
        <property name="targetMethod">  <!-- 要执行的方法名称 -->  
            <value>execute</value>  
        </property>  
    </bean>  
      
    <!-- ======================== 调度触发器 ======================== -->  
    <bean id="CronTriggerBean" class="org.springframework.scheduling.quartz.CronTriggerBean">  
        <property name="jobDetail" ref="SpringQtzJobMethod"></property>  
        <property name="cronExpression" value="0/5 * * * * ?"></property>  
    </bean>  
      
    <!-- ======================== 调度工厂 ======================== -->  
    <bean id="SpringJobSchedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
        <property name="triggers">  
            <list>  
                <ref bean="CronTriggerBean"/>  
            </list>  
        </property>  
    </bean>    

关于cronExpression表达式,这里讲解一下:

字段 允许值 允许的特殊字符
秒 0-59 , - * /
分 0-59 , - * /
小时 0-23 , - * /
日期 1-31 , - * ? / L W C
月份 1-12 或者 JAN-DEC , - * /
星期 1-7 或者 SUN-SAT , - * ? / L C #
年(可选) 留空, 1970-2099 , - * /
表达式意义
"0 0 12 * * ?" 每天中午12点触发
"0 15 10 ? * *" 每天上午10:15触发
"0 15 10 * * ?" 每天上午10:15触发
"0 15 10 * * ? *" 每天上午10:15触发
"0 15 10 * * ? 2005" 2005年的每天上午10:15触发
"0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
"0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
"0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
"0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
"0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
"0 15 10 15 * ?" 每月15日上午10:15触发
"0 15 10 L * ?" 每月最后一日的上午10:15触发
"0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发
"0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发
每天早上6点
0 6 * * *
每两个小时
0 */2 * * *
晚上11点到早上8点之间每两个小时,早上八点
0 23-7/2,8 * * *
每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点
0 11 4 * 1-3
1月1日早上4点
0 4 1 1 *

最后别忘了在web.xml里面配置Spring:

    <?xml version="1.0" encoding="UTF-8"?>  
    <web-app xmlns="http://java.sun.com/xml/ns/javaee"  
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee  
              http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
             version="2.5">  
        <welcome-file-list>  
            <welcome-file>index.html</welcome-file>  
        </welcome-file-list>  
      
        <context-param>  
            <param-name>contextConfigLocation</param-name>  
            <param-value>/WEB-INF/spring-config.xml</param-value>  
        </context-param>  
      
        <listener>  
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
        </listener>  
    </web-app>  

运行结果:

Wed Feb 08 13:58:30 CST 2012
(0)
Wed Feb 08 13:58:35 CST 2012
(1)
Wed Feb 08 13:58:40 CST 2012
(2)
Wed Feb 08 13:58:45 CST 2012
(3)
Wed Feb 08 13:58:50 CST 2012
(4)
Wed Feb 08 13:58:55 CST 2012
(5)
Wed Feb 08 13:59:00 CST 2012
(6) 

spring 4 quartz 2.2

一、首先加入spring(4.1.9.RELEASE)的依赖包,然后再加入quartz(2.2.1)的包,如下:

Xml代码 收藏代码

<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.1</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.2.1</version> </dependency>

二、配置spring.xml文件

spring.xml文件可能有人喜欢用applicationContext.xml命名,这个是Spring的主配置文件,非springMvc.xml

1、配置定时任务Java类

Xml代码 收藏代码

<!-- 配置任务bean类 --> <bean id="billsCheckJob" class="com.chinagas.biz.task.BillsCheckJob"></bean>

Java类代码如下:

Java代码 收藏代码

public class BillsCheckJob{ private Logger log = Logger.getLogger(BillsCheckJob.class); public void runTask(){ log.info("===========runTask()"); } }

也可以采用注解方式,这样Bean就不用在xml配置了,看个人喜好吧: Java代码 收藏代码

@Component("billsCheckJob") public class BillsCheckJob{ private Logger log = Logger.getLogger(BillsCheckJob.class);
public void runTask(){ log.info("===========runTask()"); } }

2、配置定时任务详细jobDetail,其中有对应的任务执行类:ref="billsCheckJob",及里面的执行方法:runTask

方法一、方法二只需要选择一种。

方法一:方法映射

Xml代码 收藏代码

<!-- 配置方法映射工厂类 --> <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="billsCheckJob"></property> <property name="targetMethod" value="runTask"></property> <property name="concurrent" value="false"></property> <!-- concurrent : false表示等上一个任务执行完后再开启新的任务 --> </bean>

方法二:继承QuartzJobBean类,实现executeInternal方法

Xml代码 收藏代码

<bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <property name="jobClass" value="com.chinagas.biz.task.ExtendsJob"> </property> <property name="durability" value="true"></property> </bean>

ExtendsJob类如下: Java代码 收藏代码

public class ExtendsJob extends QuartzJobBean{ private Logger log = Logger.getLogger(ExtendsJob.class); @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { log.info("===========ExtendsJob runTask()"); } }

3、配置定时任务的执行时间或周期,同时关联任务详细jobDetail

方法一、方法二只需要选择一种。

(1)方法一:

使用CronTriggerFactoryBean类配置,这个方法的好处在于可以使用cronExpression表达式。

Xml代码 收藏代码

<!-- 配置任务高度的的时间/周期 --> <bean id="billsCheckJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail" ref="jobDetail"></property> <property name="cronExpression" value="0 */1 * * * ?"></property> <property name="startDelay" value="3000"></property> </bean>

(2)方法二

Xml代码 收藏代码

<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> <property name="jobDetail" ref="jobDetail"/> <property name="startDelay" value="5000"/> <property name="repeatInterval" value="5000"/> </bean>

4、配置SchedulerFactoryBean类,调用时间触发

Xml代码 收藏代码

<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <!-- <ref bean="billsCheckJobTrigger"/> --> <ref bean="simpleTrigger"/> </list> </property> </bean>

配置完后就可以启动看效果了。

5、cronExpression表达式

cron的表达式被用来配置CronTrigger实例。 cron的表达式是字符串,实际上是由七子表达式,描述个别细节的时间表。这些子表达式是分开的空白,代表:

1. Seconds 2. Minutes 3. Hours 4. Day-of-Month 5. Month 6. Day-of-Week 7. Year (可选字段)

例 "0 0 12 ? * WED" 在每星期三下午12:00 执行,

个别子表达式可以包含范围, 例如,在前面的例子里("WED")可以替换成 "MON-FRI", "MON, WED, FRI"甚至"MON-WED,SAT".

「*」 代表整个时间段.

每一个字段都有一套可以指定有效值,如

Seconds (秒) :可以用数字0-59 表示,

Minutes(分) :可以用数字0-59 表示,

Hours(时) :可以用数字0-23表示,

Day-of-Month(天) :可以用数字1-31 中的任一一个值,但要注意一些特别的月份

Month(月) :可以用0-11 或用字符串 「JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC」 表示

Day-of-Week(每周):可以用数字1-7表示(1 = 星期日)或用字符口串「SUN, MON, TUE, WED, THU, FRI and SAT」表示

「/」:为特别单位,表示为「每」如「0/15」表示每隔15分钟执行一次,「0」表示为从「0」分开始, 「3/20」表示表示每隔20分钟执行一次,「3」表示从第3分钟开始执行

「?」:表示每月的某一天,或第周的某一天

「L」:用于每月,或每周,表示为每月的最后一天,或每个月的最后星期几如「6L」表示「每月的最后一个星期五」

「W」:表示为最近工作日,如「15W」放在每月(day-of-month)字段上表示为「到本月15日最近的工作日」

「#」:是用来指定「的」每月第n个工作日,例 在每周(day-of-week)这个字段中内容为"6#3" or "FRI#3" 则表示「每月第三个星期五」

1)Cron表达式的格式:秒 分 时 日 月 周 年(可选)。

字段名 允许的值 允许的特殊字符 秒 0-59 , - * / 分 0-59 , - * / 小时 0-23 , - * / 日 1-31 , - * ? / L W C 月 1-12 or JAN-DEC , - * / 周几 1-7 or SUN-SAT , - * ? / L C # 年 (可选字段) empty, 1970-2099 , - * /
「?」字符:表示不确定的值
「,」字符:指定数个值
「-」字符:指定一个值的范围
「/」字符:指定一个值的增加幅度。n/m表示从n开始,每次增加m
「L」字符:用在日表示一个月中的最后一天,用在周表示该月最后一个星期X
「W」字符:指定离给定日期最近的工作日(周一到周五)
「#」字符:表示该月第几个周X。6#3表示该月第3个周五
2)Cron表达式范例:
每隔5秒执行一次:*/5 * * * * ?
每隔1分钟执行一次:0 */1 * * * ?
每天23点执行一次:0 0 23 * * ?
每天凌晨1点执行一次:0 0 1 * * ?
每月1号凌晨1点执行一次:0 0 1 1 * ?
每月最后一天23点执行一次:0 0 23 L * ?
每周星期天凌晨1点实行一次:0 0 1 ? * L
在26分、29分、33分执行一次:0 26,29,33 * * * ?
每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?

6、官网文档

官方quartz的Jar包下载地址:

http://www.quartz-scheduler.org/downloads/

Spring官方配置文档:

http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#scheduling-quartz