Loading...
My Blog 我的工作和学习笔记

通过Spring Data MongoDB向mongoDB GridFs 存储图片

mongoDB笔记 2016/01/18 Spring Framework , MongoDB

开篇说明

  GridFS会将大文件对象分割成多个小的chunk(文件片段),一般为256k/个,每个chunk将作为mongodb的一个文档(document)被存储在chunks集合中..gridfs模块会为每个文件创建chunks和files信息.每个文件的实际内容被存在chunks(二进制数据)中,和文件有关的meta数据(filename,content_type,还有用户自定义的属性)将会被存在files集合中.files集合中的文档就是BSON格式,可以使用mongodb的索引等等特性。

    对于GridFS文件的web访问方式,大家可以搜索一下Gridfs + nginx,既可获取相应的信息..


具体步骤

1、在applicationContext-mongodb.xml配置文件中添加GridFsTemplate配置:

	<!-- GridFsTemplate -->
	<mongo:mapping-converter id="converter" />
	<bean class="org.springframework.data.mongodb.gridfs.GridFsTemplate">
	    <constructor-arg ref="mongoDbFactory" />
	    <constructor-arg ref="converter" />
	    <constructor-arg value="imethan_sys_fs" /><!-- 指定GridFS Collections名称 -->
	</bean>


2、创建文件存取测试客户端,代码如下:

package cn.imethan.mongodb;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsOperations;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.gridfs.GridFSFile;

