用t-io来写一个网页聊天室或客服是个怎样的体验
用t-io来写一个网页聊天室或客服是个怎样的体验
talent-tan 发表于3周前
用t-io来写一个网页聊天室或客服是个怎样的体验
  • 发表于 3周前
  • 阅读 5091
  • 收藏 147
  • 点赞 15
  • 评论 41
移动开发云端新模式探索实践 >>>   
摘要: tio-websocket-server的首发教程,并且是以showcase的形式展现的----不仅仅是个教程,还是个可以放心使用的脚手架。
        在t-io的官方主群,经常会有用户发出这样的感叹:“用tio写一个聊天室或在线客服,几乎就是个分分钟的事儿”。如果不考虑业务上的细节,这话儿看似浮夸,却又十分真实。         本文手把手教大家如何用t-io快速做一个网页版聊天室----可能这不仅仅只是个小demo,它更多的可作为项目的一个脚手架,读者可以以此为基础,完成一个真实的网页聊天室,甚至扩展成一个在线客服。         本文有部分代码并非必须,譬如数据监控相关的listener,但是在大型的项目中,对这些监控数据的处理却是一个必须,所以本文从实用角度出发,加了不少非必须的代码,望读者朋友喜欢并从中获益。         废话少说,撸起袖子就是干。

    1、从pom.xml开始,引入tio-websocket-server

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>tio-websocket-showcase</artifactId> <name>${project.artifactId}</name> <parent> <groupId>org.t-io</groupId> <artifactId>tio-parent</artifactId> <version>2.3.0.v20180506-RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.t-io</groupId> <artifactId>tio-websocket-server</artifactId> </dependency> <!-- slf4j-logback绑定 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-access</artifactId> </dependency> <!-- redirect apache commons logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> </dependency> <!-- redirect jdk util logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>jul-to-slf4j</artifactId> </dependency> <!-- redirect log4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <scope>test</scope> </dependency> </dependencies> </project>

    2、实现org.tio.websocket.server.handler.IWsMsgHandler

        注释都在代码中,各位慢读 package org.tio.showcase.websocket.server; import java.util.Objects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tio.core.Aio; import org.tio.core.ChannelContext; import org.tio.http.common.HttpRequest; import org.tio.http.common.HttpResponse; import org.tio.websocket.common.WsRequest; import org.tio.websocket.common.WsResponse; import org.tio.websocket.common.WsSessionContext; import org.tio.websocket.server.handler.IWsMsgHandler; /** * @author tanyaowu * 2017年6月28日 下午5:32:38 */ public class ShowcaseWsMsgHandler implements IWsMsgHandler { private static Logger log = LoggerFactory.getLogger(ShowcaseWsMsgHandler.class); public static ShowcaseWsMsgHandler me = new ShowcaseWsMsgHandler(); private ShowcaseWsMsgHandler() { } /** * 握手时走这个方法,业务可以在这里获取cookie,request参数等 */ @Override public HttpResponse handshake(HttpRequest request, HttpResponse httpResponse, ChannelContext channelContext) throws Exception { String clientip = request.getClientIp(); log.info("收到来自{}的ws握手包\r\n{}", clientip, request.toString()); return httpResponse; } /** * 字节消息(binaryType = arraybuffer)过来后会走这个方法 */ @Override public Object onBytes(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception { return null; } /** * 当客户端发close flag时,会走这个方法 */ @Override public Object onClose(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception { Aio.remove(channelContext, "receive close flag"); return null; } /* * 字符消息(binaryType = blob)过来后会走这个方法 */ @Override public Object onText(WsRequest wsRequest, String text, ChannelContext channelContext) throws Exception { WsSessionContext wsSessionContext = (WsSessionContext) channelContext.getAttribute(); HttpRequest httpRequest = wsSessionContext.getHandshakeRequestPacket();//获取websocket握手包 if (log.isDebugEnabled()) { log.debug("握手包:{}", httpRequest); } log.info("收到ws消息:{}", text); if (Objects.equals("心跳内容", text)) { return null; } String msg = channelContext.getClientNode().toString() + " 说:" + text; //用tio-websocket,服务器发送到客户端的Packet都是WsResponse WsResponse wsResponse = WsResponse.fromText(msg, ShowcaseServerConfig.CHARSET); //群发 Aio.sendToGroup(channelContext.getGroupContext(), Const.GROUP_ID, wsResponse); //返回值是要发送给客户端的内容,一般都是返回null return null; } }

    3、实现org.tio.websocket.server.WsServerAioListener

        注释都在代码中,各位慢读 /** * */ package org.tio.showcase.websocket.server; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tio.core.Aio; import org.tio.core.ChannelContext; import org.tio.core.intf.Packet; import org.tio.websocket.server.WsServerAioListener; /** * @author tanyaowu * 用户根据情况来完成该类的实现 */ public class ShowcaseServerAioListener extends WsServerAioListener { private static Logger log = LoggerFactory.getLogger(ShowcaseServerAioListener.class); public static final ShowcaseServerAioListener me = new ShowcaseServerAioListener(); private ShowcaseServerAioListener() { } @Override public void onAfterConnected(ChannelContext channelContext, boolean isConnected, boolean isReconnect) throws Exception { super.onAfterConnected(channelContext, isConnected, isReconnect); if (log.isInfoEnabled()) { log.info("onAfterConnected\r\n{}", channelContext); } //绑定到群组,后面会有群发 Aio.bindGroup(channelContext, Const.GROUP_ID); } @Override public void onAfterSent(ChannelContext channelContext, Packet packet, boolean isSentSuccess) throws Exception { super.onAfterSent(channelContext, packet, isSentSuccess); if (log.isInfoEnabled()) { log.info("onAfterSent\r\n{}\r\n{}", packet.logstr(), channelContext); } } @Override public void onBeforeClose(ChannelContext channelContext, Throwable throwable, String remark, boolean isRemove) throws Exception { super.onBeforeClose(channelContext, throwable, remark, isRemove); if (log.isInfoEnabled()) { log.info("onBeforeClose\r\n{}", channelContext); } } @Override public void onAfterDecoded(ChannelContext channelContext, Packet packet, int packetSize) throws Exception { super.onAfterDecoded(channelContext, packet, packetSize); if (log.isInfoEnabled()) { log.info("onAfterDecoded\r\n{}\r\n{}", packet.logstr(), channelContext); } } @Override public void onAfterReceivedBytes(ChannelContext channelContext, int receivedBytes) throws Exception { super.onAfterReceivedBytes(channelContext, receivedBytes); if (log.isInfoEnabled()) { log.info("onAfterReceivedBytes\r\n{}", channelContext); } } @Override public void onAfterHandled(ChannelContext channelContext, Packet packet, long cost) throws Exception { super.onAfterHandled(channelContext, packet, cost); if (log.isInfoEnabled()) { log.info("onAfterHandled\r\n{}\r\n{}", packet.logstr(), channelContext); } } }

    4、实现org.tio.core.stat.IpStatListener(非必须)

        注释都在代码中,各位慢读 /** * */ package org.tio.showcase.websocket.server; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tio.core.ChannelContext; import org.tio.core.GroupContext; import org.tio.core.intf.Packet; import org.tio.core.stat.IpStat; import org.tio.core.stat.IpStatListener; import org.tio.utils.json.Json; /** * * @author tanyaowu * */ public class ShowcaseIpStatListener implements IpStatListener { private static Logger log = LoggerFactory.getLogger(ShowcaseIpStatListener.class); public static final ShowcaseIpStatListener me = new ShowcaseIpStatListener(); /** * */ private ShowcaseIpStatListener() { } @Override public void onExpired(GroupContext groupContext, IpStat ipStat) { //在这里把统计数据入库中或日志 if (log.isInfoEnabled()) { log.info("可以把统计数据入库\r\n{}", Json.toFormatedJson(ipStat)); } } @Override public void onAfterConnected(ChannelContext channelContext, boolean isConnected, boolean isReconnect, IpStat ipStat) throws Exception { if (log.isInfoEnabled()) { log.info("onAfterConnected\r\n{}", Json.toFormatedJson(ipStat)); } } @Override public void onDecodeError(ChannelContext channelContext, IpStat ipStat) { if (log.isInfoEnabled()) { log.info("onDecodeError\r\n{}", Json.toFormatedJson(ipStat)); } } @Override public void onAfterSent(ChannelContext channelContext, Packet packet, boolean isSentSuccess, IpStat ipStat) throws Exception { if (log.isInfoEnabled()) { log.info("onAfterSent\r\n{}\r\n{}", packet.logstr(), Json.toFormatedJson(ipStat)); } } @Override public void onAfterDecoded(ChannelContext channelContext, Packet packet, int packetSize, IpStat ipStat) throws Exception { if (log.isInfoEnabled()) { log.info("onAfterDecoded\r\n{}\r\n{}", packet.logstr(), Json.toFormatedJson(ipStat)); } } @Override public void onAfterReceivedBytes(ChannelContext channelContext, int receivedBytes, IpStat ipStat) throws Exception { if (log.isInfoEnabled()) { log.info("onAfterReceivedBytes\r\n{}", Json.toFormatedJson(ipStat)); } } @Override public void onAfterHandled(ChannelContext channelContext, Packet packet, IpStat ipStat, long cost) throws Exception { if (log.isInfoEnabled()) { log.info("onAfterHandled\r\n{}\r\n{}", packet.logstr(), Json.toFormatedJson(ipStat)); } } }

    5、一些配置项

        注释都在代码中,各位慢读 /** * */ package org.tio.showcase.websocket.server; import org.tio.utils.time.Time; /** * @author tanyaowu * */ public abstract class ShowcaseServerConfig { /** * 协议名字(可以随便取,主要用于开发人员辨识) */ public static final String PROTOCOL_NAME = "showcase"; public static final String CHARSET = "utf-8"; /** * 监听的ip */ public static final String SERVER_IP = null;//null表示监听所有,并不指定ip /** * 监听端口 */ public static final int SERVER_PORT = 9326; /** * 心跳超时时间,单位:毫秒 */ public static final int HEARTBEAT_TIMEOUT = 1000 * 60; /** * ip数据监控统计,时间段 * @author tanyaowu * */ public static interface IpStatDuration { public static final Long DURATION_1 = Time.MINUTE_1 * 5; public static final Long[] IPSTAT_DURATIONS = new Long[] { DURATION_1 }; } }

    6、一些常量

        注释都在代码中,各位慢读 /** * */ package org.tio.showcase.websocket.server; /** * @author tanyaowu * */ public class Const { /** * 用于群聊的group id */ public static final String GROUP_ID = "showcase-websocket"; }

    7、一个启动类

        注释都在代码中,各位慢读 package org.tio.showcase.websocket.server; import java.io.IOException; import org.tio.server.ServerGroupContext; import org.tio.websocket.server.WsServerStarter; /** * @author tanyaowu * 2017年6月28日 下午5:34:04 */ public class ShowcaseWebsocketStarter { private WsServerStarter wsServerStarter; private ServerGroupContext serverGroupContext; /** * * @author tanyaowu */ public ShowcaseWebsocketStarter(int port, ShowcaseWsMsgHandler wsMsgHandler) throws IOException { wsServerStarter = new WsServerStarter(port, wsMsgHandler); serverGroupContext = wsServerStarter.getServerGroupContext(); serverGroupContext.setName(ShowcaseServerConfig.PROTOCOL_NAME); serverGroupContext.setServerAioListener(ShowcaseServerAioListener.me); //设置ip统计时间段 serverGroupContext.ipStats.addDurations(ShowcaseServerConfig.IpStatDuration.IPSTAT_DURATIONS); //设置ip监控 serverGroupContext.setIpStatListener(ShowcaseIpStatListener.me); //设置心跳超时时间 serverGroupContext.setHeartbeatTimeout(ShowcaseServerConfig.HEARTBEAT_TIMEOUT); } /** * @param args * @author tanyaowu * @throws IOException */ public static void start() throws IOException { ShowcaseWebsocketStarter appStarter = new ShowcaseWebsocketStarter(ShowcaseServerConfig.SERVER_PORT, ShowcaseWsMsgHandler.me); appStarter.wsServerStarter.start(); } /** * @return the serverGroupContext */ public ServerGroupContext getServerGroupContext() { return serverGroupContext; } public WsServerStarter getWsServerStarter() { return wsServerStarter; } public static void main(String[] args) throws IOException { start(); } }

    8、运行步骤7中的启动类

        会看到下面启动成功的日志 2018-05-03 17:12:47,004 WARN org.tio.server.AioServer[109]: showcase started, listen on 0.0.0.0:9326

    9、写一个js client

        为了简化js端websocket的开发,本人写了一个简单的小js,它的名字叫tiows.js,它处理了重连、发心跳等很多开发人员不愿意去干的活。它的源代码在:https://gitee.com/tywo45/tio-websocket-showcase,把源代码下载下来后,在page/tio/目录中就能看到tiows.js。         然后再打开page/index.html,就能看到下面这个界面了(前提是前面8步要完成),如果你不想完成前面8步,你同样可以在https://gitee.com/tywo45/tio-websocket-showcase中找到前8步所需要的java代码。

    10、演示地址

        tio-websocket-showcase

    11、结束语

        到目前为止(2018-05-03),本教程是学习tio-websocket-server的最佳教程,因为本人只出过这么一个教程^_^。         最后别忘了关注:让天下没有难开发的网络通信

 

  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
talent-tan
粉丝 702
博文 21
码字总数 10241
作品 3
评论 (41)
年轻人w
网络开发界的福音,给作者点赞:sunglasses:
PublicCMS
html5游戏越来越多了 websocket用途越来越广
talent-tan

引用来自“kerneler”的评论

html5游戏越来越多了 websocket用途越来越广
贝密游戏用的就是tio哦,还有好几个游戏案例
路小磊
顶起
不愿透露姓名的Mr成
和netty-socket.io比哪个好
IceRainYWC
谭总最后两句亮了��
talent-tan

引用来自“不愿透露姓名的Mr成”的评论

和netty-socket.io比哪个好
没用过netty-socket.io,贝密游戏原来是用的netty-socket.io,后来改用t-io了
https://gitee.com/beimigame/beimi
beastxiao
ws://还没问题,换成wss://后 HttpRequestDecoder类parseRequestLine方法那里line参数是编码后的,并没有解码,是最新版的还支持还是需要配置,微信小程序只支持wss,希望谭总考虑下这个问题
talent-tan

引用来自“beastxiao”的评论

ws://还没问题,换成wss://后 HttpRequestDecoder类parseRequestLine方法那里line参数是编码后的,并没有解码,是最新版的还支持还是需要配置,微信小程序只支持wss,希望谭总考虑下这个问题
早就支持wss了,你要配置一下
https://my.oschina.net/talenttan/blog/1587197
beastxiao

引用来自“beastxiao”的评论

ws://还没问题,换成wss://后 HttpRequestDecoder类parseRequestLine方法那里line参数是编码后的,并没有解码,是最新版的还支持还是需要配置,微信小程序只支持wss,希望谭总考虑下这个问题

引用来自“talent-tan”的评论

早就支持wss了,你要配置一下
https://my.oschina.net/talenttan/blog/1587197
谢谢,在请教个问题,我想在原有web系统(基于spring)里面用websocket做个聊天模块,我把启动类新增@Component注解让spring管理,用@PostConstruct注解的方法来启动,此时能正常运行,但是我想操作数据库,却注入不了service实例,不知道类似需求你是怎么处理的?
talent-tan

引用来自“beastxiao”的评论

ws://还没问题,换成wss://后 HttpRequestDecoder类parseRequestLine方法那里line参数是编码后的,并没有解码,是最新版的还支持还是需要配置,微信小程序只支持wss,希望谭总考虑下这个问题

引用来自“talent-tan”的评论

早就支持wss了,你要配置一下
https://my.oschina.net/talenttan/blog/1587197

引用来自“beastxiao”的评论

谢谢,在请教个问题,我想在原有web系统(基于spring)里面用websocket做个聊天模块,我把启动类新增@Component注解让spring管理,用@PostConstruct注解的方法来启动,此时能正常运行,但是我想操作数据库,却注入不了service实例,不知道类似需求你是怎么处理的?
我早就抛弃spring了,也抛弃servlet了
beastxiao

引用来自“beastxiao”的评论

ws://还没问题,换成wss://后 HttpRequestDecoder类parseRequestLine方法那里line参数是编码后的,并没有解码,是最新版的还支持还是需要配置,微信小程序只支持wss,希望谭总考虑下这个问题

引用来自“talent-tan”的评论

早就支持wss了,你要配置一下
https://my.oschina.net/talenttan/blog/1587197

引用来自“beastxiao”的评论

谢谢,在请教个问题,我想在原有web系统(基于spring)里面用websocket做个聊天模块,我把启动类新增@Component注解让spring管理,用@PostConstruct注解的方法来启动,此时能正常运行,但是我想操作数据库,却注入不了service实例,不知道类似需求你是怎么处理的?

引用来自“talent-tan”的评论

我早就抛弃spring了,也抛弃servlet了
没办法,大部分公司都用spring,要是我用jfinal哪会有这种问题:smile: ,言归正传,有解决方案吗?
loyal

引用来自“beastxiao”的评论

ws://还没问题,换成wss://后 HttpRequestDecoder类parseRequestLine方法那里line参数是编码后的,并没有解码,是最新版的还支持还是需要配置,微信小程序只支持wss,希望谭总考虑下这个问题

引用来自“talent-tan”的评论

早就支持wss了,你要配置一下
https://my.oschina.net/talenttan/blog/1587197

引用来自“beastxiao”的评论

谢谢,在请教个问题,我想在原有web系统(基于spring)里面用websocket做个聊天模块,我把启动类新增@Component注解让spring管理,用@PostConstruct注解的方法来启动,此时能正常运行,但是我想操作数据库,却注入不了service实例,不知道类似需求你是怎么处理的?

引用来自“talent-tan”的评论

我早就抛弃spring了,也抛弃servlet了

引用来自“beastxiao”的评论

没办法,大部分公司都用spring,要是我用jfinal哪会有这种问题:smile: ,言归正传,有解决方案吗?
只能说明你还没搞明白什么是spring....
beastxiao

引用来自“beastxiao”的评论

ws://还没问题,换成wss://后 HttpRequestDecoder类parseRequestLine方法那里line参数是编码后的,并没有解码,是最新版的还支持还是需要配置,微信小程序只支持wss,希望谭总考虑下这个问题

引用来自“talent-tan”的评论

早就支持wss了,你要配置一下
https://my.oschina.net/talenttan/blog/1587197

引用来自“beastxiao”的评论

谢谢,在请教个问题,我想在原有web系统(基于spring)里面用websocket做个聊天模块,我把启动类新增@Component注解让spring管理,用@PostConstruct注解的方法来启动,此时能正常运行,但是我想操作数据库,却注入不了service实例,不知道类似需求你是怎么处理的?

引用来自“talent-tan”的评论

我早就抛弃spring了,也抛弃servlet了

引用来自“beastxiao”的评论

没办法,大部分公司都用spring,要是我用jfinal哪会有这种问题:smile: ,言归正传,有解决方案吗?

引用来自“loyal”的评论

只能说明你还没搞明白什么是spring....
:sweat_smile: 止于表面吧,会点基本的而已,对我来说搞懂spring的路还长,庆幸的是还没停下脚本
亚虎娱乐官方app中国首席睡觉专家
可以做服务器吗?
talent-tan

引用来自“亚虎娱乐官方app中国首席效率专家”的评论

可以做服务器吗?
贝密游戏、j-im、牛吧云播都是用tio-websocket-server作为websocket服务器的。最近tio也提供了tiows.js,用于websocket客户端
小99

引用来自“亚虎娱乐官方app中国首席效率专家”的评论

可以做服务器吗?
可以做web server
liuleidefeng

引用来自“beastxiao”的评论

ws://还没问题,换成wss://后 HttpRequestDecoder类parseRequestLine方法那里line参数是编码后的,并没有解码,是最新版的还支持还是需要配置,微信小程序只支持wss,希望谭总考虑下这个问题

引用来自“talent-tan”的评论

早就支持wss了,你要配置一下
https://my.oschina.net/talenttan/blog/1587197

引用来自“beastxiao”的评论

谢谢,在请教个问题,我想在原有web系统(基于spring)里面用websocket做个聊天模块,我把启动类新增@Component注解让spring管理,用@PostConstruct注解的方法来启动,此时能正常运行,但是我想操作数据库,却注入不了service实例,不知道类似需求你是怎么处理的?
之所以不能注入service,很可能是因为你的@PostConstruct里面的代码初始化顺序的问题。@PostConstruct里面代码初始化的时候,很可能spring管理的其他组件并未完全初始化,比如数据库连接池什么的。我的解决方案是,不用@PostConstruct,而使用spring的容器监听器ApplicationListener,把websocket的初始化代码调用写在这个监听器内,当spring容器完全初始化过后,再用spring的上下文获取到你的websocket那个类的bean,然后用bean.初始化方法()来启动websocket服务,这样所有的东西都交给spring去管理就可以了。
吃饼青年

引用来自“liuleidefeng”的评论

引用来自“beastxiao”的评论

ws://还没问题,换成wss://后 HttpRequestDecoder类parseRequestLine方法那里line参数是编码后的,并没有解码,是最新版的还支持还是需要配置,微信小程序只支持wss,希望谭总考虑下这个问题

引用来自“talent-tan”的评论

早就支持wss了,你要配置一下
https://my.oschina.net/talenttan/blog/1587197

引用来自“beastxiao”的评论

谢谢,在请教个问题,我想在原有web系统(基于spring)里面用websocket做个聊天模块,我把启动类新增@Component注解让spring管理,用@PostConstruct注解的方法来启动,此时能正常运行,但是我想操作数据库,却注入不了service实例,不知道类似需求你是怎么处理的?
之所以不能注入service,很可能是因为你的@PostConstruct里面的代码初始化顺序的问题。@PostConstruct里面代码初始化的时候,很可能spring管理的其他组件并未完全初始化,比如数据库连接池什么的。我的解决方案是,不用@PostConstruct,而使用spring的容器监听器ApplicationListener,把websocket的初始化代码调用写在这个监听器内,当spring容器完全初始化过后,再用spring的上下文获取到你的websocket那个类的bean,然后用bean.初始化方法()来启动websocket服务,这样所有的东西都交给spring去管理就可以了。
说的有道理
傲娇字符

引用来自“beastxiao”的评论

ws://还没问题,换成wss://后 HttpRequestDecoder类parseRequestLine方法那里line参数是编码后的,并没有解码,是最新版的还支持还是需要配置,微信小程序只支持wss,希望谭总考虑下这个问题

引用来自“talent-tan”的评论

早就支持wss了,你要配置一下
https://my.oschina.net/talenttan/blog/1587197

引用来自“beastxiao”的评论

谢谢,在请教个问题,我想在原有web系统(基于spring)里面用websocket做个聊天模块,我把启动类新增@Component注解让spring管理,用@PostConstruct注解的方法来启动,此时能正常运行,但是我想操作数据库,却注入不了service实例,不知道类似需求你是怎么处理的?
给你一个建议,创建一个Component类,然后在ApplicationContext.xml中最后加上初始化bean的配置,这样就可以确保数据库操作相关的实例已经创建了;

编写一个启动接口,所有实现接口的bean,都会调用start方法:

package com.chz.apps.common.component;

/**
* 自启动接口类,容器启动完成后,自动调用启动方法
*/
public interface IAutoStartPlugin {

/**
* 启动
*/
void start();

}

编写启动类:

package com.chz.component.basic.listener;

import com.chz.apps.common.component.IAutoStartPlugin;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Service;

@Component
public class StartupListener implements ApplicationListener {

private static Boolean loaded = false;//启动标记,确保只启动过一次

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if(event.getApplicationContext().getParent() != null && !loaded){

Map allPlugin = SpringContextHolder.getApplicationContext().getBeansOfType(IAutoStartPlugin.class);
if(allPlugin != null || allPlugin.size() > 0){
for (Map.Entry row : allPlugin.entrySet()) {
row.getValue().start();//调用启动类
}
}
loaded = true;
}
}

}



applicationContext.xml




另外
×
talent-tan
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: