Mina入门教程(二)----Spring4 集成Mina
在spring和mina集成的时候,要十分注意一个问题:版本。
这是一个非常严重的问题,mina官网的demo没错,网上很多网友总结的代码也是对的,但是很多人将mina集成到spring中的时候,总是会发现有个问题:
java.lang.IllegalArgumentException: Cannot convert value of type [org.apache.mina.integration.beans.InetSocketAddressEditor] to required type [java.lang.Class] for property 'customEditors[java.net.SocketAddress]': PropertyEditor .....
类型不匹配。估计很多人按照网上的demo来做的话,都会遇到这个错误(除非spring是用的2.5或者之前的版本)。这是因为新版的spring有改动:
将一个PropertyEditor 实例传入CustomEditorConfigurer 已经被废除了
所以很多人会遇到这个错误。笔者也是查了N久资料,发现某位学php的牛人解决了该问题。是对spring的配置文件做一点修改:
将原来的:
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map> <entry key="java.net.SocketAddress"> <bean class="org.apache.mina.integration.beans.InetSocketAddressEditor" /> </entry> </map> </property> </bean>
改成:
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map>
<!-- 修改这里 --> <entry key="java.net.SocketAddress" value="org.apache.mina.integration.beans.InetSocketAddressEditor" > </entry> </map> </property> </bean>
这样,这个问题就解决了。想想原本的项目中,用的spring+mina没出现问题,真是个侥幸。。。
好了,下面把整合的写一下。
首先是用到的包,这里使用maven,pom.xml:
<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> <groupId>mymina</groupId> <artifactId>MyMina</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>MyMina</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <build> <plugins> <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <extensions>true</extensions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jdk14</artifactId> <version>1.7.7</version> </dependency> <dependency> <groupId>org.apache.mina</groupId> <artifactId>mina-integration-beans</artifactId> <version>2.0.7</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.0.6.RELEASE</version> </dependency> <dependency> <groupId>org.apache.mina</groupId> <artifactId>mina-core</artifactId> <version>2.0.4</version> <type>bundle</type> <scope>compile</scope> </dependency> <dependency> <groupId>org.apache.mina</groupId> <artifactId>mina-integration-spring</artifactId> <version>1.1.7</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.0.6.RELEASE</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.3.0</version> </dependency> </dependencies> </project>
这里面有几个配置是比较讲究的,包括那个plugin和mina-core中的配置,配置有点差别,会报一个bundle的错误。
因为用maven,会方便很多,找包的时候,知道依赖就可以了。这里有个网站,可以查找自己需要的依赖包:http://mvnrepository.com/ ,这个网站很不错,类似以前的findjar.com。
然后是spring整合的配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map> <entry key="java.net.SocketAddress" value="org.apache.mina.integration.beans.InetSocketAddressEditor" > </entry> </map> </property> </bean> <bean id="ioAcceptor" class="org.apache.mina.transport.socket.nio.NioSocketAcceptor" init-method="bind" destroy-method="unbind"> <property name="defaultLocalAddress" value=":8888" /> <property name="handler" ref="SampleHandler" /> <property name="filterChainBuilder" ref="filterChainBuilder" /> <property name="reuseAddress" value="true" /> </bean> <bean id="executorFilter" class="org.apache.mina.filter.executor.ExecutorFilter" /> <bean id="mdcInjectionFilter" class="org.apache.mina.filter.logging.MdcInjectionFilter"> <constructor-arg value="remoteAddress" /> </bean> <bean id="codecFilter" class="org.apache.mina.filter.codec.ProtocolCodecFilter"> <constructor-arg> <!-- <bean class="org.apache.mina.filter.codec.textline.TextLineCodecFactory" /> --> <bean class="cn.org.handler.MyCodeFactory"></bean> </constructor-arg> </bean> <bean id="loggingFilter" class="org.apache.mina.filter.logging.LoggingFilter" /> <bean id="SampleHandler" class="cn.org.handler.HandlerTwo" /> <!--boss server --> <bean id="bossSampleHandler" class="cn.org.handler.HandlerOne" /> <bean id="filterChainBuilder" class="org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder"> <property name="filters"> <map> <entry key="executor" value-ref="executorFilter" /> <entry key="mdcInjectionFilter" value-ref="mdcInjectionFilter" /> <entry key="codecFilter" value-ref="codecFilter" /> <entry key="loggingFilter" value-ref="loggingFilter" /> </map> </property> </bean> </beans>
这里需要注意的问题,前面已经有写。
再者就是服务端和客户端的代码编写,服务端已经和spring整合了,里面配置了服务端的handler,所以这里主要是服务端的handler:
package cn.org.handler; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; public class HandlerTwo extends IoHandlerAdapter { @Override public void messageReceived(IoSession session, Object message) throws Exception { // TODO Auto-generated method stub // super.messageReceived(session, message); System.out.println("received message :"+message); } @Override public void sessionClosed(IoSession session) throws Exception { // TODO Auto-generated method stub super.sessionClosed(session); } @Override public void sessionIdle(IoSession session, IdleStatus status) throws Exception { // TODO Auto-generated method stub super.sessionIdle(session, status); } }
写一个启动入口:
package cn.org.test; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { /** * @param args */ public static void main(String[] args) { ClassPathXmlApplicationContext ct = new ClassPathXmlApplicationContext("applicationContext-mina.xml"); } }
还缺少一个服务端的编码过滤器:
package cn.org.handler; import java.nio.charset.Charset; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolCodecFactory; import org.apache.mina.filter.codec.ProtocolDecoder; import org.apache.mina.filter.codec.ProtocolEncoder; import org.apache.mina.filter.codec.textline.LineDelimiter; import org.apache.mina.filter.codec.textline.TextLineDecoder; import org.apache.mina.filter.codec.textline.TextLineEncoder; public class MyCodeFactory implements ProtocolCodecFactory { private final TextLineEncoder encoder; private final TextLineDecoder decoder; /*final static char endchar = 0x1a;*/ final static char endchar = 0x0d; public MyCodeFactory() { this(Charset.forName("gb2312")); } public MyCodeFactory(Charset charset) { encoder = new TextLineEncoder(charset, LineDelimiter.UNIX); decoder = new TextLineDecoder(charset, LineDelimiter.AUTO); } public ProtocolDecoder getDecoder(IoSession session) throws Exception { // TODO Auto-generated method stub return decoder; } public ProtocolEncoder getEncoder(IoSession session) throws Exception { // TODO Auto-generated method stub return encoder; } public int getEncoderMaxLineLength() { return encoder.getMaxLineLength(); } public void setEncoderMaxLineLength(int maxLineLength) { encoder.setMaxLineLength(maxLineLength); } public int getDecoderMaxLineLength() { return decoder.getMaxLineLength(); } public void setDecoderMaxLineLength(int maxLineLength) { decoder.setMaxLineLength(maxLineLength); } }
这样,运行入口程序,服务端就启动了。除了入口,在spring中都有配置,包括端口这些。
然后写一个客户端:
客户端的handler:
package cn.org.handler; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; public class HandlerOne extends IoHandlerAdapter { @Override public void messageReceived(IoSession session, Object message) throws Exception { // TODO Auto-generated method stub //super.messageReceived(session, message); System.out.println("message :"+message); } @Override public void sessionClosed(IoSession session) throws Exception { // TODO Auto-generated method stub super.sessionClosed(session); } @Override public void sessionIdle(IoSession session, IdleStatus status) throws Exception { // TODO Auto-generated method stub super.sessionIdle(session, status); } @Override public void messageSent(IoSession session, Object message) throws Exception { System.out.println("发送的消息是:"+message.toString()); //super.messageSent(session, message); } @Override public void sessionCreated(IoSession session) throws Exception { super.sessionCreated(session); } @Override public void sessionOpened(IoSession session) throws Exception { super.sessionOpened(session); } }
客户端的入口:
package cn.org.test; import java.net.InetSocketAddress; import java.nio.charset.Charset; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.filter.logging.LoggingFilter; import org.apache.mina.transport.socket.nio.NioSocketConnector; import cn.org.handler.HandlerOne; public class ClintTest { /** * @param args */ public static void main(String[] args) { // 创建客户端连接器. NioSocketConnector connector = new NioSocketConnector(); connector.getFilterChain().addLast( "logger", new LoggingFilter() ); connector.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "GBK" )))); //设置编码过滤器 connector.setHandler(new HandlerOne());//设置事件处理器 ConnectFuture cf = connector.connect( new InetSocketAddress("127.0.0.1", 8888));//建立连接 cf.awaitUninterruptibly();//等待连接创建完成 cf.getSession().write("知识");//发送消息 cf.getSession().close(true); cf.getSession().getCloseFuture().awaitUninterruptibly();//等待连接断开 connector.dispose(); } }
这样,当服务端启动的时候,客户端发送一个信息,服务端就会收到,并作相应的处理。
运行结果
客户端控制台输出:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/Users/wanglei/.m2/repository/org/slf4j/slf4j-jdk14/1.7.7/slf4j-jdk14-1.7.7.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/C:/Users/wanglei/.m2/repository/org/slf4j/slf4j-log4j12/1.3.0/slf4j-log4j12-1.3.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.JDK14LoggerFactory]
八月 27, 2014 8:50:10 下午 org.apache.mina.filter.logging.LoggingFilter log
信息: CREATED
八月 27, 2014 8:50:10 下午 org.apache.mina.filter.logging.LoggingFilter log
信息: OPENED
八月 27, 2014 8:50:10 下午 org.apache.mina.filter.logging.LoggingFilter log
信息: SENT: HeapBuffer[pos=0 lim=0 cap=0: empty]
发送的消息是:知识
八月 27, 2014 8:50:10 下午 org.apache.mina.filter.logging.LoggingFilter log
信息: CLOSED
服务端输出:
SLF4J: Found binding in [jar:file:/C:/Users/wanglei/.m2/repository/org/slf4j/slf4j-log4j12/1.3.0/slf4j-log4j12-1.3.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.JDK14LoggerFactory]
八月 27, 2014 8:50:10 下午 org.apache.mina.filter.logging.LoggingFilter log
信息: CREATED
八月 27, 2014 8:50:10 下午 org.apache.mina.filter.logging.LoggingFilter log
信息: OPENED
八月 27, 2014 8:50:10 下午 org.apache.mina.filter.logging.LoggingFilter log
信息: RECEIVED: 知识
received message :知识
八月 27, 2014 8:50:10 下午 org.apache.mina.filter.logging.LoggingFilter log
信息: CLOSED