/**
 * GridFsClient.java
 *
 * @author Ethan Wong
 * @since JDK 1.7
 * @datetime 2016年1月18日下午3:03:02
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/main/applicationContext.xml", "classpath:/mongodb/applicationContext-mongodb.xml" })
public class GridFsClient {
	
	@Autowired
	private GridFsOperations gridFsOperations;
	
	/**
	 * 保存测试
	 * 
	 * @author Ethan Wong
	 * @datetime 2016年1月18日下午3:51:28
	 */
	@Test
	public void storeFileToGridFs() {
		DBObject metaData = new BasicDBObject();
		metaData.put("extra1", "anything 1");
		metaData.put("extra2", "anything 2");
		InputStream inputStream = null;
		try {
			inputStream = new FileInputStream("C:/Users/ETHAN/Pictures/e.png");
			GridFSFile gridFSFile = gridFsOperations.store(inputStream, "e.png", "image/png", metaData);
			System.out.println("gridFSFile:"+gridFSFile.toString());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		System.out.println("Done");
	}
	
	/**
	 * 读取测试
	 * 
	 * @author Ethan Wong
	 * @datetime 2016年1月18日下午3:51:28
	 */
	@Test
	public void findFilesInGridFs() {
		List<GridFSDBFile> result = gridFsOperations.find(new Query().addCriteria(Criteria.where("filename").is("e.png")));
		for (GridFSDBFile file : result) {
			try {
				System.out.println(file.getFilename());
				System.out.println(file.getContentType());
				//save as another image
				file.writeTo("C:/Users/ETHAN/Pictures/e1.png");
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		System.out.println("Done");
	}
}


3、登录mongoDB查看结果

db.getCollection('imethan_sys_file.files').find({})

/* 1 */
{
    "_id" : ObjectId("569c9831c6a346b44944e6e8"),
    "chunkSize" : NumberLong(261120),
    "length" : NumberLong(3327),
    "md5" : "879a908fd59d7419b42d43e389886a0b",
    "filename" : "e.png",
    "contentType" : "image/png",
    "uploadDate" : ISODate("2016-01-18T07:45:53.828Z"),
    "aliases" : null,
    "metadata" : {
        "extra1" : "anything 1",
        "extra2" : "anything 2"
    }
}


db.getCollection('imethan_sys_file.chunks').find({})

/* 1 */
{
    "_id" : ObjectId("569c9831c6a346b44944e6e9"),
    "files_id" : ObjectId("569c9831c6a346b44944e6e8"),
    "n" : 0,
    "data" : { "$binary" : "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", "$type" : "00" }
}


参考文档

http://docs.spring.io/spring-data/data-mongo/docs/1.8.2.RELEASE/reference/html/#gridfs

http://www.mkyong.com/mongodb/spring-data-mongodb-save-binary-file-gridfs-example/


Spring项目整合Logback日志框架

程序员笔记 2015/12/29 Spring Framework , 日志框架

1、加入maven依赖

  <!-- logback begin  --> 
               <dependency>               
                        <groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.1.3</version>
		</dependency>
		<dependency>
			<groupId>org.logback-extensions</groupId>
			<artifactId>logback-ext-spring</artifactId>
			<version>0.1.2</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>1.7.12</version>
		</dependency>
		<!-- logback end  -->

第一个logback-classic包含了logback本身所需的slf4j-api.jar、logback-core.jar及logback-classsic.jar

第二个logback-ext-spring是由官方提供的对Spring的支持,它的作用就相当于log4j中的Log4jConfigListener;这个listener,网上大多都是用的自己实现的,原因在于这个插件似乎并没有出现在官方文档的显要位置导致大多数人并不知道它的存在

第三个jcl-over-slf4j是用来把Spring源代码中大量使用到的commons-logging替换成slf4j,只有在添加了这个依赖之后才能看到Spring框架本身打印的日志,否则只能看到开发者自己打印的日志


2、日志配置文件"log/logback.xml"

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

	<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="${user.dir}" />

	<!-- 控制台输出 -->
	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
		<encoder charset="UTF-8">
			<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
			<pattern>%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
		</encoder>
	</appender>
	
	<!-- 按照每天生成日志文件 -->
	<appender name="rollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<file>${LOG_HOME}/logs/system.log</file>
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<!--日志文件输出的文件名-->
			<fileNamePattern>${LOG_HOME}/logs/system.%d{yyyy-MM-dd}.log</fileNamePattern>
			<!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
		</rollingPolicy>
		<encoder>
			<pattern>%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
		</encoder>
	</appender>
	
	<appender name="businessLogFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<file>${LOG_HOME}/logs/system_business.log</file>
		<!-- 按每小时滚动文件,如果一个小时内达到10M也会滚动文件, 滚动文件将会压缩成zip格式 -->
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<fileNamePattern>${LOG_HOME}/logs/system_business_%d{yyyy-MM-dd_HH}.%i.zip</fileNamePattern>
			<!--日志文件最大的大小-->
			<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<maxFileSize>10MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>
			<!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
		</rollingPolicy>
		<!-- 独立的pattern -->
		<encoder charset="UTF-8">
			<pattern>%d{HH:mm:ss.SSS},%msg%n</pattern>
		</encoder>
	</appender>
	
	<!-- business log 使用独立的appender,不继承Root的appender -->
	<logger name="business" level="INFO" additivity="false">
		<appender-ref ref="businessLogFile" />
	</logger>

	<!-- 日志输出级别 -->
	<root level="DEBUG">
		<appender-ref ref="console" />
		<appender-ref ref="rollingFile" />
		<appender-ref ref="businessLogFile" />
	</root>
	
	<!-- 下面配置一些第三方包的日志过滤级别,用于避免刷屏,additivity属性为false,表示此loger的打印信息不再向上级传递,默认为true -->
    <logger name="org.hibernate" level="error" additivity="false" />
    <logger name="org.springframework" level="error" additivity="false" />
    <logger name="com.opensymphony" level="WARN" />
    <logger name="org.apache" level="info" />
    <logger name="java.sql.Connection" level="DEBUG" />
    <logger name="java.sql.Statement" level="DEBUG" />
    <logger name="java.sql.PreparedStatement" level="DEBUG" />
	
</configuration>


3、web.xml配置文件添加如下内容配置

	<!-- logback监听配置,context-param必须独立配置 -->
	<context-param>
    	<param-name>logbackConfigLocation</param-name>
    	<param-value>classpath:log/logback.xml</param-value>
	</context-param>
	<listener>
    	<listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class>
	</listener>

注意,该配置文件必须放在<context-param>...</context-param>配置文件之后,如果没有会导致Logback找不到自定义的配置文件,而使用默认的配置文件,从而导致配置没有生效


参考文档:

http://logback.qos.ch/

http://blog.csdn.net/sadfishsc/article/details/47160213


Spring集成分布式Quartz框架之二:动态添加、修改、删除任务

Spring专题 2015/12/23 Spring Framework , Quartz

在上一篇关于《Spring集成配置分布式Quartz框架之一:常规整合》的基础之上实现动态添加、修改、删除任务


通常我们将Quartz整合到Spring是通过配置文件的方式配置任务Job和触发器Trigger,每次更改或者添加配置,都需要重新启动项目,为了解决该问题,在网络上翻阅了一些资料后,整理了动态添加、修改和删除任务的功能。

主要封装了三个类:QuartzJob(任务信息类)、QuartzJobFactory(任务执行类)、DynamicQuartzJobFactory(动态执行任务工厂类)。


废话少说,上菜~~


1、任务信息类:QuartzJob

package cn.imethan.admin.quartz.dynamic;

import java.io.Serializable;

/**

 * ScheduleJob.java

 * 

 * @author Ethan Wong

 * @since JDK 1.7

 * @datetime 2015年12月22日下午3:42:38

 */

public class QuartzJob implements Serializable {

    //需要实现序列化接口,否则没办法持久化到数据库

    private static final long serialVersionUID = -1800494926172862932L;

    private String jobId;//任务id

    private String jobName;//任务名称

    private String jobGroup;//任务分组

    private String jobStatus;//任务状态 0禁用 1启用 2删除

    private String cronExpression;//任务运行时间表达式

    private String description;//任务描述    

    

public String getJobId() {

return jobId;

}

public void setJobId(String jobId) {

this.jobId = jobId;

}

public String getJobName() {

return jobName;

}

public void setJobName(String jobName) {

this.jobName = jobName;

}

public String getJobGroup() {

return jobGroup;

}

public void setJobGroup(String jobGroup) {

this.jobGroup = jobGroup;

}

public String getJobStatus() {

return jobStatus;

}

public void setJobStatus(String jobStatus) {

this.jobStatus = jobStatus;

}

public String getCronExpression() {

return cronExpression;

}

public void setCronExpression(String cronExpression) {

this.cronExpression = cronExpression;

}

public String getDescription() {

return description;

}

public void setDescription(String description) {

this.description = description;

}

}


2、任务执行工厂类:QuartzJobFactory

package cn.imethan.admin.quartz.dynamic;

import java.io.Serializable;

import java.text.SimpleDateFormat;

import java.util.Date;


import org.quartz.DisallowConcurrentExecution;

import org.quartz.Job;

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;


/**

 * QuartzJobFactory.java

 *

 * @author Ethan Wong

 * @since JDK 1.7

 * @datetime 2015年12月21日下午5:36:08

 */

@DisallowConcurrentExecution

public class QuartzJobFactory  implements Job,Serializable{


private static final long serialVersionUID = 3352853281096701664L;


@Override

public void execute(JobExecutionContext context) throws JobExecutionException {

// TODO Auto-generated method stub

System.out.println("★★★★★★★★★★★QuartzJob★★★★★★★★★★★:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));    

        System.out.println("QuartzJob Name = [" + context.getJobDetail().getKey() + "]");

        

        try {

Thread.sleep(5000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}


}


3、动态执行任务工厂类:DynamicQuartzJobFactory

package cn.imethan.admin.quartz.dynamic;

import java.util.ArrayList;

import java.util.List;

import java.util.Set;


import javax.transaction.Transactional;


import org.quartz.CronScheduleBuilder;

import org.quartz.CronTrigger;

import org.quartz.JobBuilder;

import org.quartz.JobDetail;

import org.quartz.JobExecutionContext;

import org.quartz.JobKey;

import org.quartz.Scheduler;

import org.quartz.SchedulerException;

import org.quartz.Trigger;

import org.quartz.TriggerBuilder;

import org.quartz.TriggerKey;

import org.quartz.impl.matchers.GroupMatcher;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import org.springframework.stereotype.Component;


/**

 * DynamicQuartzJobFactory.java

 *

 * @author Ethan Wong

 * @since JDK 1.7

 * @datetime 2015年12月21日下午5:32:50

 */

@Component

@Transactional//必须添加事务注释,否则任务没办法持久化到数据库

public class DynamicQuartzJobFactory {

@Autowired

private SchedulerFactoryBean quartzScheduler;//将配置文件的注册的bean注入

/**

* 获取任务调度

* @return

*

* @author Ethan Wong

* @datetime 2015年12月23日上午11:23:49

*/

private Scheduler getScheduler(){

return quartzScheduler.getScheduler();

}

/**

* 准备任务数据

* @return

*

* @author Ethan Wong

* @datetime 2015年12月23日上午11:23:41

*/

private List<QuartzJob> getAllJob(){

List<QuartzJob> list = new ArrayList<QuartzJob>();

for (int i = 0; i < 5; i++) {

QuartzJob job = new QuartzJob();

job.setJobId("10001" + i);

job.setJobName("TESTJOB" + i);

job.setJobGroup("DEFAULT");

job.setJobStatus("1");

job.setCronExpression("0/5 * * * * ?");

job.setDescription("测试任务");

list.add(job);

}

return list;

}

/**

* 测试入口

*

* @author Ethan Wong

* @datetime 2015年12月23日上午11:24:11

*/

public void testOne(){

try {

List<QuartzJob> jobList = getAllJob();//这里获取任务信息数据

for (QuartzJob job : jobList) {

this.addJob(job.getJobName(), job.getJobGroup(), job.getCronExpression());

// this.deleteJob(job.getJobName(), job.getJobGroup());

}

} catch (SchedulerException e) {

e.printStackTrace();

}

}

/**

* 获取计划中的任务列表

* @return

* @throws SchedulerException

*

* @author Ethan Wong

* @datetime 2015年12月23日上午10:55:44

*/

public List<QuartzJob> getTriggersOfJob() throws SchedulerException{

Scheduler scheduler = this.getScheduler();

GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();

Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);

List<QuartzJob> jobList = new ArrayList<QuartzJob>();

for (JobKey jobKey : jobKeys) {

   List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);

   for (Trigger trigger : triggers) {

    QuartzJob job = new QuartzJob();

       job.setJobName(jobKey.getName());

       job.setJobGroup(jobKey.getGroup());

       job.setDescription("触发器:" + trigger.getKey());

       Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());

       job.setJobStatus(triggerState.name());

       if (trigger instanceof CronTrigger) {

           CronTrigger cronTrigger = (CronTrigger) trigger;

           String cronExpression = cronTrigger.getCronExpression();

           job.setCronExpression(cronExpression);

       }

       jobList.add(job);

   }

}

return jobList;

}


/**

* 获取正在执行的任务列表

* @return

* @throws SchedulerException

*

* @author Ethan Wong

* @datetime 2015年12月23日上午10:56:02

*/

public List<QuartzJob> getCurrentlyExecutingJobs() throws SchedulerException{

Scheduler scheduler = this.getScheduler();

List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();

List<QuartzJob> jobList = new ArrayList<QuartzJob>(executingJobs.size());

for (JobExecutionContext executingJob : executingJobs) {

QuartzJob job = new QuartzJob();

   JobDetail jobDetail = executingJob.getJobDetail();

   JobKey jobKey = jobDetail.getKey();

   Trigger trigger = executingJob.getTrigger();

   job.setJobName(jobKey.getName());

   job.setJobGroup(jobKey.getGroup());

   job.setDescription("触发器:" + trigger.getKey());

   Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());

   job.setJobStatus(triggerState.name());

   if (trigger instanceof CronTrigger) {

       CronTrigger cronTrigger = (CronTrigger) trigger;

       String cronExpression = cronTrigger.getCronExpression();

       job.setCronExpression(cronExpression);

   }

   jobList.add(job);

}

return jobList;

}

/**

* 添加任务

* @param jobName

* @param jobGroup

* @param timeExpression

* @return

* @throws SchedulerException

*

* @author Ethan Wong

* @datetime 2015年12月23日上午10:03:02

*/

public boolean addJob(String jobName,String jobGroup,String timeExpression) throws SchedulerException{

Scheduler scheduler = this.getScheduler();

//获取trigger,即在spring配置文件中定义的 bean id="myTrigger"

TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);

CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

if (trigger == null) {

//不存在,创建一个

JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class).withIdentity(jobName, jobGroup).storeDurably().build();

jobDetail.getJobDataMap().put("scheduleJob", "job");

scheduler.addJob(jobDetail, true);

//表达式调度构建器

CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(timeExpression);

//按新的cronExpression表达式构建一个新的trigger

trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).forJob(jobDetail).withSchedule(scheduleBuilder).build();

boolean isExists = scheduler.checkExists(jobDetail.getKey());

if(!isExists){

scheduler.scheduleJob(jobDetail, trigger);

}else{

}

scheduler.scheduleJob(trigger);

} else {

// Trigger已存在,那么更新相应的定时设置

//表达式调度构建器

CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(timeExpression);

//按新的cronExpression表达式重新构建trigger

trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

//按新的trigger重新设置job执行

scheduler.rescheduleJob(triggerKey, trigger);

}

