淺談Java的Mina框架傳遞對象
接觸java的Mina框架已經有很多時間了,在網上也讀過了很多的相關文章,發現Mina框架的確是一個值得拿來好好研究的東西,前些日子寫了一個山寨QQ項目,其中的通信部分用到了java中自帶的InputStream,OutputStream,Writer,Reader等等,感覺其中的很大的一個問題就是難以將事務處理的邏輯層與解析層分離開來,造成整個項目看起來比較臃腫,繁瑣,不夠模塊化,接觸Mina后發現mina在這方面做的很是恰到好處。
看到文章標題,你或許會有一些疑惑:
1、Mina框架傳遞對象是怎么回事
2、Mina傳遞對象可以用來做什么
3、Mina傳遞對象是怎么進行的
4、Mina傳遞對象過程中會遇到什么問題呢
在用原來的java的InputStream,OutputStream,Writer,Reader等進行通信的時候我們會將信息編碼轉化成字節流等進行信息傳遞,InputStream,OutputStream是基于字節流的,而Writer,Reader是基于字符的,我們都知道進行通信的服務器和客戶端是事先必須定好通信協議,如果我們將
這是我們會自然的想到要用一種東西將各個格式的信息進行分類統一起來并方便進行一些必要的信息處理,為符合這些特點,我們會想到類這個東東恰恰滿足了這些性質,我們可以將信息的格式中的內容定義為類的屬性,而對這些屬性的處理就可以用類中的方法來予以解決,這樣就對信息進行了很好的包裝。
這種思想有了,那就是在通信的時候直接進行形式上的對象傳遞(實際上在通信的時候都是最終以字節流的方式進行傳遞的),那么我們就要找一種工具進行這種形式的信息傳遞,對了,這種工具就是Mina框架,我們只看他其中的一個方法
public void messageReceived(IoSession session, Object message),這是進行消息接收是能夠被 觸發的一個方法,參數session代表當前的會話對象,參數message代表接收的到的信息,這時您會發現message的類型是Object型,而類 Object 是類層次結構的根類,當然可以用對象型的作為message啦!前面提到通信的時候都是最終以字節流的方式進行傳遞的,這樣就要進行:對象(客戶端)->字節流(客戶端)->發送->接收->字節流(服務器)->對象(服務器)的過程,呵呵不用擔心,這些繁瑣的過程,Mina都提供了很好的底層默認實現所以你只需稍稍敲點代碼就行了。
光說不練還是不行,先上一個程序實例:
服務器端(1):
Java代碼
- package Mina.server;
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
- import org.apache.mina.filter.codec.ProtocolCodecFilter;
- import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
- import org.apache.mina.transport.socket.SocketAcceptor;
- import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
- public class MainServer {
- private static MainServer mainServer = null;
- private SocketAcceptor acceptor = new NioSocketAcceptor();
- private DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
- private int bindPort = 8888;
- public static MainServer getInstances() {
- if (null == mainServer) {
- mainServer = new MainServer();
- }
- return mainServer;
- }
- private MainServer() {
- chain.addLast("myChin", new ProtocolCodecFilter(
- new ObjectSerializationCodecFactory()));
- acceptor.setHandler(ServerHandler.getInstances());
- try {
- acceptor.bind(new InetSocketAddress(bindPort));
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- public static void main(String[] args) throws Exception {
- MainServer.getInstances();
- }
- }
服務器端(2):
Java代碼
- package Mina.server;
- import org.apache.mina.core.filterchain.IoFilterAdapter;
- import org.apache.mina.core.service.IoHandler;
- import org.apache.mina.core.session.IdleStatus;
- import org.apache.mina.core.session.IoSession;
- import Mina.Object.UserInfo;
- public class ServerHandler extends IoFilterAdapter implements IoHandler {
- private static ServerHandler samplMinaServerHandler = null;
- public static ServerHandler getInstances() {
- if (null == samplMinaServerHandler) {
- samplMinaServerHandler = new ServerHandler();
- }
- return samplMinaServerHandler;
- }
- private ServerHandler() {
- }
- // 當連接后打開時觸發此方法,一般此方法與 sessionCreated 會被同時觸發
- public void sessionOpened(IoSession session) throws Exception {
- }
- public void sessionClosed(IoSession session) {
- }
- public void messageReceived(IoSession session, Object message)
- throws Exception {
- if (message instanceof UserInfo) {
- UserInfo text = (UserInfo) message;
- System.out.println("服務器接收到從客戶端的姓名:"+text.getName());
- System.out.println("服務器接收到從客戶端的QQ:"+text.getQQNum());
- }
- }
- public void exceptionCaught(IoSession arg0, Throwable arg1)
- throws Exception {
- }
- // 當消息傳送到客戶端后觸發
- public void messageSent(IoSession arg0, Object arg1) throws Exception {
- }
- // 當一個新客戶端連接后觸發此方法.
- public void sessionCreated(IoSession arg0) throws Exception {
- }
- // 當連接空閑時觸發此方法.
- public void sessionIdle(IoSession arg0, IdleStatus arg1) throws Exception {
- }
- }
客戶端(1):
Java代碼
- package Mina.client;
- import java.net.InetSocketAddress;
- import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
- import org.apache.mina.core.future.ConnectFuture;
- import org.apache.mina.filter.codec.ProtocolCodecFilter;
- import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
- import org.apache.mina.transport.socket.nio.NioSocketConnector;
- public class MainClient {
- private static MainClient mainClient = null;
- NioSocketConnector connector = new NioSocketConnector();
- DefaultIoFilterChainBuilder chain = connector.getFilterChain();
- public static MainClient getInstances() {
- if (null == mainClient) {
- mainClient = new MainClient();
- }
- return mainClient;
- }
- private MainClient() {
- chain.addLast("myChin", new ProtocolCodecFilter(
- new ObjectSerializationCodecFactory()));
- connector.setHandler(ClientHandler.getInstances());
- connector.setConnectTimeout(30);
- ConnectFuture cf = connector.connect(new InetSocketAddress("localhost",
- 8888));
- }
- public static void main(String args[]) {
- MainClient.getInstances();
- }
- }
客戶端(2):
Java代碼

- package Mina.client;
- import org.apache.mina.core.service.IoHandlerAdapter;
- import org.apache.mina.core.session.IoSession;
- import Mina.Object.UserInfo;
- public class ClientHandler extends IoHandlerAdapter {
- private static ClientHandler samplMinaClientHandler = null;
- public static ClientHandler getInstances() {
- if (null == samplMinaClientHandler) {
- samplMinaClientHandler = new ClientHandler();
- }
- return samplMinaClientHandler;
- }
- private ClientHandler() {
- }
- public void sessionOpened(IoSession session) throws Exception {
- session.write("客戶端與服務器的會話打開了……");
- UserInfo text=new UserInfo();
- text.setName("梅竹寒香");
- text.setQQNum("972341215");
- session.write(text);
- }
- public void sessionClosed(IoSession session) {
- }
- public void messageReceived(IoSession session, Object message)
- throws Exception {
- }
- public void messageSent(IoSession arg0, Object arg1) throws Exception {
- System.out.println("客戶端已經向服務器發送了:"+(String)arg1);
- }
- }
公共類:
Java代碼

- package Mina.Object;
- public class UserInfo implements java.io.Serializable{
- private String name;
- private String QQNum;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getQQNum() {
- return QQNum;
- }
- public void setQQNum(String qQNum) {
- QQNum = qQNum;
- }
- }
如下建包即可:

以上就是對象的收發的簡單示例,如果報錯,或許會是一下原因:1、包的引進是否妥當 2、是否引入了mina的第三方包(網上有了很多的相關文章,在此就不在贅述了)
通過程序您會看到對象已經成功傳遞并進行了相關屬性的輸出,對于這個簡單的程序我稍做些相關說明:
1、進行傳遞的對象所實例化的類要實現java.io.Serializable序列化接口
2、您會發現實例中的類尤其是相關的IoHandlerAdapter繼承類都采用了單實例模式,為什么這樣做呢,原因很簡單,那就是要在整個通信過程中做到對象session的等實例的單一防止發生“所托非人”的現象
3、服務器接收到message在進行類判斷時用了instanceof關鍵字
如果你看到上面的實例就覺得對象傳遞的學習已經成功了,那就錯了,細心的博友看到這個包結構:

是不是有點問題呢。
例如客戶端傳了一個userinfo對象到服務器,在服務器端判斷如果是userinfo對象后就打印出相關信息,我看源碼文檔其中有這樣的建包方式

其中服務器和客戶端共用了中間的Mina.Object包,這樣在收到對象后就能通過instanceof關鍵字判斷是不是useinfo對象,我看了一下,這個方法是可行的,現在的問題是,我們如果編寫通訊軟件的時候,肯定是服務器和客戶端是要分開的,所以那個Mina.Object包是不能共享的,所以問題來了(1)、如果將userinfo放到客戶端中,那么該怎么用instanceof進行判斷是不是userinfo呢(這時你已經不能再引入服務器中的userinfo了)(2)、如果在客戶端和服務器中都編寫一個類定義一樣的userinfo,可是他們這兩個類是分屬不同的包,所以是兩個不同的類了,這樣在用instanceof進行判斷的時候也是行不通的;那么我們該用什么方法來進行判斷接收到的類是不是userinfo對象呢?
這個問題把我糾結了很久,在網上面搜了好久也沒有解決,最后想了想那個(2)或許可以改動改動就可以解決,問題的關鍵在于兩個UserInfo分屬于兩個不同的包,如果可以將包名一致就好了,但是在一個工程里面不能同時建立兩個命名一樣的包,這樣你就會發現何不建立兩個工程呢一個是服務器,一個是客戶端,這樣都可以分別建立名字都是Object的包,這樣可不可行呢,經過試驗果然可以,這樣就就解決了上面的問題工程圖如下

好啦,有了這個工具,您會有什么想法呢?對象傳遞還可以做什么?那就是可以用它來進行圖片,文件的傳遞啦,這個只是個小小的提示具體怎么實現,就要看各位博友怎么發揮啦!呵呵
【編輯推薦】