Просмотр исходного кода

创建定时任务删除产生的pdf等文件(包括定时任务的关闭、开启、自动启动等)

sunyj 9 лет назад
Родитель
Сommit
038ffd6802

+ 62 - 0
src/main/java/com/uas/report/controller/ScheduleController.java

@@ -0,0 +1,62 @@
+package com.uas.report.controller;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.uas.report.controller.schedule.model.DailyTaskInformation;
+import com.uas.report.controller.schedule.service.DailyTaskService;
+import com.uas.report.service.FileService;
+
+@Controller
+@RequestMapping("/schedule")
+public class ScheduleController {
+
+	@Autowired
+	private FileService fileService;
+
+	@Autowired
+	private DailyTaskService dailyTaskService;
+
+	@RequestMapping("/deleteGeneratedFiles")
+	@ResponseBody
+	public String updateOverdue(@RequestParam(required = true) Integer hour,
+			@RequestParam(required = true) Integer minute, @RequestParam(required = true) Integer second) {
+		return "已开启:" + fileService.startDeleteGeneratedFilesDailyTask(hour, minute, second);
+	}
+
+	@RequestMapping("/tasks")
+	@ResponseBody
+	public List<DailyTaskInformation> allDailyTaskInformations() {
+		return dailyTaskService.allDailyTaskInformations();
+	}
+
+	@RequestMapping("/start")
+	@ResponseBody
+	public String start() {
+		return dailyTaskService.start();
+	}
+
+	@RequestMapping("/stop")
+	@ResponseBody
+	public String stop() {
+		return dailyTaskService.stop();
+	}
+
+	@RequestMapping("/restart")
+	@ResponseBody
+	public String restart() {
+		dailyTaskService.stop();
+		return dailyTaskService.start();
+	}
+
+	@RequestMapping("/isStopped")
+	@ResponseBody
+	public boolean isStopped() {
+		return dailyTaskService.isStopped();
+	}
+}

+ 111 - 0
src/main/java/com/uas/report/controller/schedule/model/DailyTaskInformation.java

@@ -0,0 +1,111 @@
+package com.uas.report.controller.schedule.model;
+
+import java.util.Objects;
+
+/**
+ * 每天定时任务
+ * 
+ * @author sunyj
+ * @since 2016年12月12日 下午3:18:42
+ */
+public class DailyTaskInformation {
+
+	/**
+	 * 任务标题
+	 */
+	private String title;
+
+	/**
+	 * 所执行的任务
+	 */
+	private Runnable command;
+
+	/**
+	 * 执行时间中的小时
+	 */
+	private int hour;
+
+	/**
+	 * 执行时间中的分钟
+	 */
+	private int minute;
+
+	/**
+	 * 执行时间中的秒
+	 */
+	private int second;
+
+	public DailyTaskInformation() {
+		super();
+	}
+
+	public DailyTaskInformation(String title, Runnable command, int hour, int minute, int second) {
+		super();
+		this.title = title;
+		this.command = command;
+		this.hour = hour;
+		this.minute = minute;
+		this.second = second;
+	}
+
+	public String getTitle() {
+		return title;
+	}
+
+	public void setTitle(String title) {
+		this.title = title;
+	}
+
+	public Runnable getCommand() {
+		return command;
+	}
+
+	public void setCommand(Runnable command) {
+		this.command = command;
+	}
+
+	public int getHour() {
+		return hour;
+	}
+
+	public void setHour(int hour) {
+		this.hour = hour;
+	}
+
+	public int getMinute() {
+		return minute;
+	}
+
+	public void setMinute(int minute) {
+		this.minute = minute;
+	}
+
+	public int getSecond() {
+		return second;
+	}
+
+	public void setSecond(int second) {
+		this.second = second;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj) {
+			return true;
+		}
+		if (obj == null || getClass() != obj.getClass() || !(obj instanceof DailyTaskInformation)) {
+			return false;
+		}
+		DailyTaskInformation other = (DailyTaskInformation) obj;
+		// command不好比较,不进行比较
+		return Objects.equals(title, other.getTitle()) && hour == other.getHour() && minute == other.getMinute()
+				&& second == other.getSecond();
+	}
+
+	@Override
+	public String toString() {
+		return "DailyTaskInformation [title=" + title + ", command=" + command + ", hour=" + hour + ", minute=" + minute
+				+ ", second=" + second + "]";
+	}
+
+}