return false;

}

/**

* 暂停任务

* @param jobName

* @param jobGroup

* @throws SchedulerException

*

* @author Ethan Wong

* @datetime 2015年12月23日上午10:17:00

*/

public void pauseJob(String jobName,String jobGroup) throws SchedulerException{

Scheduler scheduler = this.getScheduler();

JobKey jobKey = JobKey.jobKey(jobName, jobName);

scheduler.pauseJob(jobKey);

}

/**

* 恢复任务

* @param jobName

* @param jobGroup

* @throws SchedulerException

*

* @author Ethan Wong

* @datetime 2015年12月23日上午10:17:41

*/

public void resumeJob(String jobName,String jobGroup) throws SchedulerException{

Scheduler scheduler = this.getScheduler();

JobKey jobKey = JobKey.jobKey(jobName, jobGroup);

scheduler.resumeJob(jobKey);

}

/**

* 删除任务

* @param jobName

* @param jobGroup

* @throws SchedulerException

*

* @author Ethan Wong

* @datetime 2015年12月23日上午10:18:38

*/

public void  deleteJob(String jobName,String jobGroup) throws SchedulerException{

Scheduler scheduler = this.getScheduler();

JobKey jobKey = JobKey.jobKey(jobName, jobGroup);

scheduler.deleteJob(jobKey);

}

