博文背景
netty如果整合到springboot中,让spring的IOC容器可以监管到handler,handler可以使用IOC容器中的bean,开发就会很惬意了。
参考资料
https://blog.csdn.net/yuanzhenwei521/article/details/79194275 方案1
https://juejin.cn/post/6844904197129764871 方案2
https://www.cnblogs.com/chenpi/p/9696310.html CommandLineRunner解析
https://www.jianshu.com/p/5d4ffe267596 CommandLineRunner与ApplicationRunner接口
https://www.jianshu.com/p/0269a8390d5f ApplicationListener 接口相关
https://www.cnblogs.com/loong-hon/p/10917755.html Spring中ApplicationContextAware的作用
解决方案1:CommandLineRunner
利用spring的CommandLineRunner接口,来实现具体的策略。
首先,将你的server和handler都加上@compont注解,并且在server中正常使用handler进行注入,server中写的是ServerBootstrap那套启动代码,handler是继承ChannelInboundHandlerAdapter的子类,代码如下:
@Slf4j
@Component
public class ProtoServer {
@Autowired
private ProtoHandler protoHandler;
/**
* 默认监听29999端口
*/
public void listen() {
this.listenWithPort(29999);
}
@Slf4j
@Component
public class ProtoHandler extends ChannelInboundHandlerAdapter {
@Autowired
private ISessionCache concurrentMapSessionCache;
然后,找一个服务初始化类(根据自己项目结构自己写一个),实现CommandLineRunner接口,将监听的代码放到run方法中。
@Component
@Slf4j
public class ServerStarter implements CommandLineRunner {
@Autowired
private ProtoServer protoServer;
@Value("${proto.server.port}")
private int protoServerPort;
@Override
public void run(String... args) throws Exception {
try {
log.info("启动protoserver,监听:"+ protoServerPort);
protoServer.listenWithPort(protoServerPort);
} catch (Exception e) {
log.error("protoServer启动失败...,失败原因:"+e.getMessage());
}
}
}
注意:run接口里的代码要加try-catch捕获异常,否则CommandLineRunner接口实现类启动失败,整个系统也无法启动。
解决方案2:ApplicationRunner, ApplicationListener, ApplicationContextAware
解决方案2中用到了几个接口,ApplicationRunner跟CommandLineRunner类似,只是run方法的参数变了,其他没变化。
ApplicationListener 是监听容器关闭的事件,配合netty可以优雅的释放netty的资源。
ApplicationContextAware 通过它Spring容器会自动把上下文环境对象调用ApplicationContextAware接口中的setApplicationContext方法。我们在ApplicationContextAware的实现类中,就可以通过这个上下文环境对象得到Spring容器中的Bean。
/**
* 初始化Netty服务
* @author Administrator
*/
@Component
public class NettyBootsrapRunner implements ApplicationRunner, ApplicationListener<ContextClosedEvent>, ApplicationContextAware {
private static final Logger LOGGER = LoggerFactory.getLogger(NettyBootsrapRunner.class);
@Value("${netty.websocket.port}")
private int port;
@Value("${netty.websocket.ip}")
private String ip;
@Value("${netty.websocket.path}")
private String path;
@Value("${netty.websocket.max-frame-size}")
private long maxFrameSize;
private ApplicationContext applicationContext;
private Channel serverChannel;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void run(ApplicationArguments args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup);
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.localAddress(new InetSocketAddress(this.ip, this.port));
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new HttpObjectAggregator(65536));
pipeline.addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if(msg instanceof FullHttpRequest) {
FullHttpRequest fullHttpRequest = (FullHttpRequest) msg;
String uri = fullHttpRequest.uri();
if (!uri.equals(path)) {
// 访问的路径不是 websocket的端点地址,响应404
ctx.channel().writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND))
.addListener(ChannelFutureListener.CLOSE);
return ;
}
}
super.channelRead(ctx, msg);
}
});
pipeline.addLast(new WebSocketServerCompressionHandler());
pipeline.addLast(new WebSocketServerProtocolHandler(path, null, true, maxFrameSize));
/**
* 从IOC中获取到Handler
*/
pipeline.addLast(applicationContext.getBean(WebsocketMessageHandler.class));
}
});
Channel channel = serverBootstrap.bind().sync().channel();
this.serverChannel = channel;
LOGGER.info("websocket 服务启动,ip={},port={}", this.ip, this.port);
channel.closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public void onApplicationEvent(ContextClosedEvent event) {
if (this.serverChannel != null) {
this.serverChannel.close();
}
LOGGER.info("websocket 服务停止");
}
}