本篇主要介绍两种方式实现SpringMVC的服务器端推送。
方式一,基于SSE(服务器端发送事件)的服务器端推送。
这种方式的流程类似于Ajax。
首先在src/main/resources/views下面创建see.jsp。代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Insert title here</title></head><body> <div id="msgFromPush"></div> <script type="text/javascript" src="<c:url value="assets/js/jquery-3.1.1.js" />"></script> <script type="text/javascript"> if (!!window.EventSource) { var source = new EventSource('push'); s = ''; source.addEventListener('message', function(e) { s += e.data + "<br/>"; $("#msgFromPush").html(s); }); source.addEventListener('open', function(e) { console.log("连接打开"); }, false); source.addEventListener('error',function(e){ if(e.readyState==EventSource.CLOSED){ console.log("连接关闭"); }else{ console.log(e.readyState); } }); }else{ console.log("您的浏览器不支持SSE"); } </script></body></html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
1.我们在jsp页面上,首先得到EventSource对象,由于EventSource是新式浏览器才支持的对象,所以我们要先判断当前的浏览器是否支持。
2.当浏览器支持EventSource对象时,我们在创建EventSource对象的时候,同时向指定的url路径发送了请求(构造参数就是要请求的url地址)。
3.我们然后使用EventSource对象来添加事件监听,监听连接打开、连接关闭、连接成功之后的事件,并通过回调函数的方式将服务器端的响应作为回调函数的参数传入,之后我们加以处理。
服务器端的控制器:
/** * @author BeautifulSoup * SSE(基于服务器端发送事件的服务器端推送) */@Controllerpublic class SseController { /** * @return * 演示服务器端每5秒钟向浏览器推送随机消息 */ @RequestMapping(value="/push",produces="text/event-stream") public @ResponseBody String push(){ Random r=new Random(); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return "data:Testing 1,2,3"+r.nextInt()+"\n\n"; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
控制器将输出的媒体类型(参数produces)指定为text/event-stream,此时就开启了服务器端对SSE的支持。
此时我们访问jsp页面就会自动向控制器发送请求,然后服务器每隔5秒向客户端发送一次响应实现了服务器端的推送。
方式二:基于Servlet3.0+的异步方法特性实现服务器端的推送
首先在核心配置类(Servlet3.0取消了web.xml的使用)中开启异步方法支持:
public class WebInitializer implements WebApplicationInitializer { public void onStartup(ServletContext servletContext) throws ServletException { AnnotationConfigWebApplicationContext ctx=new AnnotationConfigWebApplicationContext(); ctx.register(MyMvcConfig.class); ctx.setServletContext(servletContext); Dynamic servlet=servletContext.addServlet("dispatcher", new DispatcherServlet(ctx)); servlet.addMapping("/"); servlet.setLoadOnStartup(1); servlet.setAsyncSupported(true); }}
在SpringMVC的组件配置上开启计划任务的支持:
@Configuration@EnableWebMvc@EnableScheduling @ComponentScan("com.fuyunwang")public class MyMvcConfig extends WebMvcConfigurerAdapter { @Bean public InternalResourceViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/classes/views/"); viewResolver.setSuffix(".jsp"); viewResolver.setViewClass(JstlView.class); return viewResolver; } @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/sse").setViewName("/sse"); registry.addViewController("/async").setViewName("/async"); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
在src/main/resources/views下创建async.jsp,在客户端发送ajax请求:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Insert title here</title></head><body> <script type="text/javascript" src="assets/js/jquery-3.1.1.js"></script> <script type="text/javascript"> deferred(); function deferred() { $.get('defer', function(data) { console.log(data); deferred(); }); } </script></body></html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
由于异步方法的实现是通过定时任务来完成的,所以我们需要创建定时任务(通过DeferredResult从另外一个线程返回实现),并在Controller中开启定时任务。
/** * @author BeautifulSoup * Servlet3.0异步推送方式的Service,这就是定时任务 */@Servicepublic class PushService { private DeferredResult<String> deferredResult; public DeferredResult<String> getAsyncUpdate(){ deferredResult=new DeferredResult<String>(); return deferredResult; } @Scheduled(fixedDelay=5000) public void refresh(){ if(deferredResult!=null){ deferredResult.setResult(new Long(System.currentTimeMillis()).toString()); } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
/** * @author BeautifulSoup * Servelet3.0+异步方法来保证服务器端的推送技术 */@Controllerpublic class AsyncController { @Autowired public PushService pushService; @RequestMapping("/defer") @ResponseBody public DeferredResult<String> deferredCall(){ return pushService.getAsyncUpdate(); }}
Servlet3.0+异步方法实现服务器端推送,当我们向服务器端发送请求的时候,控制器会通过定时任务来定时更新DeferredResult,从而实现了延迟响应。然后我们在服务器端轮询服务器端,实现了服务器端每隔一定时间响应的服务器端推送。