/**

* 触发任务

* @param jobName

* @param jobGroup

* @throws SchedulerException

*

* @author Ethan Wong

* @datetime 2015年12月23日上午10:19:49

*/

public void  triggerJob(String jobName,String jobGroup) throws SchedulerException{

Scheduler scheduler = this.getScheduler();

JobKey jobKey = JobKey.jobKey(jobName, jobGroup);

scheduler.triggerJob(jobKey);

}

/**

* 更新任务触发器时间

* @param jobName

* @param jobGroup

* @param timeExpression

* @throws SchedulerException

*

* @author Ethan Wong

* @datetime 2015年12月23日上午10:21:55

*/

public void modifyTimeExpression(String jobName,String jobGroup,String timeExpression) throws SchedulerException{

Scheduler scheduler = this.getScheduler();

TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);

//获取trigger,即在spring配置文件中定义的 bean id="myTrigger"

CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

//表达式调度构建器

CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(timeExpression);

//按新的cronExpression表达式重新构建trigger

trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

//按新的trigger重新设置job执行

scheduler.rescheduleJob(triggerKey, trigger);

}


}



参考文档

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

http://www.dexcoder.com/selfly/article/308

Spring security 3中的10个典型用法小结

Spring专题 2015/12/18 Spring Framework , Spring Security

Spring security整合CAS单点登录,业务系统无法单点注销

Spring专题 2015/12/18 Spring Framework , Spring Security

Spring security整合CAS单点登录,业务系统无法单点注销


Spring Security启用session-fixation-protection,这会在登录时销毁用户的当前session,然后为用户创建一个新session,并将原有session中的所有属性都复制到新session中,导致单点注销时无法注销登录原有的session,从而导致单点注销失败。


将session-fixation-protection禁用后,可以正常单点注销。


在Spring security 4 中体现在:

<bean id="sessionAuthenticationStrategy" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">

         <constructor-arg>
            <list>

                  <bean />

            </list>
          </constructor-arg>

</bean>

bean类不要引入如下bean

<bean class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" />



参考文档

http://blog.csdn.net/seng3018/article/details/7019872

使用Spring AMQP动态创建RabbitMQ连接进行消息的发送和接收

RabbitMQ笔记 2015/12/18 Spring Framework , RabbitMQ

使用Spring AMQP动态创建RabbitMQ连接进行消息的发送和接收


未完待续~~



Spring MVC i18n国际化配置

Spring专题 2015/12/18 Spring Framework

Spring MVC i18n国际化配置


#准备工作

该配置在Spring项目上进行自不必再说,另外还需要配置好Spring mvc


#message文件创建

message文件“welcome_en_US.properties”内容:

#welcome_en_US.properties

welcome.springmvc = Happy learning Spring MVC


message文件“welcome_zh_CN.properties内容:

#welcome_zh_CN.properties

welcome.springmvc = \u5feb\u4e50\u5b66\u4e60 Spring MVC



#向Spring mvc配置文件“spring-mvc.xml”中添加配置


<!-- 添加locate拦截器,拦截所有的控制器请求 -->

<mvc:interceptor>

<mvc:mapping path="/**"/>

<mvc:exclude-mapping path="/theme/**" />

<mvc:exclude-mapping path="/upload/**" />

<ref local="localeChangeInterceptor"/>

</mvc:interceptor>


<!-- i18n国际化配置 -->

<bean id="localeResolver"

class="org.springframework.web.servlet.i18n.SessionLocaleResolver">

<property name="defaultLocale" value="zh_CN" />

</bean>

<!-- i18n本地化设置拦截器 -->

<bean id="localeChangeInterceptor" 

class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">

<property name="paramName" value="language" />

</bean>

<!-- 添加message文件 -->

<bean id="messageSource"

class="org.springframework.context.support.ResourceBundleMessageSource">

<property name="basename" value="message/welcome" />

</bean>


#JSP页面

1、添加如下标签支持:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>


2、添加如下内容:

Language : <a href="?language=en_US">English</a>|<a href="?language=zh_CN">Chinese</a>

welcome.springmvc : <spring:message code="welcome.springmvc" text="default text" />

Current Locale : ${pageContext.response.locale}


#结语

上述过程简单记录spring mvc添加i18国际化配置支持,通过浏览器url请求来更改locate的值,messageSource根据不同的locate来切换展现的message内容,添加全局拦截器,所以控制器不需要添加额外的内容。


#参考文档

http://www.mkyong.com/spring-mvc/spring-mvc-internationalization-example/

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


使用Spring AMQP进行RabbitMQ消息的发送和接收

RabbitMQ笔记 2015/12/18 Spring Framework , RabbitMQ

Spring Framework项目集成Spring AMQP

#安装好RabbitMQ 3.5.6