+ 69 - 0
src/main/java/com/uas/report/controller/schedule/model/DailyTaskLog.java

@@ -0,0 +1,69 @@
+package com.uas.report.controller.schedule.model;
+
+import java.util.Date;
+
+/**
+ * 每天定时任务的执行日志
+ * 
+ * @author sunyj
+ * @since 2016年12月12日 下午3:22:30
+ */
+public class DailyTaskLog {
+
+	/**
+	 * 每天定时任务的信息
+	 */
+	private DailyTaskInformation dailyTaskInformation;
+
+	/**
+	 * 任务执行时间
+	 */
+	private Date executeTime;
+
+	/**
+	 * 任务执行日志
+	 */
+	private String log;
+
+	public DailyTaskLog() {
+		super();
+	}
+
+	public DailyTaskLog(DailyTaskInformation dailyTaskInformation, Date executeTime, String log) {
+		super();
+		this.dailyTaskInformation = dailyTaskInformation;
+		this.executeTime = executeTime;
+		this.log = log;
+	}
+
+	public DailyTaskInformation getDailyTaskInformation() {
+		return dailyTaskInformation;
+	}
+
+	public void setDailyTaskInformation(DailyTaskInformation dailyTaskInformation) {
+		this.dailyTaskInformation = dailyTaskInformation;
+	}
+
+	public Date getExecuteTime() {
+		return executeTime;
+	}
+
+	public void setExecuteTime(Date executeTime) {
+		this.executeTime = executeTime;
+	}
+
+	public String getLog() {
+		return log;
+	}
+
+	public void setLog(String log) {
+		this.log = log;
+	}
+
+	@Override
+	public String toString() {
+		return "DailyTaskLog [dailyTaskInformation=" + dailyTaskInformation + ", executeTime=" + executeTime + ", log="
+				+ log + "]";
+	}
+
+}

+ 57 - 0
src/main/java/com/uas/report/controller/schedule/service/DailyTaskService.java

@@ -0,0 +1,57 @@
+package com.uas.report.controller.schedule.service;
+
+import java.util.List;
+
+import com.uas.report.controller.schedule.model.DailyTaskInformation;
+import com.uas.report.controller.schedule.model.DailyTaskLog;
+
+/**
+ * 管理定时任务
+ * 
+ * @author sunyj
+ * @since 2016年12月13日 上午8:46:53
+ */
+public interface DailyTaskService {
+	/**
+	 * 建立每天定时任务
+	 * 
+	 * @param dailyTaskInformation
+	 *            每天定时任务的信息
+	 */
+	public void newDailyTask(DailyTaskInformation dailyTaskInformation);
+
+	/**
+	 * 保存日志到本地文件
+	 * 
+	 * @param log
+	 */
+	public void saveLog(DailyTaskLog log);
+
+	/**
+	 * 获取定时任务信息
+	 * 
+	 * @return
+	 */
+	public List<DailyTaskInformation> allDailyTaskInformations();
+
+	/**
+	 * 开启定时任务
+	 * 
+	 * @return 返回的结果
+	 */
+	public String start();
+
+	/**
+	 * 关闭定时任务
+	 * 
+	 * @return 返回的结果
+	 */
+	public String stop();
+
+	/**
+	 * 定时任务是否停止
+	 * 
+	 * @return
+	 */
+	public boolean isStopped();
+}

+ 186 - 0
src/main/java/com/uas/report/controller/schedule/service/impl/DailyTaskServiceImpl.java

