`
resp
  • 浏览: 156267 次
  • 性别: Icon_minigender_1
  • 来自: 湖南长沙
社区版块
存档分类
最新评论

关于Spirng Quartz定时触发器+源码示例!

    博客分类:
  • SSH
阅读更多

    最近实现了一个业务,刚好需要用到Spring 触发器,特写了一个Spring 触发器Demo供大家学习参考!

     Demo简单的实现了定时报时功能,通过Spring 触发器实现每分钟报一次时间!

Demo结构如下:



 运行结果如下:


 

简单解释一下Quartz:

    Quartz调度器为调度工作提供了更丰富的支持。和Java定时器一样,可以使用Quartz来每隔多少毫秒执行一个工作。但Quartz比Java Timer更先进之处在于它允许你调度一个工作在某个特定的时间或日期执行。关于Quartz的更多信息,可以访问Quartz位于http://www.opensymphony.com/quartz的主页。


在简单介绍下创建触发器的步骤:

    首先需要创建一个工作类,这个类必须继承Spring的QuartzJobBean

package jobs;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
import service.ITellingTheTimeService;


/**
 * @ProjectName:报时Demo   
 * @ClassName:TellingTheTimeJob   
 * @Description:   
 * @author:Sheep
 * @date:2012-4-19 下午03:58:11   
 * @Modifier: 
 * @Modify Date:  
 * @Modify Note:   
 * @version
 */
public class TellingTheTimeJob extends QuartzJobBean {
	
	private ITellingTheTimeService tellingTheTimeService = null;

	@Override
	protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
		//调用报时方法
		this.tellingTheTimeService.tellingTheTime();
	}

	public ITellingTheTimeService getTellingTheTimeService() {
		return tellingTheTimeService;
	}

	public void setTellingTheTimeService(
			ITellingTheTimeService tellingTheTimeService) {
		this.tellingTheTimeService = tellingTheTimeService;
	}
}

     QuartzJobBean是Quartz中与Java的TimerTask等价的类。它实现了org.quartz.Job接口。executeInternal()方法定义了当预定的时刻来临时应该执行哪些动作。


接着我们须在Sping配置文件中声明这个Job。

<!-- 配置一个Job -->
	<bean id="tellTheTimeJob" class="org.springframework.scheduling.quartz.JobDetailBean">
		<property name="jobClass" value="jobs.TellingTheTimeJob"/>
		<property name="jobDataAsMap">
			<map>
				<entry key="tellingTheTimeService" value-ref="tellingTheTimeService"></entry>
			</map>
		</property>
	</bean>

    JobDetailBean 是Quartz的org.quartz.JobDetail的子类,它要求通过jobClass属性来设置一个Job对象。
    使用Quartz的JobDetail中的另一个特别之处是TellingTheTimeJob 的tellingTheTimeService 属性是间接设置的。JobDetail的jobDataAsMap属性接受一个java.util.Map,其中包含了需要设置给jobClass的各种属性。在这里,这个map包含了一个指向tellingTheTimeService的引用,它的键值为tellingTheTimeService 。当JobDetailBean实例化时,它会将tellingTheTimeService注入到TellingTheTimeJob 的tellingTheTimeService属性中。


    一个Job定义好了,那么我们怎么来实现触发器来调用这个Job呢?接着我们需要在Sping配置触发器。

    Spring提供了两个触发器,SimpleTriggerBean和CronTriggerBean。

    我们现在看看SimpleTriggerBean是怎么配置的,如下:

<!-- 简单的触发器 -->
	<bean id="simpleTellTheTimeTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
		<property name="jobDetail">
			<ref bean="tellTheTimeJob" />
		</property>
		<!-- 以毫秒为单位,启动后一分钟触发 -->
		<property name="startDelay">
			<value>60000</value>
		</property>
		<!-- 每间隔一分钟触发一次 -->
		<property name="repeatInterval">
			<value>60000</value>
		</property>
	</bean>

    SimpleTriggerBean与ScheduledTimerTask类似。你可以用它来指定一个工作应该以怎样的频度运行,以及(可选地)在第一次运行工作之前应该等待多久。其中,jobDetail 属性告诉触发器调度哪个工作。

    我们再来看看CronTriggerBean是怎么配置的,如下:

<!-- 复杂的触发器 -->
	<bean id="complexTellTheTimeTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
		<property name="jobDetail">
			<ref bean="tellTheTimeJob"/>
		</property>
		<property name="cronExpression">
			<!-- 这里是重点,可以自定义表达式实现定时触发。以下含义是每分钟触发一次 -->
			<value>0 0/1 * * * ?</value>
		</property>
	</bean>

    和SimpleTriggerBean一样,jobDetail属性告诉触发器调度哪个工作。属性cronExpression告诉触发器何时触发。我们可以通过配置cronExpression属性的value值来实现赋值的触发逻辑。

    一个cron表达式有至少6个(也可能是7个)由空格分隔的时间元素。从左至右,这些元素的定义如下:

1.秒(0–59)
2.分钟(0–59)
3.小时(0–23)
4.月份中的日期(1–31)
5.月份(1–12或JAN–DEC)
6.星期中的日期(1–7或SUN–SAT)
7.年份(1970–2099)

    每一个元素都可以显式地规定一个值(如6),一个区间(如9-12),一个列表(如9,11,13)或一个通配符(如*)。“月份中的日期”和“星期中的日期”这两个元素是互斥的,因此应该通过设置一个问号(?)来表明你不想设置的那个字段。表7.1中显示了一些cron表达式的例子和它们的意义:

一些cron表达式的例子
表   达   式                 意     义
0 0 10,14,16 * * ?                     每天上午10点,下午2点和下午4点
0 0,15,30,45 * 1-10 * ?                每月前10天每隔15分钟
30 0 0 1 1 ? 2012                      在2012年1月1日午夜过30秒


   到了这里所需的一切都准备好了,那么如果启动这个触发器呢?我们需要在Sping XML如下配置:

<!-- Spring触发工厂 -->
	<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
		<property name="triggers">
			<list>
				<ref bean="complexTellTheTimeTrigger"/>
				<!-- ....下面可以继续添加其他触发器 -->
			</list>
		</property>
	</bean>

   Spring的SchedulerFactoryBean 是Quartz中与TimerFactoryBean等价的类。属性triggers接受一组触发器。因此只需简单地装配一个包含complexTellTheTimeTrigger的一个引用的即可。


    这里就实现了每分钟报一次时间的业务需求,下面附上源码(包含了所需的Jar包)供大家学习参考。

 

  • 大小: 6 KB
  • 大小: 41.7 KB
  • 大小: 18.1 KB
分享到:
评论
15 楼 sky_lc 2015-10-13  
我这里为啥部署上之后啥反映也没有啊~~新手,对这个不太懂,没有看到调用入口啊?是不是部署到tomcat之后就可以了?
13 楼 yahier 2015-01-10  
还是挺好的,就是简单触发器不起效
12 楼 全保生 2013-11-14  
很好,简单易懂。
11 楼 你有罪 2013-05-15  
楼主的教程写的太好了!正好要用!!! 
10 楼 resp 2012-09-17  
CNTMDCSDN 写道
楼主,出问题了:



信息: Starting Servlet Engine: Apache Tomcat/6.0.13
2012-9-14 21:06:34 org.apache.catalina.core.StandardContext listenerStart
严重: Error configuring application listener of class org.springframework.web.context.ContextLoaderListener
java.lang.NoClassDefFoundError: org/springframework/context/ApplicationContextException
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2389)
at java.lang.Class.getConstructor0(Class.java:2699)
at java.lang.Class.newInstance0(Class.java:326)
at java.lang.Class.newInstance(Class.java:308)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3771)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4334)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:791)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:771)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:525)
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:920)
at org.apache.catalina.startup.HostConfig.deployDirectories(HostConfig.java:883)
at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:492)
at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1138)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:311)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:719)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
at org.apache.catalina.core.StandardService.start(StandardService.java:516)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
at org.apache.catalina.startup.Catalina.start(Catalina.java:566)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)
Caused by: java.lang.ClassNotFoundException: org.springframework.context.ApplicationContextException
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1358)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1204)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 29 more
2012-9-14 21:06:34 org.apache.catalina.core.StandardContext listenerStart
严重: Skipped installing application listeners due to previous error(s)
2012-9-14 21:06:34 org.apache.catalina.core.StandardContext start
严重: Error listenerStart
2012-9-14 21:06:34 org.apache.catalina.core.StandardContext start
严重: Context [/timerDemo] startup failed due to previous errors
2012-9-14 21:06:34 org.apache.coyote.http11.Http11Protocol start
信息: Starting Coyote HTTP/1.1 on http-8080
2012-9-14 21:06:34 org.apache.jk.common.ChannelSocket init
信息: JK: ajp13 listening on /0.0.0.0:8009
2012-9-14 21:06:34 org.apache.jk.server.JkMain start
信息: Jk running ID=0 time=0/15  config=null
2012-9-14 21:06:34 org.apache.catalina.startup.Catalina start
信息: Server startup in 456 ms


换个Tomcat试试,别把包忘记了!
9 楼 CNTMDCSDN 2012-09-14  
楼主,出问题了:



信息: Starting Servlet Engine: Apache Tomcat/6.0.13
2012-9-14 21:06:34 org.apache.catalina.core.StandardContext listenerStart
严重: Error configuring application listener of class org.springframework.web.context.ContextLoaderListener
java.lang.NoClassDefFoundError: org/springframework/context/ApplicationContextException
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2389)
at java.lang.Class.getConstructor0(Class.java:2699)
at java.lang.Class.newInstance0(Class.java:326)
at java.lang.Class.newInstance(Class.java:308)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3771)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4334)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:791)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:771)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:525)
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:920)
at org.apache.catalina.startup.HostConfig.deployDirectories(HostConfig.java:883)
at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:492)
at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1138)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:311)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:719)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
at org.apache.catalina.core.StandardService.start(StandardService.java:516)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
at org.apache.catalina.startup.Catalina.start(Catalina.java:566)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)
Caused by: java.lang.ClassNotFoundException: org.springframework.context.ApplicationContextException
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1358)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1204)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 29 more
2012-9-14 21:06:34 org.apache.catalina.core.StandardContext listenerStart
严重: Skipped installing application listeners due to previous error(s)
2012-9-14 21:06:34 org.apache.catalina.core.StandardContext start
严重: Error listenerStart
2012-9-14 21:06:34 org.apache.catalina.core.StandardContext start
严重: Context [/timerDemo] startup failed due to previous errors
2012-9-14 21:06:34 org.apache.coyote.http11.Http11Protocol start
信息: Starting Coyote HTTP/1.1 on http-8080
2012-9-14 21:06:34 org.apache.jk.common.ChannelSocket init
信息: JK: ajp13 listening on /0.0.0.0:8009
2012-9-14 21:06:34 org.apache.jk.server.JkMain start
信息: Jk running ID=0 time=0/15  config=null
2012-9-14 21:06:34 org.apache.catalina.startup.Catalina start
信息: Server startup in 456 ms
8 楼 Wentasy 2012-07-15  
gupeipei912 写道
嗯,问题已解决,是Spring jar包的问题!

请问是怎么解决的?
7 楼 gupeipei912 2012-06-25  
嗯,问题已解决,是Spring jar包的问题!
6 楼 gupeipei912 2012-06-25  
严重: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
java.lang.NoSuchMethodError: org.springframework.web.context.ConfigurableWebApplicationContext.setId(Ljava/lang/String;)V
at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:264)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:197)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:47)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4206)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4705)
at org.apache.catalina.startup.HostConfig.checkResources(HostConfig.java:1284)
at org.apache.catalina.startup.HostConfig.check(HostConfig.java:1382)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:306)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:142)
at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1389)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1653)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1662)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1642)
at java.lang.Thread.run(Thread.java:619)
2012-6-25 10:40:56 org.apache.catalina.core.StandardContext start
严重: Error listenerStart
2012-6-25 10:40:56 org.apache.catalina.core.StandardContext start
严重: Context [/timerDemo2] startup failed due to previous errors
2012-6-25 10:40:56 org.apache.catalina.core.ApplicationContext log
信息: Closing Spring root WebApplicationContext

怎么报这个错?帮忙解决一下!谢了!
5 楼 gupeipei912 2012-06-25  
LZ你那个后台运行在哪个类里面运行的,我怎么只打印一次,好像触发器每起作用!
4 楼 qq359907964 2012-05-31  
把spring3的包都导入进去就不报错了
3 楼 qq359907964 2012-05-31  
java.lang.NoClassDefFoundError: org/springframework/context/ApplicationContextException  报错啊...
2 楼 resp 2012-04-23  
shizhangliao 写道
怎么抱这个错误呢?
严重: Error configuring application listener of class org.springframework.web.context.ContextLoaderListener
java.lang.NoClassDefFoundError: org/springframework/context/ApplicationContextException


不会吧,请问你用的JDK是什么版本?
1 楼 shizhangliao 2012-04-21  
怎么抱这个错误呢?
严重: Error configuring application listener of class org.springframework.web.context.ContextLoaderListener
java.lang.NoClassDefFoundError: org/springframework/context/ApplicationContextException

相关推荐

    Quartz+Spring定时触发器例子

    在本示例中,“Quartz+Spring定时触发器例子”是一个完整的项目,包含了所有必要的jar包,这意味着你可以直接运行它来学习和理解Quartz与Spring的集成。这个项目应该包含以下组件: 1. **配置文件**:可能包含一个...

    spring quartz实现触发器demo(笔记)

    这篇笔记将深入探讨如何使用Spring与Quartz结合来实现触发器的示例。 首先,Quartz是一个开源的作业调度框架,它允许开发者定义任务并在预定的时间点执行。Spring则是一个强大的应用框架,提供了依赖注入(DI)和...

    Spring+Quartz2.2.0+Demo源码实战演练【精华篇2014】

    - `SpringQuartz`项目中的`pom.xml`文件会列出所有依赖,包括Spring和Quartz的相关库。 - 将项目导入MyEclipse后,运行主类启动应用,然后观察任务是否按预期执行。 7. **注意事项**: - 配置Quartz时,确保...

    spring quartz 表达式在线生成器

    通常,这样的示例会包含Spring配置文件(如`applicationContext.xml`或`application.yml`)、Java配置类、定时任务类(实现`org.quartz.Job`接口)以及触发器配置(使用`org.quartz.Trigger`)。 总的来说,了解和...

    spring多个定时任务quartz配置

    在Spring框架中,Quartz是一个强大的任务调度库,可以用于执行定时任务。本文将深入探讨如何在Spring中配置Quartz以实现多个定时任务。 首先,我们需要理解Quartz的基本概念。Quartz是一个开源的工作调度框架,它...

    spring quartz 整合示例

    这个压缩包文件提供了一个实际的示例,用于演示如何在Spring应用中集成Quartz进行任务调度。下面将详细解释其中涉及的知识点。 首先,Quartz是一个开源的作业调度框架,它允许开发者创建、调度和管理重复的任务。在...

    spring2.0 Quartz 执行每天定时任务 普通普是执行任务

    标题中的“spring2.0 Quartz 执行每天定时任务 普通普是执行任务”涉及到的是在Spring 2.0框架中使用Quartz库来创建并执行每天定时任务的场景。Quartz是一款强大的开源作业调度框架,它可以用来按计划执行各种任务,...

    quartz和spring-quartz

    Quartz和Spring-Quartz是两个在Java世界中广泛使用的定时任务管理框架。Quartz是一个开源的作业调度框架,允许应用程序定义和调度任务在特定时间执行。而Spring-Quartz则是Spring框架对Quartz的集成,它使得在Spring...

    spring3.2+quartz2+maven

    标题 "spring3.2+quartz2+maven" 指的是一个集成项目,它将Spring框架3.2版本、Quartz作业调度库2.0版本以及Maven构建工具结合在一起,用于创建一个具备定时任务功能的应用。在这个项目中,Spring提供依赖注入和管理...

    基于Spring的Quartz动态定时任务增删改查源码.rar

    本资源"基于Spring的Quartz动态定时任务增删改查源码.rar"提供了一套完整的示例,帮助开发者了解如何在Spring环境中集成Quartz来实现动态管理定时任务。 首先,Quartz的核心概念包括Job(任务)、Trigger(触发器)...

    Quartz集群+spring data

    至于博文链接中的内容,虽然无法直接查看,但通常会包含如何在实际项目中设置Quartz集群和Spring Data集成的详细步骤,包括配置示例、代码示例以及可能遇到的问题和解决方案。 在使用过程中,可能会遇到如任务并发...

    Quartz与Spring整合练习源代码

    将Quartz与Spring整合,可以方便地管理和控制定时任务,同时利用Spring的依赖注入和管理功能,提高代码的可维护性和可测试性。 Quartz的核心概念包括Job、Trigger和Scheduler。Job是实际需要执行的任务,Trigger是...

    spring2与quartz在Web整合

    通过查看源码,我们可以学习如何配置 Spring 和 Quartz,如何定义 Job 和 Trigger,以及如何在 Tomcat 下部署和运行应用。 7. **最佳实践**:为了优化性能和可维护性,推荐将 Quartz 的配置和作业定义分离,使得...

    quartz 定时任务

    通过这个"spring+quartz demo"源码,你可以深入学习如何在Spring应用中配置和使用Quartz,以及如何将它与Spring的其他功能(如依赖注入)结合。这将帮助你构建更复杂、可扩展的定时任务系统。记得下载后仔细阅读代码...

    spring quartz学习总结: cluster的配置和示例

    本文将深入探讨Spring与Quartz集成时如何配置集群以及提供一个示例。 1. **Quartz集群的基本概念** - **Job**:任务,是执行的具体逻辑。 - **Trigger**:触发器,决定何时执行Job。 - **Scheduler**:调度器,...

    Quartz 定时任务web使用

    Quartz 是一个开源的作业调度框架,常用于Java应用程序中实现定时任务的管理。它提供了丰富的API和功能,使得开发者可以灵活地定义和控制任务的执行。本篇将重点介绍如何在Web环境中集成并使用Quartz,以及相关的...

    定时器(quartz+spring)读取数据库配置

    在提供的压缩包文件“quartz项目资料”中,可能包含了关于如何设置Quartz与Spring集成的详细示例代码、数据库脚本、配置文件等,你可以进一步学习和参考。通过实践这些资料,你将能够更好地理解和掌握如何利用Quartz...

    Spring3.2.3+Quartz2.2 整合配置

    在IT行业中,Spring框架是Java企业级应用开发的首选,而Quartz则是一个强大的作业调度框架,常用于执行定时任务。本篇文章将详细介绍如何将Spring 3.2.3版本与Quartz 2.2版本进行整合配置,以便在Spring应用中实现...

    spring集成quartz集群配置

    以下是一个简单的Spring配置示例,展示了如何集成Quartz和数据库: ```xml &lt;bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailBean"&gt; &lt;bean id="cronTrigger" class="org.spring...

    spring quartz集群配置

    在分布式系统中,定时任务的管理往往是一个重要的环节,Spring与Quartz的结合能够帮助我们实现复杂的工作调度。Quartz是一个开源的作业调度框架,它提供了高度可配置的定时任务执行能力。本文将深入探讨如何在Spring...

Global site tag (gtag.js) - Google Analytics