@EnableWebMvc以及有关WebSocket的一些心得

有关@EnableWebMvc

打开@EnableWebMvc查看官方注释,:

 * Adding this annotation to an {@code @Configuration} class imports the Spring MVC
 * configuration from {@link WebMvcConfigurationSupport}, e.g.:

意思就是这个标签注释与@Configuration一起使用,注释在springMvc的配置类上以配置自己需要的springMvc配置

 * <p>To customize the imported configuration, implement the interface
 * {@link WebMvcConfigurer} and override individual methods, e.g.:

这段和上面意思差不多,它实现了WebMvcConfigurer接口,可以通过override很简单的重写原来的配置,至于@EnableWebMvc应该是可注释可不注释

 * <p>If {@link WebMvcConfigurer} does not expose some more advanced setting that
 * needs to be configured consider removing the {@code @EnableWebMvc}
 * annotation and extending directly from {@link WebMvcConfigurationSupport}
 * or {@link DelegatingWebMvcConfiguration}, e.g.:

这段意思就是如果WebMvcConfigurer提供的方法不够用了,可以继承DelegatingWebMvcConfiguration或WebMvcConfigurationSupport并且移除@EnableWebMvc

这三段注释看起来解释的都是一件事,@EnableWebMvc作用:声明被注释类是配置类,并且导入DelegatingWebMvcConfiguration类,很简单,而且实际开发中我们经常使用的WebMvcConfigurer接口并不需要注册@EnableWebMvc也能使用;
它主要关键在于导入DelegatingWebMvcConfiguration类上,这个类它是WebMvcConfigurationSupport的子类,而自动配置类中有这样一段注释:

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
		TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
缺少WebMvcConfigurationSupport类自动注释才会生效,也就是说当我们在配置类上注释了@EnableWebMvc时,自动配置就没了!这一点对那些对spring框架不了解的程序员来说十分不友好,尤其是springBoot框架下,什么静态资源失效,什么映射找不到,那都是家常便饭;
所以如果除非是特殊情况下,最好不要在配置类上注释@EnableWebMvc,其实这个注释现在也基本不用了,如果要简单重写配置,实现WebMvcConfigurer接口即可,@EnableWebMvc加上与否决定是否屏蔽@EnableAutoConfiguration,如果WebMvcConfigurer提供的设置不够,直接继承WebMvcConfigurationSupport类即可,无论加不加@EnableWebMvc接口,都会屏蔽@EnableAutoCongfiguration

WebSocket

webSocket是一种支持后端主动与前端通信的协议,它有两种实现方式:
纯注解方法:
(1)将ServerEndpointExporter实例注入到spring容器进行管理

@Configuration
public class webSocketConfig {
//	@Bean
//	public ServerEndpointExporter getEndPoint() {
//		return new ServerEndpointExporter();
//	}
}

(2) 实现消息处理类,前后端的通过socket发送接收的消息都在这个类里处理

//package com.fh.config;
//
//import java.io.IOException;
//import java.util.concurrent.ConcurrentHashMap;
//
//import javax.websocket.OnClose;
//import javax.websocket.OnError;
//import javax.websocket.OnMessage;
//import javax.websocket.OnOpen;
//import javax.websocket.Session;
//import javax.websocket.server.PathParam;
//import javax.websocket.server.ServerEndpoint;
//import org.springframework.stereotype.Component;
//
//import com.alibaba.fastjson.JSON;
//import com.alibaba.fastjson.JSONObject;
//
//
//@ServerEndpoint("/web/{sid}") 配置映射
//@Component
//public class MyWebSocket {
//	// 存储回话
//	private static ConcurrentHashMap<String,Session> map = 
//			new ConcurrentHashMap<>(); 线程安全
//	
//	@OnOpen 当有新客户端发起连接时调用
//	public void testonOpen(Session session,@PathParam("sid") String sid) {
//		map.put(sid, session);
//		System.out.println("服务端接到到客户端编号:"+sid+"的请求连接,已连接");
//		System.out.println("当前服务端连接总数为" + map.size());
//	}
//	
//	@OnClose 当由客户端断开连接时调用,多半是网页关闭
//	public void testonClose(@PathParam("sid") String sid) {
//		System.out.println("服务端接收到编号为"+sid+"的客户端的断开连接请求,已断开");
//		map.remove(sid);
//	}
//	
//	@OnMessage 当客户端给服务端发送消息时调用
//	public void testOnMessage(String message,Session session,@PathParam("sid") String sid) {
//		JSONObject object = JSON.parseObject(message);
//		System.out.println("客户端编号为" + sid);
//		System.out.println("服务端接收到消息,消息为:"+object.getString("message"));
//		System.out.println("开始发送消息到特定客户端");
//		try {
//			map.get(object.getString("sid")).getBasicRemote().sendText(object.getString("message"));
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//	}
//	
//	@OnError 出错时调用,Throwable参数必须有
//	public void testOnError(@PathParam("sid") String sid,Throwable error) {
//		System.out.println("编号为"+sid+"客户端出错,请检查");
//	}
//}