@@ -0,0 +1,186 @@
+package com.uas.report.controller.schedule.service.impl;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import com.alibaba.fastjson.JSONObject;
+import com.uas.report.controller.schedule.model.DailyTaskInformation;
+import com.uas.report.controller.schedule.model.DailyTaskLog;
+import com.uas.report.controller.schedule.service.DailyTaskService;
+import com.uas.report.core.exception.ReportException;
+import com.uas.report.util.PathUtils;
+
+/**
+ * 管理定时任务
+ * 
+ * @author sunyj
+ * @since 2016年12月12日 上午11:40:00
+ */
+@Service
+public class DailyTaskServiceImpl implements DailyTaskService {
+
+	/**
+	 * 随容器启动时,普通成员变量的值会丢失,需要使用类域
+	 */
+	private static List<DailyTaskInformation> dailyTaskInformations = new ArrayList<>();
+
+	private static ScheduledExecutorService scheduledExecutorService;
+
+	private Logger logger = Logger.getLogger(DailyTaskServiceImpl.class);
+
+	/**
+	 * 一天的毫秒数
+	 */
+	private static final long MILLISECONDS_OF_ONE_DAY = 24 * 60 * 60 * 1000;
+
+	@Override
+	public void newDailyTask(DailyTaskInformation dailyTaskInformation) {
+		if (dailyTaskInformation == null || StringUtils.isEmpty(dailyTaskInformation.getTitle())
+				|| dailyTaskInformation.getCommand() == null) {
+			throw new NullPointerException();
+		}
+		if (containsDailyTask(dailyTaskInformation)) {
+			throw new ReportException("任务已存在:" + dailyTaskInformation);
+		}
+		dailyTaskInformations.add(dailyTaskInformation);
+		stop();
+		start();
+	}
+
+	/**
+	 * 判断定时任务是否已经存在
+	 * 
+	 * @param dailyTaskInformation
+	 * @return
+	 */
+	private boolean containsDailyTask(DailyTaskInformation dailyTaskInformation) {
+		if (CollectionUtils.isEmpty(dailyTaskInformations)) {
+			return false;
+		}
+		for (DailyTaskInformation d : dailyTaskInformations) {
+			if (dailyTaskInformation.equals(d)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * 建立每天定时任务
+	 * 
+	 * @param command
+	 *            所执行的任务
+	 * @param hour
+	 *            执行时间中的小时
+	 * @param minute
+	 *            执行时间中的分钟
+	 * @param second
+	 *            执行时间中的秒
+	 */
+	private void newDailyTask(Runnable command, int hour, int minute, int second) {
+		Calendar now = Calendar.getInstance();
+		now.setTime(new Date());
+		Calendar initialDelayCalendar = (Calendar) now.clone();
+		initialDelayCalendar.set(Calendar.HOUR_OF_DAY, hour);
+		initialDelayCalendar.set(Calendar.MINUTE, minute);
+		initialDelayCalendar.set(Calendar.SECOND, second);
+		if (initialDelayCalendar.before(now)) {
+			initialDelayCalendar.add(Calendar.DAY_OF_MONTH, 1);
+		}
+		scheduledExecutorService.scheduleAtFixedRate(command,
+				initialDelayCalendar.getTimeInMillis() - now.getTimeInMillis(), MILLISECONDS_OF_ONE_DAY,
+				TimeUnit.MILLISECONDS);
+
+	}
+
+	@Override
+	public void saveLog(DailyTaskLog log) {
+		if (log == null) {
+			throw new NullPointerException();
+		}
+		FileWriter fileWriter = null;
+		try {
+			fileWriter = new FileWriter(PathUtils.getAppPath() + "daily-task-log.log", true);
+			fileWriter.write(JSONObject.toJSONString(log) + "\n");
+			fileWriter.flush();
+			logger.info("Saved task log:" + JSONObject.toJSONString(log) + "\n");
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			if (fileWriter != null) {
+				try {
+					fileWriter.close();
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+		}
+	}
+
+	@Override
+	public List<DailyTaskInformation> allDailyTaskInformations() {
+		return dailyTaskInformations;
+	}
+
+	@Override
+	public String start() {
+		String message = "";
+		if (!isStopped()) {
+			message = "已存在运行的定时任务";
+			logger.error(message);
+			return message;
+		}
+		if (!CollectionUtils.isEmpty(dailyTaskInformations)) {
+			// 线程数与任务数保持一致,这样保证任务间不会互相影响
+			scheduledExecutorService = Executors.newScheduledThreadPool(dailyTaskInformations.size());
+			for (DailyTaskInformation dailyTaskInformation : dailyTaskInformations) {
+				logger.info("New daily task: " + dailyTaskInformation);
+				newDailyTask(dailyTaskInformation.getCommand(), dailyTaskInformation.getHour(),
+						dailyTaskInformation.getMinute(), dailyTaskInformation.getSecond());
+			}
+			message = "已开启定时任务:" + dailyTaskInformations;
+			logger.info(message + "\n");
+			return message;
+		} else {
+			message = "定时任务为空";
+			logger.error(message + "\n");
+			return message;
+		}
+	}
+
+	@Override
+	public String stop() {
+		String message = "";
+		if (isStopped()) {
+			message = "定时任务已经停止或者未开启过";
+			logger.error(message);
+			return message;
+		}
+		logger.info("Remove old daily tasks...");
+		scheduledExecutorService.shutdownNow();
+		message = "已关闭定时任务";
+		logger.info(message + "\n");
+		return message;
+	}
+
+	@Override
+	public boolean isStopped() {
+		if (scheduledExecutorService == null) {
+			return true;
+		}
+		return scheduledExecutorService.isShutdown() || scheduledExecutorService.isTerminated();
+	}
+
+}

+ 15 - 0
src/main/java/com/uas/report/service/FileService.java

@@ -7,6 +7,8 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.springframework.web.multipart.MultipartFile;
 
+import com.uas.report.controller.schedule.model.DailyTaskInformation;
+
 /**
  * 文件操作
  * 
@@ -192,4 +194,17 @@ public interface FileService {
 	 *            给定的pdf文件绝对路径
 	 */
 	public void createPagedPdfFiles(String pdfFileAbsolutePath);
+
+	/**
+	 * 开启删除产生的pdf等文件的每天定时任务
+	 * 
+	 * @param hour
+	 *            任务时间:小时
+	 * @param minute
+	 *            任务时间:分钟
+	 * @param second
+	 *            任务时间:秒
+	 * @return
+	 */
+	public DailyTaskInformation startDeleteGeneratedFilesDailyTask(Integer hour, Integer minute, Integer second);
 }

+ 30 - 0
src/main/java/com/uas/report/service/impl/FileServiceImpl.java

@@ -33,10 +33,14 @@ import com.itextpdf.text.Document;
 import com.itextpdf.text.DocumentException;
 import com.itextpdf.text.pdf.PdfCopy;
 import com.itextpdf.text.pdf.PdfReader;
+import com.uas.report.controller.schedule.model.DailyTaskInformation;
+import com.uas.report.controller.schedule.model.DailyTaskLog;
+import com.uas.report.controller.schedule.service.DailyTaskService;
 import com.uas.report.core.exception.ReportException;
 import com.uas.report.service.FileService;
 import com.uas.report.support.SysConf;
 import com.uas.report.util.FileUtils;
+import com.uas.report.util.ReportConstants;
 import com.uas.report.util.ReportUtils;
 import com.uas.report.util.ZipUtils;
 
@@ -45,6 +49,9 @@ public class FileServiceImpl implements FileService {
 
 	@Autowired
 	private SysConf sysConf;
+	
+	@Autowired
+	private DailyTaskService dailyTaskService;
 
 	private Logger logger = Logger.getLogger(getClass());
 
@@ -512,4 +519,27 @@ public class FileServiceImpl implements FileService {
 		return filePath;
 	}
 
+	@Override
+	public DailyTaskInformation startDeleteGeneratedFilesDailyTask(Integer hour, Integer minute, Integer second) {
+		if (hour == null || minute == null || second == null) {
+			throw new NullPointerException();
+		}
+		final DailyTaskInformation dailyTaskInformation = new DailyTaskInformation();
+		dailyTaskInformation.setTitle("定时删除pdf文件");
+		dailyTaskInformation.setHour(hour);
+		dailyTaskInformation.setMinute(minute);
+		dailyTaskInformation.setSecond(second);
+		dailyTaskInformation.setCommand(new Runnable() {
+			@Override
+			public void run() {
+				logger.info("Daily Task run...");
+				delete(ReportConstants.GENERATED_FILES_ABSOLUTE_PATH, true);
+				DailyTaskLog dailyTaskLog = new DailyTaskLog(dailyTaskInformation, new Date(), "success");
+				dailyTaskService.saveLog(dailyTaskLog);
+			}
+		});
+		dailyTaskService.newDailyTask(dailyTaskInformation);
+		return dailyTaskInformation;
+	}
+
 }

+ 35 - 0
src/main/java/com/uas/report/support/StartupProcessor.java

@@ -0,0 +1,35 @@
+package com.uas.report.support;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+
+import com.uas.report.service.FileService;
+
+public class StartupProcessor implements ApplicationListener<ContextRefreshedEvent> {
+
+	@Autowired
+	private SysConf sysConf;
+
+	@Autowired
+	private FileService fileService;
+
+	@Override
+	public void onApplicationEvent(ContextRefreshedEvent event) {
+		// 非root application context,才执行
+		if (event.getApplicationContext().getParent() != null) {
+			startDeleteGeneratedFilesDailyTask();
+		}
+	}
+
+	/**
+	 * 容器启动时开启删除产生的pdf等文件的定时任务
+	 */
+	public void startDeleteGeneratedFilesDailyTask() {
+		if (sysConf.isDailyTaskAutoStart()) {
+			fileService.startDeleteGeneratedFilesDailyTask(sysConf.getDailyTaskHour(), sysConf.getDailyTaskMinute(),
+					sysConf.getDailyTaskSecond());
+		}
+	}
+
+}

+ 56 - 0
src/main/java/com/uas/report/support/SysConf.java

@@ -47,6 +47,30 @@ public class SysConf {
 	@Value("#{sys.standardJrxmlsUrl}")
 	private String standardJrxmlsUrl;
 
+	/**
+	 * 每天定时任务是否自动开启
+	 */
+	@Value("#{sys.dailyTaskAutoStart}")
+	private boolean dailyTaskAutoStart;
+
+	/**
+	 * 每天定时任务时间中的小时
+	 */
+	@Value("#{sys.dailyTaskHour}")
+	private int dailyTaskHour;
+
+	/**
+	 * 每天定时任务时间中的分钟
+	 */
+	@Value("#{sys.dailyTaskMinute}")
+	private int dailyTaskMinute;
+
+	/**
+	 * 每天定时任务时间中的秒
+	 */
+	@Value("#{sys.dailyTaskSecond}")
+	private int dailyTaskSecond;
+
 	public String getLocalBaseDir() {
 		return localBaseDir;
 	}
@@ -94,4 +118,36 @@ public class SysConf {
 	public void setStandardJrxmlsUrl(String standardJrxmlsUrl) {
 		this.standardJrxmlsUrl = standardJrxmlsUrl;
 	}
+
+	public boolean isDailyTaskAutoStart() {
+		return dailyTaskAutoStart;
+	}
+
+	public void setDailyTaskAutoStart(boolean dailyTaskAutoStart) {
+		this.dailyTaskAutoStart = dailyTaskAutoStart;
+	}
+
+	public int getDailyTaskHour() {
+		return dailyTaskHour;
+	}
+
+	public void setDailyTaskHour(int dailyTaskHour) {
+		this.dailyTaskHour = dailyTaskHour;
+	}
+
+	public int getDailyTaskMinute() {
+		return dailyTaskMinute;
+	}
+
+	public void setDailyTaskMinute(int dailyTaskMinute) {
+		this.dailyTaskMinute = dailyTaskMinute;
+	}
+
+	public int getDailyTaskSecond() {
+		return dailyTaskSecond;
+	}
+
+	public void setDailyTaskSecond(int dailyTaskSecond) {
+		this.dailyTaskSecond = dailyTaskSecond;
+	}
 }

+ 5 - 0
src/main/java/com/uas/report/util/ReportConstants.java

@@ -58,6 +58,11 @@ public class ReportConstants {
 	 */
 	public static final String GENERATED_FILES_PATH = "resources/generate/";
 
+	/**
+	 * 生成的pdf、excel等文件的绝对路径
+	 */
+	public static final String GENERATED_FILES_ABSOLUTE_PATH = PathUtils.getAppPath() + GENERATED_FILES_PATH;
+
 	/**
 	 * jdbc配置文件的绝对路径
 	 */

+ 6 - 1
src/main/resources/dev/sys.properties

@@ -3,4 +3,9 @@ localImagesDir=/Picture
 localJrxmlDir=/jrxml
 standardMaster=STANDARD_MASTER
 hasStandardJrxmls=false
-standardJrxmlsUrl=http://print.ubtob.com/report/file/standardJrxmls?userName=%s&onlyData=1
+standardJrxmlsUrl=http://print.ubtob.com/report/file/standardJrxmls?userName=%s&onlyData=1
+#schedule
+dailyTaskAutoStart=true
+dailyTaskHour=3
+dailyTaskMinute=0
+dailyTaskSecond=0

+ 6 - 1
src/main/resources/prod/sys.properties

@@ -3,4 +3,9 @@ localImagesDir=/Picture
 localJrxmlDir=/jrxml
 standardMaster=STANDARD_MASTER
 hasStandardJrxmls=true
-standardJrxmlsUrl=http://print.ubtob.com/report/file/standardJrxmls?userName=%s&onlyData=1
+standardJrxmlsUrl=http://print.ubtob.com/report/file/standardJrxmls?userName=%s&onlyData=1
+#schedule
+dailyTaskAutoStart=true
+dailyTaskHour=3
+dailyTaskMinute=0
+dailyTaskSecond=0

+ 6 - 1
src/main/resources/test/sys.properties

@@ -3,4 +3,9 @@ localImagesDir=/Picture
 localJrxmlDir=/jrxml
 standardMaster=STANDARD_MASTER
 hasStandardJrxmls=true
-standardJrxmlsUrl=http://192.168.253.60:8090/report/file/standardJrxmls?userName=%s&onlyData=1
+standardJrxmlsUrl=http://192.168.253.60:8090/report/file/standardJrxmls?userName=%s&onlyData=1
+#schedule
+dailyTaskAutoStart=true
+dailyTaskHour=3
+dailyTaskMinute=0
+dailyTaskSecond=0

+ 10 - 1
src/main/webapp/WEB-INF/views/console.html

@@ -62,7 +62,6 @@
 					<li><a target="_blank">file/delete?filePath=C:/test&isAbsolutePath=true</a></li>
 				</ol>
 			</ol>
-
 		</div>
 		<div id="rightContainer">
 			<h2>3. 标准模板</h2>
@@ -106,6 +105,16 @@
 					<li><a target="_blank">fileUpload?userName=B2B/1111</a></li>
 				</ol>
 			</ol>
+
+			<h2>5.定时任务</h2>
+			<ol>
+				<li><a target="_blank">schedule/deleteGeneratedFiles?hour=3&minute=0&second=0</a></li>
+				<li><a target="_blank">schedule/tasks</a></li>
+				<li><a target="_blank">schedule/start</a></li>
+				<li><a target="_blank">schedule/stop</a></li>
+				<li><a target="_blank">schedule/restart</a></li>
+				<li><a target="_blank">schedule/isStopped</a></li>
+			</ol>
 		</div>
 	</div>
 </body>

+ 14 - 0
src/main/webapp/WEB-INF/webmvc.xml

@@ -7,6 +7,20 @@
 	<mvc:annotation-driven>
 		<!-- 解决Controller返回String时中文乱码的问题 -->
 		<mvc:message-converters>
+			<!-- 避免IE执行AJAX时,返回JSON出现下载文件 -->
+			<bean id="fastJsonHttpMessageConverter"
+				class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
+				<property name="supportedMediaTypes">
+					<list>
+						<value>application/json;charset=UTF-8</value>
+					</list>
+				</property>
+				<property name="features">
+					<list>
+						<value>DisableCircularReferenceDetect</value>
+					</list>
+				</property>
+			</bean>
 			<bean class="org.springframework.http.converter.StringHttpMessageConverter">
 				<!-- 不可为text/plain -->
 				<property name="supportedMediaTypes" value="text/html;charset=UTF-8" />