安装的介绍在这篇文章:http://imethan.cn/blog/article/78


#通过eclipse创建简单的maven项目

#编辑pom.xml,指定编译器的版本

<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>

</plugins>

</build>

#编辑pom.xml,添加maven依赖

<properties>

<spring-rabbit-version>1.4.3.RELEASE</spring-rabbit-version>

<rabbitmq-version>3.5.0</rabbitmq-version>

</properties>

<dependencies>

<!-- rabbitmq begin -->

<dependency>

<groupId>org.springframework.amqp</groupId>

<artifactId>spring-rabbit</artifactId>

<version>${spring-rabbit-version}</version>

</dependency>

<dependency>

<groupId>com.rabbitmq</groupId>

<artifactId>amqp-client</artifactId>

<version>${rabbitmq-version}</version>

</dependency>

<!-- rabbitmq end -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-test</artifactId>

<version>4.1.7.RELEASE</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.12</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>commons-logging</groupId>

<artifactId>commons-logging</artifactId>

<version>1.2</version>

</dependency>

</dependencies>


#添加rabbitmq.properties文件

#rabbitmq connection information

mq.host=192.168.42.133

mq.port=5673

mq.username=spring

mq.password=spring.com

mq.virtualhost=spring

mq.defaultqueue=defaultqueue


#添加Spring Rabbitmq配置文件“applicationContext-rabbitmq.xml”

<?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:context="http://www.springframework.org/schema/context"

xmlns:p="http://www.springframework.org/schema/p" 

xmlns:rabbit="http://www.springframework.org/schema/rabbit"

xsi:schemaLocation="

http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd

http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd" 

default-lazy-init="true">

<description>Spring AMQP</description>

<context:annotation-config />

<context:component-scan base-package="cn.imethan" />

<context:property-placeholder location="classpath:/rabbitmq/rabbitmq.properties" />

<!-- 发送确认监听 -->

<bean id="messageConfirmCallback" class="cn.imethan.rabbitmq.callback.MessageConfirmCallback"/>

<!-- 失败回调监听 -->

<bean id="messageReturnCallback" class="cn.imethan.rabbitmq.callback.MessageReturnCallback"/>


<!-- 连接工厂设置 -->

<bean id="rabbitConnectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">

  <property name="username" value="${mq.username}"/>

  <property name="password" value="${mq.password}"/>

  <property name="virtualHost" value="${mq.virtualhost}"/>

   <property name="host" value="${mq.host}"/>

   <property name="port" value="${mq.port}"/>

   <property name="channelCacheSize" value="25"/>    

   <property name="publisherConfirms" value="true"/>

   <property name="publisherReturns" value="true"/>

</bean>

<bean id="rabbitTemplate" class="org.springframework.amqp.rabbit.core.RabbitTemplate">

<property name="connectionFactory" ref="rabbitConnectionFactory"/>

<!-- 设置确认监听 -->

<property name="confirmCallback" ref="messageConfirmCallback"/>

<!-- 如果mandatory设置为true,但是不能被路由,则由returnCallback监听获取消息 -->

<property name="mandatory" value="true"/>

<!-- 设置失败监听 -->

<property name="returnCallback" ref="messageReturnCallback"/>

   <property name="exchange" value="remoting.exchange"/>    

   <property name="routingKey" value="${mq.defaultqueue}"/>

</bean>

<!-- 队列创建,并且绑定 -->

<rabbit:admin id="admin" connection-factory="rabbitConnectionFactory" />

<rabbit:queue name="${mq.defaultqueue}" declared-by="admin" />

<!-- 创建交换机并且绑定 -->

<rabbit:direct-exchange name="remoting.exchange" declared-by="admin">

<rabbit:bindings>

<rabbit:binding queue="${mq.defaultqueue}" key="${mq.defaultqueue}" />

</rabbit:bindings>

</rabbit:direct-exchange>

<!-- 监听设置 -->

<rabbit:listener-container connection-factory="rabbitConnectionFactory">

<rabbit:listener ref="awareRecviveListener" method="listen" queue-names="${mq.defaultqueue}"  />

</rabbit:listener-container>

<!-- 接收监听器 -->

<bean id="awareRecviveListener" class="cn.imethan.rabbitmq.listener.AwareRecviveListener" />

<bean id="simpleRecviveListener" class="cn.imethan.rabbitmq.listener.SimpleRecviveListener" />

</beans>



#创建几个重要的类

MessageConfirmCallback:

public class MessageConfirmCallback implements ConfirmCallback {

@Override

public void confirm(CorrelationData correlationData, boolean ack,String cause) {

System.out.println("[RabbitMQ] MessageConfirmCallback correlationData:"+correlationData);

System.out.println("[RabbitMQ] MessageConfirmCallback             ack:"+ack);

System.out.println("[RabbitMQ] MessageConfirmCallback           cause:"+cause);

if(ack){

System.out.println("[RabbitMQ] send message to rabbitmq success !");

}else{

System.out.println("[RabbitMQ] send message to rabbitmq fail !");

}

}

}

MessageReturnCallback :

public class MessageReturnCallback implements ReturnCallback {

@Override

public void returnedMessage(Message message, int replyCode,

String replyText, String exchange, String routingKey) {

System.out.println("[RabbitMQ] MessageReturnCallback message:"+message);

System.out.println("[RabbitMQ] MessageReturnCallback replyCode:"+replyCode);

System.out.println("[RabbitMQ] MessageReturnCallback replyText:"+replyText);

System.out.println("[RabbitMQ] MessageReturnCallback exchange:"+exchange);

System.out.println("[RabbitMQ] MessageReturnCallback routingKey:"+routingKey);

}

}


AwareRecviveListener:

@Service