手动配置方法(老框架使用):
(1)消息处理类,同第一种方法的(2)

@Component
public class MySocketHandler extends TextWebSocketHandler {
	
	private static ConcurrentHashMap<String,WebSocketSession> map = 
	new ConcurrentHashMap<>();
	
	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		// TODO Auto-generated method stub
		map.put((String)session.getAttributes().get("id"), session);
		System.out.println("已经有一个客户端连接,当前连接数:"+ map.size());
		super.afterConnectionEstablished(session);
	}

	@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("接收到客户端发来的消息,"+(String)session.getAttributes().get("id")+"消息为:"+ message.getPayload());
		super.handleTextMessage(session, message);
	}

	@Override
	public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("客户端"+(String)session.getAttributes().get("id")+"出错");
		super.handleTransportError(session, exception);
	}

	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("已经有一个客户端退出,退出客户端为"+(String)session.getAttributes().get("id")
				+ ",当前连接数:"+ map.size());
		super.afterConnectionClosed(session, status);
	}
	
}

afterConnectionEstablished对应@OnOpen,handleTextMessage对应@OnMessage,handleTransportError对应@OnError,afterConnectionClosed对应@OnClose

(2)拦截器,用于拦截客户端发起的连接请求以及后续的消息

public class MySocketIntercepter implements HandshakeInterceptor {

	@Override
	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
			Map<String, Object> attributes) throws Exception {
		// TODO Auto-generated method stub
		if (request instanceof ServletServerHttpRequest) {
			HttpServletRequest rq2 = ((ServletServerHttpRequest) request).
					getServletRequest();
			String id = rq2.getParameter("id");
			attributes.put("id", id);
			return true;
		}
		System.out.println("websocket连接已经映射到,拦截器取值出错");
		return false;
	}

	@Override
	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
			Exception exception) {
		// TODO Auto-generated method stub

	}

}

注意消息处理类是无法直接获取请求的,所以如果客户端发来的连接请求带有数据,比如客户端的id之类的,必须在拦截器中处理

(3)注册配置类

@Configuration
@EnableWebSocket
public class TestConfig  implements  WebSocketConfigurer {
	@Autowired
	MySocketHandler handler;
	
	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry arg0) {
		// TODO Auto-generated method stub
		arg0.addHandler(handler, "/webForSSM").addInterceptors(new MySocketIntercepter());
		System.out.println("已经映射");
	}

}

@EnableWebSocket注册到该类上时标识该类是WebSocket的注册类,@Configuration并不是必须的,你用@Componoent也可以,总之能被spring容器扫描即可;

然后是前端的实现:

var websocket;
	function on() {
		if (typeof(WebSocket) == "undefined") {
			alert("此浏览器不支持WebSocket,连接失败;");
		} else {
			var basePath = [[${basePath}]];
			webSocket = new WebSocket((basePath +"webForSSM?id=1").replace('http','ws'));
			webSocket.onopen = function() {
				$(".res").html($(".res").html() + "已打开");
			//$("div.res").html("已连接");
			}
			webSocket.onmessage = function (event) {
				$(".res").html($(".res").html() + event.data);
			}
		}
		
	}
	
	function send() {
		var value = {message :$(".socketText").val(),
				sid : '2'};
		
		webSocket.send(JSON.stringify(value));
	}

如果浏览器支持WebSocket,直接new即可,否则也可以使用socketjs,后端注册类时要改动一下:

@Configuration
@EnableWebSocket
public class TestConfig  implements  WebSocketConfigurer {
	@Autowired
	MySocketHandler handler;
	
	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry arg0) {
		// TODO Auto-generated method stub
//		arg0.addHandler(handler, "/webForSSM").addInterceptors(new MySocketIntercepter());
//		System.out.println("已经映射");
		arg0.addHandler(handler, "/webForSSM").
		addInterceptors(new MySocketIntercepter()).withSockJS();

	}

}

对了,有关之前的配置消息解析器为什么可以@Bean创建:

@Configuration
public class ConfigurationSet {
	
	@Bean
	public StringHttpMessageConverter getConverter() {
		StringHttpMessageConverter temp = new StringHttpMessageConverter();
		temp.setDefaultCharset(Charset.forName("utf-8"));
		return temp;
	}

这个是springboot的优点之一,@Bean会自动将注册到容器中的消息转换器放入到消息转换器的list集合中,也就是自动配置;
注意:转换器自动注册机制是由WebMvcAutoConfiguration实现的,如果你的项目继承了WebMvcConfigurationSupport或者它的子类,会失效,只能手动配置


参考的文章:https://charming.blog.csdn.net/article/details/84636521?utm_medium=distribute.pc_relevant.none-task-blog-2defaultCTRLISTdefault-2.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2defaultCTRLISTdefault-2.no_search_link

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值