SpringBoot的DeferredResult实例:二、另一个请求控制请求的返回时机

SpringBoot的DeferredResult实例:二、另一个请求控制请求的返回时机

本系列文章均采用springboot,采用同样的环境。

一、创建工程

1、 在http://start.spring.io/中创建RabbitMQHello工程:

      A、MAVEN工程

      B、2.0.0.BUILD-SNAPSHOT

      C 、Group:com.example

      D、Artifact:RabbitMQHello

      E、Packaging:jar

      F、[Java](http://lib.csdn.net/base/java)Version:1.8

G、WEB、勾选Thymeleaf

2、下载工程、解压,然后导入eclipse中

3、修改pom.xml以便于热部署

A、在dependencies中增加spring-boot-devtools

    <dependency>  
          <groupId>org.springframework.boot</groupId>  
          <artifactId>spring-boot-devtools</artifactId>  
          <optional>true</optional>  
    </dependency>  

B、在build的spring-boot-maven-plugin中增加依赖包

    <build>  
        <plugins>  
            <plugin>  
                <groupId>org.springframework.boot</groupId>  
                <artifactId>spring-boot-maven-plugin</artifactId>  
                <dependencies>  
                    <!-- spring热部署 -->  
                    <dependency>  
                        <groupId>org.springframework</groupId>  
                        <artifactId>springloaded</artifactId>  
                        <version>1.2.6.RELEASE</version>  
                    </dependency>  
                </dependencies>  
            </plugin>  
        </plugins>  
    </build>  

4、增加日志配置文件

在src/main/resources下增加文件logback.xml,内容如下

    <?xml version="1.0" encoding="UTF-8"?>  
    <configuration>      
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">      
            <encoder>      
                <pattern>%d %p (%file:%line\)- %m%n</pattern>    
                <charset>GBK</charset>   
            </encoder>      
        </appender>      
        <appender name="baselog"      
            class="ch.qos.logback.core.rolling.RollingFileAppender">      
            <File>log/base.log</File>      
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">      
                <fileNamePattern>log/base.log.%d.%i</fileNamePattern>      
                <timeBasedFileNamingAndTriggeringPolicy  class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">      
                    <maxFileSize>64 MB</maxFileSize>      
                </timeBasedFileNamingAndTriggeringPolicy>      
            </rollingPolicy>      
            <encoder>      
                <pattern>      
                    %d %p (%file:%line\)- %m%n    
                </pattern>      
                <charset>UTF-8</charset>  
            </encoder>      
        </appender>      
        <root level="info">      
            <appender-ref ref="STDOUT" />      
        </root>      
        <logger name="com.example" level="DEBUG">      
            <appender-ref ref="baselog" />      
        </logger>      
    </configuration>    

二、編制请求、响应实体

A、请求实体RequestMsg

package com.example;

import java.io.Serializable;

public class RequestMsg implements Serializable{

	private static final long serialVersionUID = 1L;
	
	private String param;
	
	public RequestMsg() {
		super();
	}

	public RequestMsg(String param) {
		super();
		this.param = param;
	}

	public String getParam() {
		return param;
	}

	public void setParam(String param) {
		this.param = param;
	}
}

注意空的构造函数,必须有的

B、响应实体

package com.example;

import java.io.Serializable;

public class ResponseMsg<T> implements Serializable{

	private static final long serialVersionUID = 1L;
	
	public static final Integer STATUS_SUCCESS = 0;
	public static final Integer STATUS_FAILED = -1;
	
	private int status;
	
	private String msg;
	
	private T data;

	/**
	 * 空构造函数
	 */
	public ResponseMsg() {
		super();
		
	}
	/**
	 * 全字段构造函数
	 * @param status
	 * @param msg
	 * @param data
	 */
	public ResponseMsg(int status, String msg, T data) {
		super();
		this.status = status;
		this.msg = msg;
		this.data = data;
	}
	/**
	 * 失败
	 * @param message
	 */
	public void fail(String message){
		this.setStatus(STATUS_FAILED);
		this.setMsg(message);
	}
	/**
	 * 成功
	 * @param message
	 * @param data
	 */
	public void success(String message,T data){
		this.setStatus(STATUS_SUCCESS);
		this.setMsg(message);
		this.setData(data);
	}

	public int getStatus() {
		return status;
	}

	public void setStatus(int status) {
		this.status = status;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public T getData() {
		return data;
	}

	public void setData(T data) {
		this.data = data;
	}

	@Override
	public String toString() {
		return "ResponseMsg [status=" + status + ", msg=" + msg + ", data=" + data + "]";
	}

}

三、编制控制器

package com.example;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class DeferredController {
	
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@RequestMapping("/")
        public String index(Model model){
	      logger.debug("进入index");
              return "index";
        }
}

这个控制器,简单,就是返回index.html页面

四、编制rest控制器

package com.example;

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;

@RestController
@RequestMapping("/api")
public class DeferredRestController {
	
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	private final Map<Integer,DeferredResult<ResponseMsg<String>>> responseBodyMap=new HashMap<Integer,DeferredResult<ResponseMsg<String>>>();
	private final Map<Integer,RequestMsg> requestBodyMap=new HashMap<Integer,RequestMsg>();
	
	/**
	 * 第一个请求
	 * @param req
	 * @return
	 */
	@RequestMapping("/request1")
	@ResponseBody
    public DeferredResult<ResponseMsg<String>> request1(RequestMsg req){
		logger.debug("request1:请求参数{}",req.getParam());
		DeferredResult<ResponseMsg<String>> result = new DeferredResult<ResponseMsg<String>>();
		requestBodyMap.put(1, req);//把请求放到第一个请求map中
		responseBodyMap.put(1, result);//把请求响应的DeferredResult实体放到第一个响应map中
		return result;
    }
	
	/**
	 * 第二个请求
	 * @param req
	 * @return
	 */
	@RequestMapping("/request2")
	@ResponseBody
    public DeferredResult<ResponseMsg<String>> request2(RequestMsg req){
		logger.debug("request2:请求参数{}",req.getParam());
		DeferredResult<ResponseMsg<String>> result = new DeferredResult<ResponseMsg<String>>();
		requestBodyMap.put(2, req);//把请求放到第二个请求map中
		responseBodyMap.put(2, result);//把请求响应的DeferredResult实体放到第二个响应map中
		return result;
    }
	
	/**
	 * 第三个请求
	 * @param req
	 * @return
	 */
	@RequestMapping("/request3")
	@ResponseBody
    public DeferredResult<ResponseMsg<String>> request3(RequestMsg req){
		logger.debug("request3:请求参数{}",req.getParam());
		DeferredResult<ResponseMsg<String>> result = new DeferredResult<ResponseMsg<String>>();
		requestBodyMap.put(3, req);//把请求放到第三个请求map中
		responseBodyMap.put(3, result);//把请求响应的DeferredResult实体放到第三个响应map中
		return result;
    }
	
	/**
	 * 控制第x个请求执行返回操作,同时自己也返回同样的值
	 * @param x
	 * @return
	 */
	@RequestMapping(value="/requestXReturn",method=RequestMethod.POST)
	@ResponseBody
    public ResponseMsg<String> request1Return(Integer x){
		ResponseMsg<String> msg=new ResponseMsg<String>();
		logger.debug("requestXReturn--1:请求参数{}",x);
		DeferredResult<ResponseMsg<String>> result =responseBodyMap.get(x);
		if (result==null){
			msg.fail("錯誤!请求已经释放");
			return msg;
		}
		String resultStr="result"+x.toString()+". Received:"+requestBodyMap.get(x).getParam();
		msg.success("成功", resultStr);
		result.setResult(msg);//设置DeferredResult的结果值,设置之后,它对应的请求进行返回处理
		responseBodyMap.remove(x);//返回map删除
		logger.debug("requestXReturn--2:请求参数{}",x);
		logger.debug("requestXReturn--3:返回参数{}",msg);
		return msg;
    }
}

1、定义了两个map,分别用于保存请求对象和返回对象。

2、三个请求,分别把各自的请求、响应对象存放到对应键值的map中

3、控制返回的,到map中取出对象,然后对响应对象进行设值(result.setResult(msg);),使相应的请求发起链接进行返回处理。

五、页面制作

1、这个页面需要jQuery,下载jquery放到src/main/resources/static下

2、在src/main/resources/templates/下新建文件index.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>DeferredResult Demo</title>
</head>
<body>
    <div>
		第一个请求参数:<input type="text" name="request1Request" id="request1RequestId"/><br/>
		第一个请求返回:<input type="text" name="request1Result" id="request1ResultId"/><br/> 
		<input type="button" value="第一个请求" id="button1Request" οnclick="button1RequestClick()"/>
	</div>
	<div>
		第二个请求参数:<input type="text" name="request2Request" id="request2RequestId"/><br/>
		第二个请求返回:<input type="text" name="request2Result" id="request2ResultId"/><br/> 
		<input type="button" value="第二个请求" id="button2Request"  οnclick="button2RequestClick()"/>
	</div>
	<div>
		第三个请求参数:<input type="text" name="request31Request" id="request3RequestId"/><br/>
		第三个请求返回:<input type="text" name="request3Result" id="request3ResultId"/><br/> 
		<input type="button" value="第三个请求" id="button3Request"  οnclick="button3RequestClick()"/>
	</div>
	<div>
		<input type="button" value="第二个请求返回" id="button2GetReturn"  οnclick="buttonXGetReturnClick(2)"/>
	</div>
	<div>
		<input type="button" value="第三个请求返回" id="button3GetReturn"  οnclick="buttonXGetReturnClick(3)"/>
	</div>
	<div>
		<input type="button" value="第一个请求返回" id="button1GetReturn" οnclick="buttonXGetReturnClick(1)"/>
	</div>
   	<script th:src="@{jquery-1.12.4.min.js}" type="text/javascript"></script>
	<script th:inline="javascript">
    	function button1RequestClick(){
    		//$('#button1Request').attr('disabled',"true");//添加disabled属性
			//$('#button2Request').removeAttr("disabled"); //移除disabled属性 
    		var param=$("#request1RequestId").val();
    		$.ajax({
    			timeout:30000,
    			async:true,
         		type:'post',
    			url:'/api/request1',
    			dataType : 'json',
    			data : {
    				'param' : param
    			},
    			success : function(data) {
    				console.log(data);
    				$("#request1ResultId").val(data.data);
    			},
    			error : function(data) {
    				console.log("button1RequestClick---error");
    				console.log(data);
    				//alert("错误消息:" + data);
    			}
    		});
    	};
    	function button2RequestClick(){
    		var param=$("#request2RequestId").val();
    		$.ajax({
    			timeout:30000,
    			async:true,
         		type:'post',
    			url:'/api/request2',
    			dataType : 'json',
    			data : {
    				'param' : param
    			},
    			success : function(data) {
    				console.log(data);
    				$("#request2ResultId").val(data.data);
    			},
    			error : function(data) {
    				console.log("button2RequestClick---error");
    				console.log(data);
    				//alert("错误消息:" + data);
    			}
    		});
    	};
    	
    	function button3RequestClick(){
    		var param=$("#request3RequestId").val();
    		$.ajax({
    			timeout:30000,
    			async:true,
         		type:'post',
    			url:'/api/request3',
    			dataType : 'json',
    			data : {
    				'param' : param
    			},
    			success : function(data) {
    				console.log(data);
    				$("#request3ResultId").val(data.data);
    			},
    			error : function(data) {
    				console.log("button3RequestClick---error");
    				console.log(data);
    				//alert("错误消息:" + data);
    			}
    		});
    	};
    	
    	function buttonXGetReturnClick(x){
    		console.log("buttonXGetReturnClick参数:"+x)
    		$.ajax({
         		type:'post',
    			url:'/api/requestXReturn',
    			dataType : 'json',
    			data : {
    				'x' : x
    			},
    			success : function(data) {
    				console.log("buttonXGetReturnClick:success");
    				console.log(data);
    			},
    			error : function(data) {
    				console.log("buttonXGetReturnClick---error");
    				console.log(data);
    			}
    		});
    	};
</script> 
</body>
</html>

这个页面完全的普通思路,没有任何技巧,包括六个按钮,三个请求按钮分别调用三个请求;三个请求返回控制,调用控制返回。

六、小结

整个demo,就是为了体现DeferredResult的思想。DeferredResult执行的分成两段(第一段,发起DeferredResult;第二段,等待别的线程设置DeferredResult的resukt值,DeferredResult开始执行后续事项及返回值),这两段在两个独立的线程中。通过这个模型,我们就可以实现线程的耦合。

本篇仅仅是一个简单的demo,没有考虑超时,以及公共的返回方法,在后续章节中一一呈现。


原文:SpringBoot的DeferredResult实例:二、另一个请求控制请求的返回时机_lxhjh的博客-CSDN博客
作者:lxhjh