public class AwareRecviveListener implements ChannelAwareMessageListener  {

@Autowired

private RabbitTemplate rabbitTemplate;

@Override

public void onMessage(Message message, Channel channel) throws Exception {

MessageProperties messageProperties = message.getMessageProperties() ;

String encoding = messageProperties.getContentEncoding();//编码

String content = new String(message.getBody(),encoding);//内容

String appId = messageProperties.getAppId();//发送时设置的发送方标示appid

System.out.println("[RabbitMQ] recv message -appId:"+appId +" -message:"+content);

String vhost = rabbitTemplate.getConnectionFactory().getVirtualHost();//当前虚拟主机名称

String consumerQueue = messageProperties.getConsumerQueue();//当前接收的队列名称

//业务处理

this.handle(content);

}


/**

* 业务处理

* @param message 消息

*

* @author Ethan Wong

* @create-time 2015年5月9日 下午5:15:03

*/

private void handle(String message) {

}

}


SimpleRecviveListener:

@Service

public class SimpleRecviveListener {


/**

* 消息监听

* @param message 消息

*

* @author Ethan Wong

* @create-time 2015年5月9日 下午5:14:36

*/

public void listen(String message) {

this.handle(message);

}


/**

* 消息监听

* @param message 消息

* @throws UnsupportedEncodingException

*

* @author Ethan Wong

* @create-time 2015年5月9日 下午5:14:45

*/

public void listen(byte[] message) throws UnsupportedEncodingException {

this.handle(new String(message, "UTF-8"));

}


/**

* 业务处理

* @param message 消息

*

* @author Ethan Wong

* @create-time 2015年5月9日 下午5:15:03

*/

private void handle(String message) {

System.out.println("[RabbitMQ] recv message:"+message);


}

}


RabbitMqSender:

@Service

public class RabbitMqSender {

@Autowired

private RabbitTemplate rabbitTemplate;

/**

* 发送消息

* @param queue

* @param content

*

* @author Ethan

* @datetime 2015年11月9日 下午8:24:00

*/

public void sendMessage(String queue,String content){

try {

String routingKey = queue;

//发送消息

rabbitTemplate.setQueue(queue);

rabbitTemplate.setRoutingKey(routingKey);

rabbitTemplate.send(this.generateMessage(content,queue));

} catch (AmqpException e) {

e.printStackTrace();

}

}

/**

* 生成消息

* @param content 消息内容

* @param queueName 队列名称

* @return

*

* @author Ethan

* @create-time 2015年5月6日 下午4:20:50

*/

private Message generateMessage(String content,String queueName){

String appId = rabbitTemplate.getConnectionFactory().getVirtualHost()+"/"+queueName;//将虚拟主机和队列名称设置为appId

//发送消息

MessageProperties props = MessagePropertiesBuilder.newInstance()

.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN)

.setContentEncoding("UTF-8")

.setTimestamp(new Date()).setHeader("", "")

.setAppId(appId)//设置发送中心系统识别

.build();

Message message = MessageBuilder.withBody(content.getBytes())

.andProperties(props)

.build();

return message;

}

}


#到此处,所有的准备工作都做好了,开始测试,测试类如下:

RabbitMqTest:

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations={"classpath:rabbitmq/applicationContext-rabbitmq.xml"}) 

public class RabbitMqTest{  

@Autowired

private RabbitMqSender rabbitMqSender;

@BeforeClass

public static void initRabbitMq(){

}

/**

* 测试向MQ发送消息

*

* @author Ethan Wong

* @create-time 2015年5月6日 下午2:47:34

*/

@Test

public void testSend(){

rabbitMqSender.sendMessage("defaultqueue", "向RabbitMQ发送消息!!!");

}

@AfterClass

public static void testRecv(){

}

}


源代码后续将和其他部分整理完整后,提交GitHub.


#参考文档

http://projects.spring.io/spring-amqp/

http://docs.spring.io/spring-amqp/docs/1.5.1.RELEASE/reference/html/



Spring Framework项目集成Spring Data MongoDB

mongoDB笔记 2015/12/18 Spring Framework , MongoDB

以下简要记录Spring Framework项目集成Spring Data MongoDB的过程步骤

#首先引入依赖的类库,Spring Framework项目使用的是Gradle构建工具,所以依赖引入如下:

    //Spring data mongodb begin
    compile 'org.springframework.data:spring-data-mongodb:1.8.0.RELEASE'
    compile 'com.mysema.querydsl:querydsl-apt:3.6.7'
    compile 'com.mysema.querydsl:querydsl-mongodb:3.6.7'
    //Spring data mongodb end
    
    //mongodb driver begin
    compile 'org.mongodb:mongo-java-driver:2.12.3'
    //mongodb driver end
    
   //需要引入测试依赖包
   testCompile 'org.springframework:spring-test:4.2.1.RELEASE'
   //引入junit依赖包
   testCompile group: 'junit', name: 'junit', version: '4.+'


#添加mongodb配置文件applicationContext-mongodb.xml,配置文件内容如下:

<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.8.xsd" 
default-lazy-init="false">
<description>applicationContext</description>

<mongo:mongo host="${mongo.host}" port="${mongo.port}">
<mongo:options connections-per-host="${mongo.connectionsPerHost}"
threads-allowed-to-block-for-connection-multiplier="${mongo.threadsAllowedToBlockForConnectionMultiplier}"
connect-timeout="${mongo.connectTimeout}" max-wait-time="${mongo.maxWaitTime}"
auto-connect-retry="${mongo.autoConnectRetry}" socket-keep-alive="${mongo.socketKeepAlive}"
socket-timeout="${mongo.socketTimeout}" slave-ok="${mongo.slaveOk}"
write-number="1" write-timeout="0" write-fsync="true" />
</mongo:mongo>

<!-- Factory bean that creates the Mongo instance -->
<mongo:db-factory dbname="${mongo.databaseName}" mongo-ref="mongo" username="${mongo.username}" password="${mongo.password}" />

<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
</bean>

