上節實現了netty的基本鏈接,這節加入spring來管理netty,由spring來開啓netty服務。java
在netty服務器中,咱們創建了三個類:HelloServer(程序主入口) , HelloServerInitializer(傳輸通道初始化),HelloServerHandler(業務控制器)spring
這三個類中HelloServer中new了一個HelloServerInitializer,在HelloServerInitializer最後又new了一個HelloServerHandler。其中須要new的地方,就是spring要管理的地方。apache
導入須要的JAR包和相關依賴,主要是spring,log4j,netty等,後面的項目源碼中包含了全部jar包;bootstrap
另外你能夠使用maven來構建項目,添加各類jar包。服務器
主程序很簡單,就是加載spring配置文件就能夠了。app
package com.wayhb; import org.apache.log4j.Logger; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestSpring { private static Logger log = Logger.getLogger(TestSpring.class); public static void main(String[] args) { // TODO Auto-generated method stub //加載spirng配置文件 ApplicationContext context= new ClassPathXmlApplicationContext("server.xml"); } }
server.xmlsocket
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:lang="http://www.springframework.org/schema/lang" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd" default-lazy-init="false" default-autowire="byName"> <!-- <bean id="user" class="com.wayhb.User"> <property name="userid" value="20"></property> <property name="username" value="wayhb"></property> </bean>--> <!--開啓註解方式,掃描com.wayhb,com.netty兩個包路徑--> <context:annotation-config/> <context:component-scan base-package="com.wayhb,com.netty" /> <!-- 傳統方法配置BEAN <bean id="helloServer" class="com.netty.HelloServer" init-method="serverStart"> <property name="helloServerInitializer" ref="helloServerInitializer"></property> </bean> <bean id="helloServerInitializer" class="com.netty.HelloServerInitializer"> <property name="helloServerHandler" ref="helloServerHandler"></property> </bean> <bean id="helloServerHandler" class="com.netty.HelloServerHandler" scope="prototype"></bean> --> </beans>
log4j.propertiesmaven
### 設置### #log4j.rootLogger = debug,stdout,D,E log4j.rootLogger = debug,stdout ### 輸出信息到控制擡 ### log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n ### 輸出DEBUG 級別以上的日誌到=E://logs/error.log ### #log4j.appender.D = org.apache.log4j.DailyRollingFileAppender #log4j.appender.D.File = C://logs/log.log #log4j.appender.D.Append = true #log4j.appender.D.Threshold = DEBUG #log4j.appender.D.layout = org.apache.log4j.PatternLayout #log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n ### 輸出ERROR 級別以上的日誌到=E://logs/error.log ### #log4j.appender.E = org.apache.log4j.DailyRollingFileAppender #log4j.appender.E.File =C://logs/error.log #log4j.appender.E.Append = true #log4j.appender.E.Threshold = ERROR #log4j.appender.E.layout = org.apache.log4j.PatternLayout #log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
其中輸出功能是關閉的,須要的話去掉#就能夠了ide
package com.netty; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import javax.annotation.PostConstruct; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; //註解方式注入bean,名字是helloServer @Service("helloServer") public class HelloServer { private static Logger log = Logger.getLogger(HelloServer.class); /** * 服務端監聽的端口地址 */ private static final int portNumber = 7878; //自動裝備變量,spring會根據名字或者類型來裝備這個變量,註解方式不須要set get方法了 @Autowired private HelloServerInitializer helloServerInitializer; //程序初始方法入口註解,提示spring這個程序先執行這裏 @PostConstruct public void serverStart() throws InterruptedException{ EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup); b.channel(NioServerSocketChannel.class); b.childHandler(helloServerInitializer); // 服務器綁定端口監聽 ChannelFuture f = b.bind(portNumber).sync(); // 監聽服務器關閉監聽 f.channel().closeFuture().sync(); log.info("###########################################"); // 能夠簡寫爲 /* b.bind(portNumber).sync().channel().closeFuture().sync(); */ } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
註解方式更加簡便,配置內容少了不少oop
package com.netty; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.Delimiters; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; @Service("helloServerInitializer") public class HelloServerInitializer extends ChannelInitializer<SocketChannel> { @Autowired private HelloServerHandler helloServerHandler; @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // 以("\n")爲結尾分割的 解碼器 pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); // 字符串解碼 和 編碼 pipeline.addLast("decoder", new StringDecoder()); pipeline.addLast("encoder", new StringEncoder()); // 本身的邏輯Handler pipeline.addLast("handler", helloServerHandler); } }
package com.netty; import java.net.InetAddress; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.DefaultChannelGroup; import io.netty.util.concurrent.GlobalEventExecutor; import io.netty.channel.ChannelHandler.Sharable; @Service("helloServerHandler") @Scope("prototype") //特別注意這個註解@Sharable,默認的4版本不能自動導入匹配的包,須要手動加入 //地址是import io.netty.channel.ChannelHandler.Sharable; @Sharable public class HelloServerHandler extends SimpleChannelInboundHandler<String> { public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // (2) Channel incoming = ctx.channel(); for (Channel channel : channels) { channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入\n"); } channels.add(ctx.channel()); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // (3) Channel incoming = ctx.channel(); for (Channel channel : channels) { channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 離開\n"); } channels.remove(ctx.channel()); } @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { // 收到消息直接打印輸出 System.out.println(ctx.channel().remoteAddress() + " Say : " + msg); // 返回客戶端消息 - 我已經接收到了你的消息 ctx.writeAndFlush("Received your message !\n"); } /* * * 覆蓋 channelActive 方法 在channel被啓用的時候觸發 (在創建鏈接的時候) * * channelActive 和 channelInActive 在後面的內容中講述,這裏先不作詳細的描述 * */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("RamoteAddress : " + ctx.channel().remoteAddress() + " active !"); ctx.writeAndFlush( "Welcome to " + InetAddress.getLocalHost().getHostName() + " service!\n"); super.channelActive(ctx); } }
特別注意這個註解@Sharable,默認的4版本不能自動導入匹配的包,須要手動加入
地址是import io.netty.channel.ChannelHandler.Sharable;
1,打開TestSpring.java,右鍵運行,正常狀況下服務器會開啓;
2,打開上節寫的項目,打開HelloClient.java,右鍵運行;
3,HelloClient.java上再運行一次,出現兩個客戶端,若是都鏈接成功,說明項目沒有問題。
注意事項:netty4須要@Sharable才能夠開啓多個handler,因此須要多個鏈接的handler類上要加入註解@Sharable,該註解自動導入有問題,手動加入包import io.netty.channel.ChannelHandler.Sharable;