<mongo:repositories base-package="cn.imethan" mongo-template-ref="mongoTemplate"
base-class="cn.imethan.common.mongodb.BaseRepositoryImpl" />

<!-- Use this post processor to translate any MongoExceptions thrown in @Repository annotated classes -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<!-- Enabling Spring Data web support in XML -->
<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />
</beans>


#添加属性配置文件mongo.properties,配置文件内容如下:

#local mongo db 
mongo.host=127.0.0.1
mongo.port=27017
mongo.username=
mongo.password=
mongo.databaseName=test

mongo.connectionsPerHost=8
mongo.threadsAllowedToBlockForConnectionMultiplier=4
mongo.connectTimeout=1000
mongo.maxWaitTime=1500
mongo.autoConnectRetry=true
mongo.socketKeepAlive=true
mongo.socketTimeout=1500
mongo.slaveOk=true


#增加通用基础Repository接口BaseRepository和Repository接口实现类BaseRepositoryImpl

/**
 * BaseRepository.java
 *
 * @author Ethan Wong
 * @since JDK 1.7
 * @datetime 2015年9月30日下午6:06:15
 */
@NoRepositoryBean
public interface BaseRepository<T, ID extends Serializable> extends MongoRepository<T, ID> {
/**
* Get {@link MongoOperations}.
* @return
*
* @author Ethan Wong
* @datetime 2015年10月1日上午5:38:14
*/
public MongoOperations getMongoOperations();
/**
* Get {@link MongoEntityInformation}.
* @return
*
* @author Ethan Wong
* @datetime 2015年10月1日上午5:39:49
*/
public MongoEntityInformation<T, ID> getMongoEntityInformation();
}
/**
 * BaseRepositoryImpl.java
 *
 * @author Ethan Wong
 * @since JDK 1.7
 * @datetime 2015年9月30日下午6:08:09
 */
public class BaseRepositoryImpl<T, ID extends Serializable> extends SimpleMongoRepository<T, ID>implements BaseRepository<T, ID> {

private MongoOperations mongoOperations;
private MongoEntityInformation<T, ID> metadata;

public BaseRepositoryImpl(MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) {
super(metadata, mongoOperations);
this.mongoOperations = mongoOperations;
this.metadata = metadata;
}

@Override
public MongoOperations getMongoOperations() {
return mongoOperations;
}

@Override
public MongoEntityInformation<T, ID> getMongoEntityInformation() {
return metadata;
}
}


#以上基础配置完成,以下开始测试

#添加自定义Repository接口

/**
 * ChannelRepository.java
 *
 * @author Ethan Wong
 * @time 2014年3月2日下午4:44:45
 */
@Repository
public interface ChannelRepository extends MongoRepository<Channel, String>,
ChannelRepositoryCustom<Channel, String>, BaseRepository<Channel, String> {
/**
* @see http://docs.spring.io/spring-data/data-mongo/docs/1.8.0.RELEASE/reference/html/
*
* @author Ethan Wong
* @datetime 2015年10月1日上午5:21:48
*/
Long countByTitle(String title);
Long deleteByTitle(String title);

List<Channel> removeByTitle(String title);
List<Channel> findByTitle(String title);
List<Channel> findByTitle(String title, Sort sort);

Page<Channel> findByTitle(String title, Pageable pageable);
//Slice<Channel> findByTitle(String title, Pageable pageable);
//List<Channel> findByTitle(String title, Pageable pageable);
List<Channel> findTop3ByTitle(String title, Pageable pageable);
List<Channel> findFirst10ByTitle(String title, Sort sort);


List<Channel> readAllByTitleNotNull();
}


# Channel类:

/**
 * Channel.java
 *
 * @author Ethan Wong
 * @time 2014年3月2日下午4:44:32
 */
@Document
public class Channel extends BaseDocument implements Serializable{

private static final long serialVersionUID = -3919470868083141326L;
private String title;//标题
private String describe;//描述

public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
}


#添加单元测试类ChannelRepositoryTest,其中applicationContext.xml为Spring Framework的配置文件

/**
 * ChannelRepositoryTest.java
 *
 * @author Ethan Wong
 * @time 2014年3月12日下午2:39:27
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/main/applicationContext.xml",
"classpath*:/mongodb/applicationContext-mongodb.xml"
})
public class ChannelRepositoryTest {
@Autowired
private ChannelRepository channelRepository;

     @Test
public void testQuery(){
String name = channelRepository.getDbName();
System.out.println("name:"+name);
Pageable  pageable  = new PageRequest(0,10,Direction.DESC,"createTime");
String title = "标题1";
long count = channelRepository.countByTitle(title);
System.out.println("count:"+count);
List<Channel> list = channelRepository.findByTitle(title);
System.out.println("list:"+list);
List<Channel> list1 = channelRepository.findTop3ByTitle(title, pageable);
System.out.println("list1:"+list1);
List<Channel> list2 = channelRepository.readAllByTitleNotNull();
System.out.println("list2:"+list2);
MongoTemplate mongoTemplate = (MongoTemplate) channelRepository.getMongoOperations();
List<Channel> list3 = mongoTemplate.findAll(channelRepository.getMongoEntityInformation().getJavaType());
System.out.println("list3:"+list3);
Query query = new Query(Criteria.where("title").is(title));
Update update = new Update().set("describe", "随便更新点什么");
Channel channel = mongoTemplate.findAndModify(query, update, Channel.class);
System.out.println("channel:"+channel);
}
}



#参考文档

http://docs.spring.io/spring-data/data-mongo/docs/1.8.0.RELEASE/reference/html/

http://docs.spring.io/spring/docs/4.1.7.RELEASE/spring-framework-reference/htmlsingle/



Spring集成分布式Quartz框架之一:常规整合

Spring专题 2015/12/18 Spring Framework , Quartz

该配置为了解决WEB项目分布式部署时定时任务的并发冲突问题。

配置环境如下:

JDK 1.7

Spring 4.0.2

Quartz 2.2.1

首先要导入quartz涉及到11张表

从官网http://www.quartz-scheduler.org下载“quartz-2.2.1-distribution.tar.gz”,解压,从“quartz-2.2.1-distribution\quartz-2.2.1\docs\dbTables”目录下找到合适的您项目的脚本


两个主要配置文件:quartz.properties和applicationContext-quartz.xml,文件内容如下:

quartz.properties文件内容:

# Using Spring datasource in quartzJobsConfig.xml

# Spring uses LocalDataSourceJobStore extension of JobStoreCMT

org.quartz.jobStore.useProperties=true

org.quartz.jobStore.tablePrefix=QRTZ_

org.quartz.jobStore.isClustered=true

# 1 mins

org.quartz.jobStore.clusterCheckinInterval=15000

org.quartz.scheduler.skipUpdateCheck=true


# Change this to match your DB vendor

org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate

# Needed to manage cluster instances

org.quartz.scheduler.instanceId=AUTO

org.quartz.scheduler.instanceName=quartzInstanceName

 

org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool

org.quartz.threadPool.threadCount=10

org.quartz.threadPool.threadPriority=5

org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true


# Configure Plugins

org.quartz.plugin.triggHistory.class=org.quartz.plugins.history.LoggingJobHistoryPlugin

#org.quartz.plugin.triggHistory.class=org.quartz.plugins.history.LoggingTriggerHistoryPlugin

#org.quartz.plugin.triggHistory.triggerFiredMessage=Trigger \{1\}.\{0\} fired job \{6\}.\{5\} at: \{4, date, HH:mm:ss MM/dd/yyyy}

#org.quartz.plugin.triggHistory.triggerCompleteMessage=Trigger \{1\}.\{0\} completed firing job \{6\}.\{5\} at \{4, date, HH:mm:ss MM/dd/yyyy\}


applicationContext-quartz.xml文件内容:

<?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: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-3.2.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-3.2.xsd

http://www.springframework.org/schema/task

http://www.springframework.org/schema/task/spring-task-3.2.xsd">


<description>quartz分布式配置文件</description>


<context:annotation-config />


<!-- 任务配置 -->

<bean id="jobDetail"

class="org.springframework.scheduling.quartz.JobDetailFactoryBean">

<property name="jobClass" value="cn.imethan.admin.quartz.job.TestJob" />

<property name="durability" value="true" />

<!-- <property name="requestsRecovery" value="true" /> -->

</bean>


<!-- 触发器 -->

<bean id="jobDetailTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">

<property name="jobDetail" ref="jobDetail" />

<property name="cronExpression">

<value>0/5 * * * * ?</value>

</property>

<property name="timeZone">

<value>GMT+8:00</value>

</property>

</bean>


<!-- 任务调度配置 -->

<bean id="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

<property name="configLocation" value="classpath:/quartz/quartz.properties" />

<property name="dataSource" ref="myDataSource" />

<property name="transactionManager" ref="myTxManager" />


<!-- This name is persisted as SCHED_NAME in db. for local testing could 

change to unique name to avoid collision with dev server -->

<property name="schedulerName" value="quartzScheduler" />


<property name="startupDelay" value="5" />


<!-- Will update database cron triggers to what is in this jobs file on 

each deploy. Replaces all previous trigger and job data that was in the database. 

YMMV -->

<property name="overwriteExistingJobs" value="true" />

<property name="autoStartup" value="true" />

<property name="applicationContextSchedulerContextKey" value="applicationContext" />

<property name="jobFactory">

<bean class="cn.imethan.admin.quartz.common.AutowiringSpringBeanJobFactory" />

</property>


<!-- NOTE: Must add both the jobDetail and trigger to the scheduler! -->

<property name="triggers">

<list>

<ref bean="jobDetailTrigger" />

</list>

</property>

<property name="jobDetails">

<list>

<ref bean="jobDetail" />

</list>

</property>

</bean>

</beans>


两个主要的类,AutowiringSpringBeanJobFactory和TestJob,类内容如下:

AutowiringSpringBeanJobFactory类内容:

package cn.imethan.admin.quartz.common;


import org.quartz.spi.TriggerFiredBundle;

import org.springframework.beans.factory.config.AutowireCapableBeanFactory;

import org.springframework.context.ApplicationContext;

import org.springframework.context.ApplicationContextAware;

import org.springframework.scheduling.quartz.SpringBeanJobFactory;


/**

 * AutowiringSpringBeanJobFactory.java

 *

 * @author Ethan Wong

 * @time 2015年9月19日下午3:33:22

 */

public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {


private transient AutowireCapableBeanFactory beanFactory;


public void setApplicationContext(final ApplicationContext context) {

beanFactory = context.getAutowireCapableBeanFactory();

}


@Override

protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {

final Object job = super.createJobInstance(bundle);

beanFactory.autowireBean(job);

return job;

}

}


TestJob类内容如下:

package cn.imethan.admin.quartz.job;

import org.quartz.DisallowConcurrentExecution;

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

import org.springframework.scheduling.quartz.QuartzJobBean;


import cn.imethan.common.utils.DateUtil;


/**

 * TestJob.java

 *

 * @author Ethan Wong

 * @time 2015年9月19日下午3:36:40

 */

@DisallowConcurrentExecution

public class TestJob extends QuartzJobBean {

    protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {

   

    System.out.println("[TestJob]Sleep 10s begin date:"+DateUtil.getDatetimeStr(DateUtil.DATE_PATTERN_01));

    try {

Thread.sleep(4000);

} catch (InterruptedException e) {

e.printStackTrace();

}

    System.out.println("[TestJob]Sleep 10s end   date:"+DateUtil.getDatetimeStr(DateUtil.DATE_PATTERN_01));

    }

    

}




参考文档

http://www.cnblogs.com/skyblue/p/3296350.html